Tadle

Tadle
DeFi
30,000 USDC
View results
Submission Details
Severity: medium
Invalid

Lack of Transfer Amount Verification Summary

Summary

The withdraw() function in the TokenManager contract does not verify if the actual amount of tokens transferred matches the intended withdrawal amount. This could lead to discrepancies between the recorded and actual token balances.

Vulnerability Details

The current implementation of the withdraw() function does not check if the amount of tokens actually transferred matches the amount that was supposed to be withdrawn:

function withdraw() public {
uint256 amount = userTokenBalanceMap[msg.sender][address(token)];
require(amount > 0, "No balance to withdraw");
require(token.transfer(msg.sender, amount), "Transfer failed");
userTokenBalanceMap[msg.sender][address(token)] = 0;
}

While the transfer function returns a boolean indicating success or failure, it doesn’t guarantee that the exact amount was transferred. Some tokens may have transfer fees or other mechanisms that could result in a smaller amount being received than expected.

Impact

This vulnerability could lead to inconsistencies between the recorded token balances in the contract and the actual token balances. In the worst case, it could be exploited by malicious token contracts to drain more funds than intended from the TokenManager.

Tools Used

manual code review

Recommendations

Implement a balance check before and after the transfer to verify the exact amount transferred:

function withdraw() public nonReentrant {
uint256 amount = userTokenBalanceMap[msg.sender][address(token)];
require(amount > 0, "No balance to withdraw");
uint256 balanceBefore = token.balanceOf(address(this));
userTokenBalanceMap[msg.sender][address(token)] = 0;
token.safeTransfer(msg.sender, amount);
uint256 balanceAfter = token.balanceOf(address(this));
require(balanceBefore - balanceAfter == amount, "Transfer amount mismatch");
}

Use OpenZeppelin's SafeERC20 library for safer token transfers:

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract TokenManager {
using SafeERC20 for IERC20;
// ...
function withdraw() public nonReentrant {
// ... (implementation as above)
token.safeTransfer(msg.sender, amount);
}
}

Consider implementing a tolerance threshold for tokens with transfer fees:

uint256 tolerance = amount * 3 / 1000; // 0.3% tolerance
require(balanceBefore - balanceAfter >= amount - tolerance, "Transfer amount too low");
require(balanceBefore - balanceAfter <= amount, "Transfer amount too high");

This way the contract will ensure that the actual amount of tokens transferred matches the expected amount, preventing potential exploits and maintaining accurate balance records.

Updates

Lead Judging Commences

0xnevi Lead Judge
9 months ago
0xnevi Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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