57506 sc high force repay don t update cumulativeearmarked variable

Submitted on Oct 26th 2025 at 20:09:20 UTC by @OxPhantom for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #57506

  • Report Type: Smart Contract

  • Report severity: High

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

  • Impacts:

    • Permanent freezing of funds

Description

Brief/Intro

Force repay during liquidation decrements an account’s earmarked debt but does not decrement the global cumulativeEarmarked. This desynchronizes global earmark accounting, leading to an underestimated unearmarked bucket and potentially throttling future earmarks and mis-weighting decay.

Vulnerability Details

In repay(), the contract removes earmark at both the account and global levels:

// Repay debt from earmarked amount of debt first
uint256 earmarkToRemove = credit > account.earmarked ? account.earmarked : credit;
account.earmarked -= earmarkToRemove;

uint256 earmarkPaidGlobal = cumulativeEarmarked > earmarkToRemove ? earmarkToRemove : cumulativeEarmarked;
cumulativeEarmarked -= earmarkPaidGlobal;

But in _forceRepay() (used by liquidation pre-step), only the account’s earmark is decremented; the global cumulativeEarmarked is not updated:

Effects:

  • cumulativeEarmarked stays too high, so liveUnearmarked = totalDebt - cumulativeEarmarked is too low.

  • Future _earmark() calls may clamp amount unnecessarily: if (amount > liveUnearmarked) amount = liveUnearmarked;.

  • Earmark/redemption weights and survival math use cumulativeEarmarked (e.g., in _doLiquidation and decay weighting), potentially skewing per-user attribution after force-repay events.

Impact Details

  • Operationally reduces earmark throughput post-liquidation (less debt considered unearmarked than reality), delaying redemptions.

  • Skews global decay/weight accounting (e.g., _redemptionWeight is parameterized by cumulativeEarmarked).

References

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

Proof of Concept

Proof of Concept

You can copy paste this code in AlchemistV3.t.sol and run forge test --mt testLiquidate_POC -vv

This POC demonstrated that the cumulativeEarmarked is not changed by force repay.

Was this helpful?