58125 sc critical repayment fee overpayment from pooled collateral

Submitted on Oct 30th 2025 at 19:59:14 UTC by @xanony for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58125

  • Report Type: Smart Contract

  • Report severity: Critical

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

  • Impacts:

    • Theft of unclaimed yield

Description

Brief/Intro

When a liquidation involves only earmarked debt repayment (without further liquidation), the protocol pays the liquidator a repayment fee calculated as fee = repaidAmount * repaymentFee / BPS. However, the fee is transferred unconditionally from the contract's pooled collateral, while the victim's account is only debited by min(fee, account.collateralBalance).

If the account lacks sufficient collateral to cover the fee, the shortfall is implicitly socialized across all depositors' pooled collateral, resulting in:

  • Direct theft of user funds from the shared collateral pool

  • Silent value leakage to liquidators beyond what was deducted from the liquidated account

  • Accounting mismatch between events logged and actual state changes

Vulnerability Details

Affected Components

  • Contract: src/AlchemistV3.sol

  • Functions:

    • _resolveRepaymentFee() (lines 903-909) - calculates fee, debits only min(fee, balance)

    • _liquidate() (lines 793-850) - transfers full feeInYield to liquidator unconditionally

Root Cause

The vulnerability stems from a mismatch between fee deduction and fee payout:

Fee Calculation & Deduction (_resolveRepaymentFee):

Fee Payout (_liquidate repayment-only path):

The Problem:

  1. _resolveRepaymentFee returns the full calculated fee

  2. But only debits min(fee, account.collateralBalance) from the account

  3. _liquidate transfers the full fee to liquidator from contract balance

  4. Gap: fee - min(fee, balance) is paid from other users' pooled collateral

Mathematical Example:

Impact Details

Direct Impact

  • Theft of Pooled Collateral: Liquidators receive more than what was actually deducted from the liquidated account

  • Socialized Losses: Other depositors' collateral is silently drained to cover the shortfall

  • Accounting Mismatch: RepaymentFee event logs the theoretical fee, not the actual deduction

  • Protocol Insolvency Risk: Repeated exploitation could drain the collateral pool

Attack Scenario

  1. Attacker creates multiple positions with minimal collateral

  2. Mints maximum debt and immediately gets earmarked

  3. Allows positions to become liquidatable

  4. Accomplice liquidator calls liquidate() on each position

  5. Each liquidation:

    • Deducts small collateral from account (~1-5 tokens)

    • Pays full fee from pool (~10-50 tokens)

    • Profit: 90-98% from other users' funds

Funds at Risk

Per-liquidation loss:

  • Repayment amount: $10,000

  • Repayment fee: 5% = $500

  • Account collateral: $50

  • Loss from pool: $450 per liquidation

Systemic exposure:

  • Average protocol TVL: $100M

  • Positions with low collateral: ~5% = $5M

  • Potential theft if all liquidated: $5M * 5% * 90% = $225k stolen from pool

References

  • Vulnerable contract: https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol

  • Fee calculation: Lines 903-909 (_resolveRepaymentFee)

  • Fee payout: Lines 820-823, 845-848 (_liquidate)

https://gist.github.com/leojay-net/6e50339084321c8343b7a8da17e68683

Proof of Concept

Proof of Concept

Was this helpful?