In the TokenManager.sol#withdraw() function, if _tokenAddress is ETH, the low-level transfer() function is used to transfer wrappedNativeToken, so users using multi-signature wallets cannot withdraw assets.
When _tokenAddress is wrappedNativeToken, the TokenManager.sol#withdraw() function sends ETH from the CapitalPool to the caller (msg.sender) using the low-level transfer() function:
However, transfer() only forwards 2300 gas, which is not enough for the recipient to execute any non-trivial logic in a receive() or fallback function. Even if the call were to reach the implementation contract and fire an event, it would require more than 6,000 gas.
If a user calls withdraw() on a contract account, such as a multi-signature or smart contract wallet with a receive() function requiring >2300 gas, the call will fail permanently. The ETH you wish to withdraw will be permanently locked in the CapitalPool contract, resulting in loss of funds.
Alice calls withdraw() on the multi-signature wallet contract to withdraw 10 ETH worth.
The multi-signature wallet contract is msg.sender, payable(msg.sender).transfer(claimAbleAmount) is called, and ETH is transferred to the multi-signature wallet contract.
The receive() function of the multi-signature wallet contract attempts to transfer the received ETH to Alice, which uses >2300 gas.
Due to lack of gas, the multisig's receive() will fail and the transaction will be reverted.
Now the 10 ETH is permanently locked in the CapitalPool contract, so there is no way for Alice to claim it.
User are not able to claim withdraw request in the worst case
Manual Review
Using call() instead of transfer():
Invalid, known issues [Medium-2](https://github.com/Cyfrin/2024-08-tadle/issues/1)
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.