52798 sc high integer division remainder loss in batched yield distribution causes permanent fund lock

Submitted on Aug 13th 2025 at 09:17:55 UTC by @ZeroExRes for Attackathon | Plume Network

  • Report ID: #52798

  • Report Type: Smart Contract

  • Report severity: High

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

  • Impacts: Permanent freezing of funds

Description

Brief/Intro

The distributeYieldWithLimit() function contains a flaw where integer division remainders from each batch are permanently lost and locked in the contract, while the regular distributeYield() function correctly handles these remainders. This creates an accumulating fund loss mechanism that can result in significant monetary losses over time, with no recovery method available.

Vulnerability Details

The issue stems from different remainder handling between two yield distribution functions:

distributeYield() (Works Correctly):

// Processes all holders except last
for (uint256 i = 0; i < lastProcessedIndex; i++) {
    uint256 share = (amount * holderBalance) / effectiveTotalSupply;
    // ... distribute share
    distributedSum += share;
}

// CRITICAL: Last holder gets ALL remainder
uint256 lastShare = amount - distributedSum;  // ← Captures remainder
yToken.safeTransfer(lastHolder, lastShare);

distributeYieldWithLimit() (Loses Remainder):

Impact Details

Each batched distribution loses integer division remainders from every user calculation, with no recovery mechanism available. Even though there is no significant loss for a particular user, these per-user remainders accumulate into non-trivial amounts with each distribution, creating systematic fund loss that compounds over the contract's operational lifetime.

Proof of Concept

Add to ArcToken.t.sol:

References

Mentioned above


If you want, I can:

  • Suggest concrete fixes (e.g., accumulate distributed sum per batch and forward remainder to a designated recipient or track leftover for later distribution), or

  • Produce a small patch diff for ArcToken.sol implementing correct remainder handling in distributeYieldWithLimit().

Was this helpful?