For the complete documentation index, see llms.txt. This page is also available as Markdown.

51146 sc low getmaxnumberoftokens returns wrong max number of tokens available to buy

Submitted on Jul 31st 2025 at 15:10:10 UTC by @Oxgritty for Attackathon | Plume Network

  • Report ID: #51146

  • Report Type: Smart Contract

  • Report severity: Low

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

Description

Brief / Intro

  • getMaxNumberOfTokens is used to know the quantity of tokens available to buy.

  • It returns a wrong amount because totalAmountForSale is not updated when withdrawUnsoldArcTokens is used to withdraw unsold ARC tokens.

Vulnerability Details

  • getMaxNumberOfTokens relies on info.totalAmountForSale to compute the available balance.

  • withdrawUnsoldArcTokens withdraws ARC tokens but does not update info.totalAmountForSale.

  • As a result, getMaxNumberOfTokens can return an amount greater than the actual token balance held by the contract.

Impact Details

  • A buyer calling getMaxNumberOfTokens may see an inflated available amount.

  • If the buyer attempts to purchase that inflated amount via buy, the buy call will revert due to ContractBalanceInsufficient.

References

  1. getMaxNumberOfTokens function: https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/arc/src/ArcTokenPurchase.sol#L359

  2. withdrawUnsoldArcTokens function: https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/arc/src/ArcTokenPurchase.sol#L439

  3. buy function revert due to insufficient balance: https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/arc/src/ArcTokenPurchase.sol#L263

Proof of Concept

1

Setup

Token Admin calls enableToken to register a token for sale with totalAmountForSale = 500e18.

2

Withdraw unsold tokens

Token Admin calls withdrawUnsoldArcTokens to withdraw 50e18 tokens. (Contract token balance is now 450e18, but info.totalAmountForSale remains 500e18.)

3

Buyer queries available tokens

Buyer calls getMaxNumberOfTokens. The function returns 500e18 (based on info.totalAmountForSale), which is higher than the actual balance.

4

Buy attempt fails

Buyer calls buy with amount 500e18. The buy call reverts due to ContractBalanceInsufficient.

Was this helpful?