Core Contracts

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

Unsafe ERC20 Transfer Handling Vulnerability in Treasury Contract

Description

The Treasury contract directly uses transfer and transferFrom methods of ERC20 tokens without properly checking their return values. Some ERC20 tokens (like USDT) don't revert on failure and instead return false, while others don't return any value at all. The current implementation assumes all transfers succeed, which could lead to silent failures and loss of funds.

Vulnerable code locations:

// In deposit function
IERC20(token).transferFrom(msg.sender, address(this), amount);
// In withdraw function
IERC20(token).transfer(recipient, amount);

Impact

  • Silent failures of token transfers could lead to loss of funds

  • State updates could occur even when transfers fail

  • Accounting system could become out of sync with actual balances

  • Some ERC20 tokens (e.g., USDT) could fail silently

Fix Recommendation

  1. Use OpenZeppelin's SafeERC20 library:

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract Treasury is ITreasury, AccessControl, ReentrancyGuard {
using SafeERC20 for IERC20;
function deposit(address token, uint256 amount) external override nonReentrant {
if (token == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
_balances[token] += amount;
_totalValue += amount;
emit Deposited(token, amount);
}
function withdraw(
address token,
uint256 amount,
address recipient
) external override nonReentrant onlyRole(MANAGER_ROLE) {
if (token == address(0)) revert InvalidAddress();
if (recipient == address(0)) revert InvalidRecipient();
if (_balances[token] < amount) revert InsufficientBalance();
_balances[token] -= amount;
_totalValue -= amount;
IERC20(token).safeTransfer(recipient, amount);
emit Withdrawn(token, amount, recipient);
}
}
  1. Alternative manual check:

function deposit(address token, uint256 amount) external override nonReentrant {
if (token == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
bool success = IERC20(token).transferFrom(msg.sender, address(this), amount);
require(success, "Token transfer failed");
// Get balance before and after to confirm transfer
uint256 balanceBefore = IERC20(token).balanceOf(address(this));
uint256 balanceAfter = IERC20(token).balanceOf(address(this));
require(balanceAfter - balanceBefore == amount, "Transfer amount mismatch");
_balances[token] += amount;
_totalValue += amount;
emit Deposited(token, amount);
}

Tools Used

  • Manual code review

  • Static analysis

  • Mock contract testing

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

[INVALID] SafeERC20 not used

LightChaser Low-60

Support

FAQs

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