58792 sc high the cumulativeearmark does not decrease in forcerepay which lead to transfer more collateral from users even when all earmark debt cleared which breaks the alchemix v3 core logic

Submitted on Nov 4th 2025 at 13:49:11 UTC by @zeroK for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58792

  • Report Type: Smart Contract

  • Report severity: High

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

  • Impacts:

    • Theft of unclaimed yield

    • Permanent freezing of funds

Description

Brief/Intro

when liquidate function invoked, the first and most important thing should be done first is repaying the earmark debt which is top priority when liquidation occur, this happens by invoking the _forceRepay function, which have many similarities compared to repay function, however there is a flow in forceRepay function, this function decrease the earmarked amount from users and sends equal value in myt to transmuter, but it never decrease the cumulativeEarmarked by the removed earmark, this lead to a critical flow as shown below:

User A: earmarked = 200
User B: earmarked = 300
User C: earmarked = 100
cumulativeEarmarked = 600 (correct)


user A get liquidated:
_forceRepay(userA, 200) 

// What happens:
User A: earmarked = 0  -->  Local state updated
cumulativeEarmarked = 600  --> Global state NOT updated (BUG!)

// State now:
User A: earmarked = 0
User B: earmarked = 300
User C: earmarked = 100
Sum of user earmarks = 400
cumulativeEarmarked = 600  --> Inflated by 200!

as shown the user A earmark removed locally for the users but the global state never updated, if we assume that all other users paid their earmark debt by invoking repay then the cumulative should be equal to zero, but this won't happen because the liquidation or _forceRepay does not updated the cumulative earmarked. the critical issue arises when redeem function occur with amount > transmuter current myt balance which lead to invoke the redeem function below:

as. shown the redeem will assume that cumulative earmarked still exist for some users but this is not correct, users with earmarked debt already paid as buffer to transmuter, this lead to transfer out more collateral to the transmuter rather than returning zero(and transfer back the amountToRedeem back to caller with discount fee if not matured), this will break the alchemix v3 core idea and make the repay invocation make no sense because users collateral will always be used even if the earmarked debt is zero or less than expected.

Vulnerability Details

we can see that _liquidate invoke calls to _forceRepay before _doLiquidate to pay earmark debt and check if the position still valid for liquidation:

we can see forceRepay will decrease user earmark and transfer it to transmuter as myt but cumulativeEarmark never decreases:

this will directly affect redeem function, which lead to transfer more myt than the system have to and increases the redemption ratio much more than expected which affect users any time _sync get invoked:

for this reason, cumulative earmarked should be updated whenever the _forceRepay get invoked.

Impact Details

forceRepay does not update cumulative earmark globally which lead to make the buffer system in repay function make no sense and transfer out more myt from the alchemix system to transmuter plus updating redemption ratio more than expected which affect users positions.

References

https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L1041C1-L1095C6

https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L738-L781

https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L791-L843

https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L589-L641

Proof of Concept

Proof of Concept

run test below in alchemistV3.t.sol

Was this helpful?