Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

DoS via `_totalValue` overflow through zero-value token deposits in `Treasury.deposit()`

Summary

The Treasury::deposit() function allows any user to deposit arbitrary ERC20 tokens, incrementing the _totalValue without considering the token's actual value or decimals. This enables malicious users to deposit worthless or high-decimal tokens to overflow _totalValue, permanently blocking future deposits.

Vulnerability Details

The vulnerability exists in the deposit() function of the Treasury contract:

function deposit(address token, uint256 amount) external override nonReentrant {
if (token == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
IERC20(token).transferFrom(msg.sender, address(this), amount);
_balances[token] += amount;
@> _totalValue += amount;
emit Deposited(token, amount);
}

The issue arises because:

  1. Any user can deposit any ERC20 token

  2. _totalValue is incremented by the raw amount without considering token value/decimals

  3. No access control on deposits

  4. No whitelist of accepted tokens

Impact

A malicious actor can permanently disable the Treasury's deposit functionality by:

  1. Creating a worthless ERC20 token

  2. Minting a large amount (close to type(uint256).max)

  3. Depositing this amount to the Treasury

  4. The _totalValue will approach type(uint256).max

  5. Any subsequent legitimate deposits will revert due to overflow

This creates a permanent DoS condition where:

  • New legitimate deposits become impossible

  • Protocol operations requiring deposits are blocked

  • Treasury becomes unusable for its core function

  • Requires contract redeployment to fix

Tools Used

Manual review

Proof of Concept

Add test case to Treasury.test.js

it("Dos via overflow", async () => {
// Create worthless token
const MockToken = await ethers.getContractFactory("MockToken");
const attackToken = await MockToken.deploy("Worthless Token", "WTHL", 18);
// Mint large amount
await attackToken.mint(user1.address, ethers.MaxUint256);
// Deposit to overflow _totalValue
await attackToken.connect(user1).approve(treasury.getAddress(), ethers.MaxUint256);
await treasury.connect(user1).deposit(attackToken.getAddress(), ethers.MaxUint256);
// Attempt legitimate deposit - will revert
const amount = ethers.parseEther("100");
await token.connect(user1).approve(treasury.getAddress(), amount);
// Reverts with panic 0x11 (Arithmetic operation overflowed outside of an unchecked block)
await expect(treasury.connect(user1).deposit(token.getAddress(), amount))
.to.be.revertedWithPanic("0x11");
});

Recommendations

Do not track total value in a variable, instead calculate the total value in the getTotalValue() functions iterating over all tokens balances and their actual values according to oracles.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Treasury::deposit increments _totalValue regardless of the token, be it malicious, different decimals, FoT etc.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!