58723 sc high cumulativeearmarked is not updated at forcerepay
Submitted on Nov 4th 2025 at 09:31:01 UTC by @farismaulana for Audit Comp | Alchemix V3
Report ID: #58723
Report Type: Smart Contract
Report severity: High
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol
Impacts:
Protocol insolvency
Description
Brief/Intro
in a healthy state, cumulativeEarmarked should equal the sum of all account.earmarked balances. but in the forceRepay function, while the account.earmarked is deducted by earmarkToRemove it is not the case for global cumulativeEarmarked . a frequent liquidation where forceRepay would actually inflates this state, leading to incorrect global state used by the protocol.
Vulnerability Details
function _forceRepay(uint256 accountId, uint256 amount) internal returns (uint256) {
...
// Repay debt from earmarked amount of debt first
uint256 earmarkToRemove = credit > account.earmarked ? account.earmarked : credit;
account.earmarked -= earmarkToRemove;on the snippet above, the _forceRepay is deducting from account.earmarked. the amount is based on how much the account can repay their debt where it would prioritize the earmarked amount first.
this is intended but there are oversight on how it should also reduce the global state of cumulativeEarmarked but it is not. this issue would make the whole protocol use wrong global state.
Impact Details
given how cumulativeEarmarked is now inflated, various issue would arise. example:
in redeem function, the amount is capped by
cumulativeEarmarkedthat can be inflated, resulting in more amount than intended can be redeemed unfairly._earmarkand_calculateUnrealizedDebtrelies onliveUnearmarked = totalDebt - cumulativeEarmarkedthat now would be understated resulting in inaccuracy for the rest of the function
References
https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L761-L762
Proof of Concept
Proof of Concept
add this to src/test/AlchemistV3.t.sol:
the test would pass because the earmark before and after force repay indeed does not change.
Was this helpful?