58718 sc medium in forcerepay protocol fee collection leads to theft of unclaimed yield

Submitted on Nov 4th 2025 at 08:44:15 UTC by @legion for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58718

  • Report Type: Smart Contract

  • Report severity: Medium

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

  • Impacts:

    • Theft of unclaimed yield

Description

Brief/Intro

The _forceRepay function in AlchemistV3.sol contains an off-by-one boundary condition error that causes protocol fees to be permanently lost when a borrower's remaining collateral exactly equals the protocol fee amount. The function uses a strict greater-than comparison (>) instead of greater-than-or-equal (>=) when deciding whether to collect the protocol fee, causing the fee transfer to be silently skipped in edge cases. This results in systematic theft of protocol-owned yield that should be collected as revenue during forced liquidation repayments.

Vulnerability Details

Root Cause

In AlchemistV3.sol at lines 771-775, the protocol fee collection logic contains a flawed boundary check:

function _forceRepay(uint256 accountId, uint256 amount) internal returns (uint256) {
    // ... repayment logic ...
    
    uint256 protocolFeeTotal = creditToYield * protocolFee / BPS;
    
    emit ForceRepay(accountId, amount, creditToYield, protocolFeeTotal);
    
    if (account.collateralBalance > protocolFeeTotal) {  //  BUG: Should be >=
        account.collateralBalance -= protocolFeeTotal;
        // Transfer the protocol fee to the protocol fee receiver
        TokenUtils.safeTransfer(myt, protocolFeeReceiver, protocolFeeTotal);
    }
    
    if (creditToYield > 0) {
        // Transfer the repaid tokens from the account to the transmuter.
        TokenUtils.safeTransfer(myt, address(transmuter), creditToYield);
    }
    // ...
}

The Problem: The condition account.collateralBalance > protocolFeeTotal requires the remaining collateral to be strictly greater than the fee amount. When they are exactly equal, the condition evaluates to false, and the entire fee collection block is skipped.

Attack Scenario

The vulnerability manifests during liquidations where:

  1. A borrower's position has debt that is fully earmarked (e.g., after transmuter redemptions)

  2. A liquidator calls liquidate(), which triggers _forceRepay to repay the earmarked debt

  3. After the forced repayment transfer to the transmuter, the borrower's remaining collateral exactly equals the calculated protocol fee

  4. Due to the strict > check, the protocol fee is never transferred to protocolFeeReceiver

  5. The uncollected fee remains in the borrower's collateral balance indefinitely

This scenario occurs naturally without manipulation when:

  • The borrower's initial collateral amount

  • The amount force-repaid to the transmuter

  • The protocol fee percentage

Why This Is a Vulnerability

  1. Silent Failure: The ForceRepay event emits protocolFeeTotal as if it were collected, but no actual transfer occurs

  2. Permanent Loss: Once the liquidation completes, there's no mechanism to retroactively collect the missed fee

Impact Details

Direct Financial Loss

Per-Incident Loss:

  • Each affected liquidation loses the entire protocol fee for that forced repayment

  • With a 20% protocol fee (default configuration), this represents 20% of the repaid yield value

  • Example: A 100 MYT forced repayment with exact boundary conditions results in 20 MYT lost revenue

References

Vulnerable Code

Proof of Concept

Proof of Concept

Was this helpful?