52519 sc low missing eligibility check before fund transfer in distributeyield leads to permanent loss of yield tokens
Submitted on Aug 11th 2025 at 11:21:59 UTC by @vivekd for Attackathon | Plume Network
Report ID: #52519
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/arc/src/ArcToken.sol
Impacts:
Permanent freezing of funds
Description
Brief / Intro
The distributeYield function transfers yield tokens into the ArcToken contract before verifying if any holders are eligible to receive them.
When all token holders are restricted (blacklisted or sanctioned), the function exits early after the transfer, leaving the yield tokens permanently trapped in the contract with no recovery mechanism, resulting in irreversible loss of funds.
Vulnerability Details
The distributeYield function follows a flawed order of operations that creates a permanent fund trap when all holders are restricted from receiving yield.
Problematic execution flow (excerpt):
function distributeYield(uint256 amount) external onlyRole(YIELD_DISTRIBUTOR_ROLE) nonReentrant {
// ... validation checks ...
// STEP 1: Transfer funds INTO contract (always executes)
ERC20Upgradeable yToken = ERC20Upgradeable(yieldTokenAddr);
yToken.safeTransferFrom(msg.sender, address(this), amount); // Line 435
// STEP 2: Calculate eligible holders
uint256 effectiveTotalSupply = 0;
for (uint256 i = 0; i < holderCount; i++) {
address holder = $.holders.at(i);
if (_isYieldAllowed(holder)) {
effectiveTotalSupply += balanceOf(holder);
}
}
// STEP 3: If no eligible holders, EXIT without distributing
if (effectiveTotalSupply == 0) {
emit YieldDistributed(0, yieldTokenAddr); // Line 449
return; // Funds already in contract, now trapped forever!
}
}The critical flaw is that funds are pulled into the contract (Step 1) before checking if anyone can receive them (Step 2). When effectiveTotalSupply == 0 (all holders restricted), the function returns early, abandoning the transferred yield tokens in the contract.
No Recovery Mechanism Exists:
No
withdrawStuckTokensfunctionNo admin rescue capability
No mechanism to redistribute trapped funds later
Thus the contract has no way to return or recover these tokens.
This can occur through normal operations when:
Global sanctions list is updated to include all holder jurisdictions
All holders become blacklisted due to regulatory compliance
A token is created but all initial holders are restricted before first distribution
Impact Details
Yield tokens become permanently inaccessible once trapped. Each failed distribution adds to the accumulated locked value.
Proof of Concept
A step-by-step demonstration of permanent fund loss:
The vulnerability is confirmed when yield tokens remain trapped in the ArcToken contract after all holders are restricted, with no mechanism to recover them.
References
https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/arc/src/ArcToken.sol#L384-L460
Was this helpful?