49787 sc high batched yield distribution doesn t account for transfers purchases between batches

Submitted on Jul 19th 2025 at 13:31:56 UTC by @Vanshika for Attackathon | Plume Network

  • Report ID: #49787

  • Report Type: Smart Contract

  • Report severity: High

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

  • Impacts:

    • Theft of unclaimed yield

Description

Brief / Intro

ArcToken has a function to distribute yield in batches when the number of token holders is too high to fit in one transaction. Any change in user balances sandwiched between separate batches can lead to transaction revert and loss of yield for some token holders.

Vulnerability Details

ArcToken::distributeYieldWithLimit() distributes yield to a specified range of token holders at a time. holders is an enumerable set of addresses among whom the total yield amount has to be distributed. When this is done in batches, if users at the beginning or middle can inflate their balance and get more yield between batches, the contract can run out of funds before it reaches the end.

The contract receives the totalAmount to distribute only at the first batch when startIndex == 0. Yield calculation for a token holder is:

uint256 share = (totalAmount * holderBalance) / effectiveTotalSupply;

effectiveTotalSupply is calculated as the sum of ArcToken balances for all eligible yield recipients. This calculation occurs separately for each batch of transfers, so effectiveTotalSupply can change between batches. This leads to incorrect yield shares or function reverts. A user can purchase more ArcTokens right before early batches, receive an inflated share, and then sell the tokens to a new holder to DoS one or more later batches.

Impact Details

There is a high likelihood of accidental accounting errors and reverts during regular use of the function. This can also be exploited by malicious actors to steal yield from other token holders.

Proof of Concept

PoC test (click to expand)

Copy the following test into ArcToken.t.sol and run with:

Expected Results

If maxHolders in batch 2 = 1, expected test output:

If you test with maxHolders = 2 or 3 for batch2 (uncomment David's assert), expected failing output:

Was this helpful?