Native ETH to be withdrawn (via the transfer()
method in the TokenManager#withdraw()
) will be locked forever in the TokenManager contract, leading to loss of user (msg.sender
)'s collateral (native ETH).
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 steps would be proceeded:
1/ WETH (wrappedNativeToken
) would be transferred from the CapitalPool contract (capitalPoolAddr
) to the TokenManager contract (address(this)
).
2/ Then, the claimAbleAmount
of the native ETH would be withdrawn from the WETH contract (IWrappedNativeToken(wrappedNativeToken)
) to the TokenManager contract (address(this)
).
3/ Finally, the claimAbleAmount
of the native ETH-withdrawn would be transferred from the TokenManager contract (address(this)
) to the user (msg.sender
).
https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/TokenManager.sol#L160-L166
https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/TokenManager.sol#L168
https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/TokenManager.sol#L169
However, within the TokenManager#withdraw()
, the transfer()
method of the native ETH would be used - when the the claimAbleAmount
of the native ETH-withdrawn would be transferred from the TokenManager contract (address(this)
) to a caller (msg.sender
) during the step 3/ above:
(NOTE:This caller (msg.sender
) would be an end user like an EOA or a smart contract wallet)
https://github.com/Cyfrin/2024-08-tadle/blob/main/src/core/TokenManager.sol#L169
This is problematic because the transfer()
method of the native ETH would only forward 2300 gas, which is not enough for the recipient to execute any non-trivial logic in a receive()
or fallback()
. Especially, it is not enough for the smart contract wallets like (Gnosis) Safes to receive funds, which require > 6k gas for the call to reach the implementation contract.
In this case (the step 3/ above), if a caller (msg.sender
) would be a multisig or smart contract wallet like Safe that has the receive()
requiring >2300 gas
, their subsequent TokenManager#withdraw()
call will fail permanently.
As a result, native ETH to be withdrawn (via the transfer()
method in the TokenManager#withdraw()
) will be locked forever in the TokenManager contract, leading to loss of user (msg.sender
)'s collateral (native ETH).
(Reference: A similar finding in the past C4 contest is here)
Native ETH to be withdrawn (via the transfer()
method in the TokenManager#withdraw()
) will be locked forever in the TokenManager contract, leading to loss of user (msg.sender
)'s collateral (native ETH).
Foundry
Within the TokenManager#withdraw()
, consider using the low-level call
method of the native ETH - instead of using the transfer()
method of the native ETH like this:
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.