58688 sc critical alchemistv3 liquidate can steal other users collateral

Submitted on Nov 4th 2025 at 03:51:45 UTC by @DeoGratias for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58688

  • Report Type: Smart Contract

  • Report severity: Critical

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol

  • Impacts:

    • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

AlchemistV3::_liquidate can steal other users’ collateral

Description: In the “repay-only” liquidation path, the liquidator’s repayment fee can be paid out of the contract’s global MYT balance rather than strictly from the liquidated account’s collateral. This happens because AlchemistV3::_resolveRepaymentFee transfers the full fee to the liquidator, even when the account has no collateral left to fund that fee. The transfer pulls MYT from the protocol contract balance, not from the debtor’s balance.

The liquidator effectively is stealing other users collateral in this case.

Impact:

  • Direct value theft: Liquidators can be paid fees sourced from other users’ deposits.

  • Withdrawal failures / DoS: Honest users cannot fully withdraw; attempts to withdraw their full recorded collateral revert.

Proof of Concept

Proof of Concept:

  • Set protocolFee = 0, repaymentFee = 10%.

  • User B deposits MYT (forms shared pool).

  • User A deposits, mints near minimum collateralization; total debt is fully earmarked via transmuter.

  • Simulate a price drop so shares needed to repay > A’s collateral.

  • Call liquidate(A) → repay-only branch: _forceRepay zeros A’s debt, drains A’s collateral, then _resolveRepaymentFee pays fee to liquidator from the contract’s MYT.

  • B’s attempt to withdraw all of their deposit reverts; only (deposit − fee) can be withdrawn, leaving a phantom remainder equal to the fee.

  • Note: The repayment fee is high in this POC, but the issue is still present regardless of the size of the repayment fee, as long as it is non-zero.

To run this poc paste the below code in AlchemistV3.t.sol and run the command forge test --mt testPOC_RepayOnly_Liquidation_StealsFee_And_B_WithdrawAll_Reverts

Output

Was this helpful?