Tadle

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

Lack of the `receive()` or `fallback()` function in the TokenManager contract, which lead the TokenManager contract to failing to receive the native ETH-sent from the WETH contract

Summary

Within the TokenManager contract, there is "no" implementation of the receive() or fallback() function to receive the native ETH-sent.

This lead the TokenManager contract to failing to receive the native ETH-sent from the WETH contract - when the IWrappedNativeToken(wrappedNativeToken)#withdraw() is called via the TokenManager#withdraw().

This result in that a deposited-collateral of the caller (msg.sender) of the TokenManager#withdraw() is stuck forever inside the TokenManager contract.

Vulnerability Details

When a user would like to withdraw their deposited-collateral (native ETH) from the CapitalPool contract, the user would call the TokenManager#withdraw().

Within the TokenManager#withdraw(), the following three steps would be proceeded:

/**
* @notice Withdraw
* @dev Caller must be owner
* @param _tokenAddress Token address
* @param _tokenBalanceType Token balance type
*/
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( ///<----------------------- @audit
wrappedNativeToken,
capitalPoolAddr,
address(this),
claimAbleAmount,
capitalPoolAddr
);
IWrappedNativeToken(wrappedNativeToken).withdraw(claimAbleAmount); ///<-------- @audit - WETH# withdraw()
payable(msg.sender).transfer(claimAbleAmount); ///<------- @audit

When the step 2/ above, the claimAbleAmount of the native ETH would be transferred from the WETH contract to the TokenManager contract via the IWrappedNativeToken(wrappedNativeToken).withdraw(claimAbleAmount) like this:
https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/TokenManager.sol#L168

IWrappedNativeToken(wrappedNativeToken).withdraw(claimAbleAmount);

According to the Solidity's documentation, the receive() or fallback() function must be implemented for a smart contract to receive the native ETH-sent.

However, within the TokenManager contract, there is "no" implementation of the receive() or fallback() function to receive the native ETH-sent.

This lead the TokenManager contract to failing to receive (the claimAbleAmount of) the native ETH-sent from the WETH contract - when the IWrappedNativeToken(wrappedNativeToken)#withdraw() is called via the TokenManager#withdraw().
(NOTE:At this point, the TX of the TokenManager#withdraw() will be reverted)

Impact

This lead a deposited-collateral of the caller (msg.sender) of the TokenManager#withdraw() to being stuck forever inside the TokenManager contract.

Tools Used

  • Foundry

Recommendations

Within the TokenManager contract, consider implementing the receive() or fallback() - so that the TokenManager contract can receive the native ETH-sent from the WETH contract when the IWrappedNativeToken(wrappedNativeToken)#withdraw() is called via the TokenManager#withdraw().

Updates

Lead Judging Commences

0xnevi Lead Judge 10 months 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.