The original implementation used the transfer() method inside the claim() function to send a user's withdrawn ETH, but transfer() only forwards 2300 gas.
This is problematic because claim() requires the caller to be the same address that initiated the original withdrawal request via withdraw(). If a user calls withdraw() from a contract account that requires more than 2300 gas, their subsequent claim() call will fail permanently. Consequently, the withdrawn ETH will be locked in the WithdrawQueue contract indefinitely, leading to a loss of funds.
The issue is resolved by using the low-level call method instead of transfer, allowing multisigs or other smart contract wallets to claim the withdrawal request in Native ETH. Additionally, a check is implemented to ensure the call was successful; if not, the transaction reverts.
// send selected redeem asset to user
if (_withdrawRequest.collateralToken == IS_NATIVE) {
(bool success, ) = payable(msg.sender).call{ value: _withdrawRequest.amountToRedeem }(
""
);
if (!success) revert TransferFailed();
Conclusion
The implementation of the low-level call method instead of transfer ensures that multisigs and smart contract wallets can successfully claim withdrawal requests in Native ETH, preventing funds from being permanently locked. This solution addresses the issue raised.
Lines of code
Vulnerability details
Lines of code
Vulnerability details
C4 issue
H-01: Withdrawals Can Be Locked Forever if Recipient is a Contract
Link to issue
Comments
The original implementation used the
transfer()
method inside theclaim()
function to send a user's withdrawn ETH, buttransfer()
only forwards 2300 gas.This is problematic because
claim()
requires the caller to be the same address that initiated the original withdrawal request viawithdraw()
. If a user callswithdraw()
from a contract account that requires more than 2300 gas, their subsequentclaim()
call will fail permanently. Consequently, the withdrawn ETH will be locked in theWithdrawQueue
contract indefinitely, leading to a loss of funds.Mitigation
PR: https://github.com/Renzo-Protocol/Contracts/pull/109/files
The issue is resolved by using the low-level call method instead of transfer, allowing multisigs or other smart contract wallets to claim the withdrawal request in Native ETH. Additionally, a check is implemented to ensure the call was successful; if not, the transaction reverts.
Conclusion
The implementation of the low-level call method instead of transfer ensures that multisigs and smart contract wallets can successfully claim withdrawal requests in Native ETH, preventing funds from being permanently locked. This solution addresses the issue raised.