Tadle

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

Denial of Service Due to Fixed Gas Limit in withdraw Function

Summary

The withdraw function in the contract includes logic that handles the transfer of both ERC20 tokens and native tokens (such as Ether). When withdrawing native tokens, the function uses Solidity's transfer() method, which uses a fixed gas limit of 2300. However, If the recipient is a smart contract, this gas limit may be insufficient and would lead to the transaction to fail, leading to a denial of service (DoS).

Description

The withdraw function allows users to withdraw their tokens from the contract. When withdrawing native tokens like ether, the function first converts wrapped native tokens (WETH) back into Ether and then uses Solidity’s transfer() method to send the Ether to the user.

However, the transfer() method in Solidity has a fixed gas limit of 2300. If the recipient of the transfer is a smart contract, the contract's receive() or fallback() function might require more than 2300 gas to execute due to the unknown logic that this protocol might not be aware of, such as emitting events. This could cause the transaction to fail, leaving the Ether locked in the contract and preventing the user from successfully withdrawing their funds.

POC

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); // sends ether back to the user //@audit Low- if the recipient is a smart contract, the fixed gas limit(23000) associated with transfer() in Solidity might prevent the transaction from being completed as the as the contract's receive() or fallback() methods might require more than 2300 Gas for execution due to other operations like logging events
} 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

The use of the transfer() method can lead to a denial of service or funds being stucked in the contract

Mitigation

Replace the use of transfer() with call{value: amount}(""), which allows specifying a gas limit or providing all available gas, which would reduce such risks

Updates

Lead Judging Commences

0xnevi Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

[invalid] finding-TokenManager-withdraw-transfer-2300-gas

Invalid, known issues [Medium-2](https://github.com/Cyfrin/2024-08-tadle/issues/1)

Support

FAQs

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