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 V3arrow-up-right

  • 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()arrow-up-right (logic that calls queryGraph)

Transmuter.sol::queryGraph()arrow-up-right (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.number to ensure each _earmark() call always advances state.

  • Add a minimum block spacing requirement before updating lastEarmarkBlock to prevent zero-range queryGraph calls.

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?