Immunefi Audit Competitions
Plume | Attackathon

52915 sc low yield are transferred before eligibility check potentially leading to freezing of funds

Submitted on Aug 14th 2025 at 09:13:02 UTC by @spongebob for Attackathon | Plume Network

Description

The distributeYield and distributeYieldWithLimit functions in the ArcToken contract contain a flaw where yield tokens are transferred from the caller to the contract before checking if any token holders are eligible to receive yield.

Behavior:

Root cause summary:

The same pattern exists in distributeYieldWithLimit where tokens are transferred on the first batch (startIndex == 0) before eligibility checks.

Impact

Yield tokens transferred to the contract cannot be recovered through any on-chain mechanism, leading to permanent loss of yield tokens when all token holders become ineligible for yield distribution.

Proof of Concept

Deploy ArcToken with yield restrictions

  • Deploy an ArcToken contract with yield blacklist restrictions enabled.
  • Set up a yield token (e.g., USDC) for distribution.

Create token holders and restrict them

  • Mint ArcTokens to multiple addresses (e.g., Alice, Bob, Charlie).
  • Add all token holders to the yield blacklist using YieldBlacklistRestrictions.addToBlacklist().

Verify all holders are restricted

  • Confirm effectiveTotalSupply == 0 by checking that _isYieldAllowed() returns false for all holders.
  • All holders should be ineligible for yield distribution.

Attempt yield distribution

  • As a YIELD_DISTRIBUTOR_ROLE account, approve yield tokens to the ArcToken contract.
  • Call distributeYield(amount) with a significant amount of yield tokens.

Observe fund loss

  • The safeTransferFrom executes first, transferring yield tokens from caller to contract.
  • Function calculates effectiveTotalSupply == 0 and emits YieldDistributed(0, yieldTokenAddr).
  • Yield tokens are now permanently trapped in the ArcToken contract.

Confirm permanent loss

  • Check that yield tokens are stuck in the contract balance.
  • Verify no sweep/recovery functions exist in the contract to retrieve the tokens.
  • Funds remain inaccessible to all parties, including contract administrators.

Note: The same scenario applies to distributeYieldWithLimit() when called with startIndex == 0.

References