51276 sc low arctokenpurchase re enabling active token sales causes accounting corruption and token loss

Submitted on Aug 1st 2025 at 11:08:35 UTC by @rilwan99 for Attackathon | Plume Network

  • Report ID: #51276

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

    • Permanent freezing of funds

Description

Brief / Intro

The ArcTokenPurchase.enableToken() function lacks validation to prevent re-enabling already enabled tokens, which leads to accounting inconsistencies and Arc Tokens being permanently locked in the contract.

Vulnerability Details

The vulnerability exists in the enableToken() function in ArcTokenPurchase.sol:

function enableToken(
    address _tokenContract,
    uint256 _numberOfTokens,
    uint256 _tokenPrice
) external onlyTokenAdmin(_tokenContract) {
    // ... validation checks ...
    
    // No check if token is already enabled
    ps.tokenInfo[_tokenContract] = TokenInfo({
        isEnabled: true,
        tokenPrice: _tokenPrice,
        totalAmountForSale: _numberOfTokens,  // Overwrites previous value
        amountSold: 0  // Resets to 0, losing previous sales data
    });
    
    ps.enabledTokens.add(_tokenContract);
    emit TokenSaleEnabled(_tokenContract, _numberOfTokens, _tokenPrice);
}

No validation checks if the token is already enabled. The assignment completely replaces the existing TokenInfo struct, losing all previous state including sales history and actual token availability.

Impact Details

  • Token Loss: Unsold tokens become permanently locked when amountSold resets to 0.

  • Accounting Mismatch: Mismatch between actual contract balance and recorded availability.

References

https://github.com/plumenetwork/contracts/blob/main/arc/src/ArcTokenPurchase.sol

Proof of Concept

1

Initial Setup

  • Admin calls enableToken(tokenAddr, 100e18, 10e6) // 100 tokens at $10 each

  • Contract receives 100 Arc Tokens

  • State: totalAmountForSale = 100e18, amountSold = 0, isEnabled = true

2

Users Purchase Tokens

  • User1 calls buy(tokenAddr, 200e6, 20e18) // Buys 200 tokens

  • User2 calls buy(tokenAddr, 200e6, 20e18) // Buys 200 tokens

  • State after sales: totalAmountForSale = 100e18, amountSold = 40e18, isEnabled = true

  • Remaining for sale: 60e18 Arc tokens

3

Token Admin Re-enables (Bug Trigger)

  • Admin calls enableToken(tokenAddr, 50e18, 20e6) // Admin wants to change price

  • State becomes: totalAmountForSale = 50e18, amountSold = 0, isEnabled = true // OVERWRITTEN

  • Contract still holds 60 Arc Tokens, but accounting shows only 50 available

Impact: 10 Arc Tokens (60e18 - 50e18) are left permanently stuck in the contract and not accounted for. There is no way to withdraw/recover these funds.

Was this helpful?