Tadle

Tadle
DeFiFoundry
27,750 USDC
View results
Submission Details
Severity: low
Invalid

TokenManager::withdraw() function will revert for Native token

Summary

TokenManager::withdraw() function will revert for native tokens as TokenManagerdoes not have receive()or fallback()function to accept native tokens directly.

Vulnerability Details

In the withdraw(...)function, if the tokenAddressis a wrapped native token, then the logic transfers the wrapped native token into TokenManagercontract first using _transfer(...)function.

After that, it withdraws the native token from the wrappedNativeTokencontract by calling withdraw(...)

IWrappedNativeToken(wrappedNativeToken).withdraw(claimAbleAmount);

Example demo using WETH contract's withdraw that sends ETH to the msg.senderwhich in our case is TokenManager

function withdraw(uint wad) public {
require(balanceOf[msg.sender] >= wad);
balanceOf[msg.sender] -= wad;
msg.sender.transfer(wad);
emit Withdrawal(msg.sender, wad);
}

The withdraw function will try to send native tokens directly to TokenManagercontract, but since the token manager contract does not have receive()or fallback()functions implemented, the TokenManagercontract cannot accept native tokens directly. This will result in revert.

function withdraw(
address _tokenAddress,
TokenBalanceType _tokenBalanceType
) external whenNotPaused {
uint256 claimAbleAmount = userTokenBalanceMap[_msgSender()][
_tokenAddress
][_tokenBalanceType];
if (claimAbleAmount == 0) {
return;
}
address capitalPoolAddr = tadleFactory.relatedContracts(
RelatedContractLibraries.CAPITAL_POOL
);
if (_tokenAddress == wrappedNativeToken) {
/**
* @dev token is native token
* @dev transfer from capital pool to msg sender
* @dev withdraw native token to token manager contract
* @dev transfer native token to msg sender
*/
_transfer(
wrappedNativeToken,
capitalPoolAddr,
address(this),
claimAbleAmount,
capitalPoolAddr
);
==> IWrappedNativeToken(wrappedNativeToken).withdraw(claimAbleAmount);
==> payable(msg.sender).transfer(claimAbleAmount);
} else {
/**
* @dev token is ERC20 token
* @dev transfer from capital pool to msg sender
*/
_safe_transfer_from(
_tokenAddress,
capitalPoolAddr,
_msgSender(),
claimAbleAmount
);
}
emit Withdraw(
_msgSender(),
_tokenAddress,
_tokenBalanceType,
claimAbleAmount
);

Impact

Withdrawals for wrappedNativeTokenwill revert

Tools Used

Manual review

Recommendations

Add receive()function to support receiving of native token into TokenManagercontract.

Also restrict the receive to accept ether from wrappedNativeTokencontract to limit the contracts from where native tokens can be received into.

Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

[invalid] finding-TokenManager-no-native-ETH-receive

Invalid, TokenManager is the implementation contract of 9the transparent upgradeable proxy, as can seen by the comments [here](https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/proxy/UpgradeableProxy.sol#L15). The receive payable is implemented as seen [here](https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/proxy/UpgradeableProxy.sol#L37)

Support

FAQs

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

Give us feedback!