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).
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.
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.
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.