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 distributes yield in batches when the number of token holders is too high to fit in a single transaction. Any change in user balances occurring between separate batches can cause incorrect accounting or transaction reverts, potentially leading to loss or theft of yield.

Vulnerability Details

ArcToken::distributeYieldWithLimit() distributes yield across a range of token holders per call. holders is an enumerable set of addresses among whom the totalAmount is distributed. When distributing in batches:

  • The contract receives the totalAmount to distribute only on the first batch (when startIndex == 0).

  • Each batch recalculates the effectiveTotalSupply as the sum of ArcToken balances for eligible yield recipients for that batch.

  • Because effectiveTotalSupply is recalculated per batch, it can change between batches if balances are modified (transfers, purchases, sells).

  • A user who increases their balance immediately before an early batch receives a disproportionate share of that batch. They can then sell tokens so later batches find less yield remaining and may revert or leave holders unpaid.

Yield for a holder is calculated as:

Since effectiveTotalSupply can vary between batches, this leads to incorrect share allocations and potential reverts (or theft of yield).

Impact Details

  • High likelihood of accidental accounting errors and transaction reverts during normal use.

  • Malicious actors can exploit timing between batches to inflate yield for early batches and cause DoS or theft for later batches.

Proof of Concept

PoC test (click to expand)

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

forge test --mt test_batchDistribution --via-ir

Expected Result if maxHolders in batch2 = 1:

Expected result if you test with maxHolders = 2 or 3 for batch2: (uncomment david's assert statement)

Reproduction / Steps

1

Setup

  • Transfer tokens to multiple holders (alice, bob, charlie, david, ethan).

  • Blacklist owner from yield calculation (so owner balance is excluded from effectiveTotalSupply).

  • Approve yieldToken allowance for the contract.

  • Call distributeYieldWithLimit(YIELD_AMOUNT, 0, 3) to distribute the first batch.

2

Sandwich / Exploit

  • Before running subsequent batch calls, an attacker increases their ArcToken balance (e.g., charlie receives more tokens).

  • The attacker gets a larger share of the yield calculated in a prior or current batch due to increased holderBalance and a recalculated (smaller or larger) effectiveTotalSupply.

  • The attacker then sells tokens, reducing the remaining supply and causing later batches to find insufficient yield (leading to incorrect payments or reverts).

3

Result

  • Early batches overpay the attacker.

  • Later batches run out of funds or revert, causing yield loss or failed distributions for other holders.

Notes

  • The core issue is reliance on a per-batch recalculation of effectiveTotalSupply while the total distributed amount is provided only once at the start. Ensuring a fixed denominator across all batches (or transferring funds for each batch separately) is necessary to prevent this class of issue.

Was this helpful?