57774 sc critical redemption earmark mechanism can be permanently blocked via single block earmark calls
#57774 [SC-Critical] Redemption Earmark Mechanism Can Be Permanently Blocked via Single-Block Earmark Calls
Submitted on Oct 28th 2025 at 20:13:50 UTC by @pindarev for Audit Comp | Alchemix V3
Report ID: #57774
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol
Impacts:
Protocol insolvency
Permanent freezing of unclaimed yield
Description
Brief/Intro
The protocol’s debt redemption process relies on block-based accrual via queryGraph(startBlock, endBlock). An attacker can repeatedly trigger _earmark() every block to ensure endBlock == startBlock, causing queryGraph() to always return zero and preventing any debt from being earmarked.
Alchemix v3 utilizes a block-range-dependent staking graph to determine how much debt should be redeemed (“earmarked”) over time. However, the contract updates lastEarmarkBlock every time _earmark() is called. An attacker can force _earmark() to run every block, causing the graph query to always evaluate over an empty range and return zero. This effectively starves the redemption pipeline indefinitely.
Vulnerability Details
The vulnerable call occurs here (simplified):
And in the Transmuter:
If _earmark() is invoked every block, then:
startBlock = N + 1
endBlock = N + 1
This condition triggers, so:
queryGraph returns 0 → No earmarking occurs → No redemption progresses
Any address can call poke(tokenId) to trigger this behavior. No value or permission is needed, making this trivially griefable.
Root cause
The protocol attempts to derive continuous redemption progress based on block deltas, but updates to lastEarmarkBlock are attacker-influenceable. By controlling when _earmark() is called, an attacker controls the block range width, which can be forced to zero every time.
Impact Details
This vulnerability prevents the protocol from accruing earmarked debt for the Transmuter. As a consequence:
The redemption pipeline will not progress — no debt will be earmarked for redemption while the attacker maintains the attack.
Users that create redemptions will not receive yield in future, because the redemption queue will stop advancing.
The protocol’s redemption logic is effectively broken and the protocol becomes unstable with respect to redemptions (high impact on user experience and protocol guarantees).
References
AlchemistV3.sol::_earmark() (logic that calls queryGraph)
Transmuter.sol::queryGraph() (returns zero when endBlock <= startBlock)
Mitigation
Consider one the options:
Switch from block-range based accrual to an index-based accumulator so that earmark progression cannot be forced to zero by timing calls.
Maintain a stored graph cursor (or accumulator checkpoint) instead of relying on
block.numberto ensure each_earmark()call always advances state.Add a minimum block spacing requirement before updating
lastEarmarkBlockto prevent zero-rangequeryGraphcalls.
Proof of Concept
Proof of Concept
Add the following test in src/test/AlchemistV3.t.sol file and run it using this command forge test --mt test_EarmarkGriefingAttack -vv
PoC:
Was this helpful?