52468 sc insight dos in batch yield distribution due to cross batch state inconsistency

Submitted on Aug 11th 2025 at 02:48:59 UTC by @flora for Attackathon | Plume Network

  • Report ID: #52468

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/arc/src/ArcToken.sol

  • Impacts:

    • Smart contract unable to operate due to lack of token funds

Description

Brief/Intro

The distributeYieldWithLimit function in ArcToken.sol is vulnerable to a Denial of Service (DoS) attack. An administrative action, such as blacklisting a user for yield, performed mid-distribution between batches can create a state inconsistency. This causes subsequent distribution transactions to revert due to a shortfall in required funds. If exploited, this vulnerability would permanently halt the yield distribution process, preventing users from receiving their entitled yield and locking the remaining undistributed funds within the contract, requiring privileged intervention to resolve.

Vulnerability Details

1

Fund transfer and batch model

  • At the start of the first batch (startIndex == 0), the contract pulls the totalAmount from the distributor (msg.sender).

  • Funds are transferred only once at the beginning.

Code snippet:

src/ArcToken.sol:526-528
if (startIndex == 0) {
    yToken.safeTransferFrom(msg.sender, address(this), totalAmount);
}
2

State recalculation per batch

  • For every batch, the contract iterates through all holders to recalculate effectiveTotalSupply, only including those currently eligible for yield.

  • The denominator is recalculated in every batch, allowing state changes to interfere.

Code snippet:

src/ArcToken.sol:512-516
uint256 effectiveTotalSupply = 0;
for (uint256 i = 0; i < totalHolders; i++) {
    address holder = $.holders.at(i);
    if (_isYieldAllowed(holder)) {
        effectiveTotalSupply += balanceOf(holder);
    }
}
3

Flawed calculation across batches

  • The share for each user is calculated using (totalAmount * holderBalance) / effectiveTotalSupply.

  • The numerator (totalAmount) remains fixed across all batches, while the denominator (effectiveTotalSupply) can change between batches.

  • If a large holder becomes ineligible after their batch, the denominator decreases for subsequent batches while the numerator remains the same, inflating subsequent per-holder shares.

  • The inflated shares can exceed the remaining balance in the contract and cause a revert (ERC20InsufficientBalance), permanently halting the distribution.

Code snippet:

src/ArcToken.sol:540
uint256 share = (totalAmount * holderBalance) / effectiveTotalSupply;

Impact Details

This vulnerability has a high-severity impact, as it breaks a core protocol function and freezes funds.

  • Temporary Freezing of Funds: undistributed yield tokens can become locked inside the ArcToken contract. In the PoC, 360 USDC (out of 1200 USDC) were trapped and irrecoverable without privileged administrative action.

  • Denial of Service on Core Functionality: once the DoS state is triggered, no further batches can be processed, preventing users from receiving scheduled yield.

  • Loss of Yield for Users: users scheduled in subsequent batches cannot receive their yield.

References

  • Vulnerable Contract: src/ArcToken.sol

  • Vulnerable Function: distributeYieldWithLimit (Lines 466-555)

Proof of Concept

Full PoC contract (expand to view)

Notes / Recommendations (implied by issue)

  • The root cause is using a fixed totalAmount across batches while recalculating effectiveTotalSupply per-batch. Fixes should ensure per-batch calculations use the remaining undistributed amount or a per-batch numerator that aligns with the denominator to prevent inflation when eligibility changes mid-process.

  • Consider one of:

    • Pulling funds per-batch proportional to the remaining undistributed amount.

    • Computing shares using a snapshot of eligible supply at the start and retaining that snapshot across batches.

    • Locking eligibility state for the duration of distribution or providing a single-batch distribution only.

  • Any fix must preserve security against reentrancy and race conditions while ensuring the sum of per-batch transfers cannot exceed the contract balance.

Was this helpful?