Requestor uses to.transfer() to withdraw the balance of the creator, but the creator may not be able to receive it
Summary
address.transfer() only forwards 2300 gas, which can easily make gas sent revert if the target is a smart contract wallet. Thus, users participating in the draws (which is permissionless, and intends to allow anyone to participate, as in the docs), may not be able to withdraw their funds after funding rng requests.
Vulnerability Detail
The auction in DrawManager is started by calling RngWitnet::startDraw(), which requests a random number from Requestor::randomize() and then DrawManager::startDraw().
Users starting the auction can choose to send funds to the Requestor directly or as msg.value in the RngWitnet::startDraw() call. Either way, funds are expected to be leftover in the Requestor contract, as it is not possible to predict the exact gas price and corresponding fee (the fee is based on the gas price) that will be used when the RngWitnet::startDraw() is called. Also, requests that have already been requested for the same block number have their msg.value refunded.
Lastly, the funds are always sent to the msg.sender in requestors[msg.sender], corresponding to the Requestor, and RngWitnet is the creator, which has no way of specifying a different address to send the funds to.
Thus, as PoolTogether incentives the participation of anyone, some users using smart wallets may request randomness, paying the fee and leaving the excess in the Requestor contract, and then when they try to withdraw, it fails.
Impact
Stuck tokens due to not being able to withdraw funds from the Requestor.
0x73696d616f
medium
Requestor
usesto.transfer()
to withdraw the balance of the creator, but the creator may not be able to receive itSummary
address.transfer()
only forwards 2300 gas, which can easily make gas sent revert if the target is a smart contract wallet. Thus, users participating in the draws (which is permissionless, and intends to allow anyone to participate, as in the docs), may not be able to withdraw their funds after funding rng requests.Vulnerability Detail
The auction in
DrawManager
is started by callingRngWitnet::startDraw()
, which requests a random number fromRequestor::randomize()
and thenDrawManager::startDraw()
.Users starting the auction can choose to send funds to the
Requestor
directly or asmsg.value
in theRngWitnet::startDraw()
call. Either way, funds are expected to be leftover in theRequestor
contract, as it is not possible to predict the exact gas price and corresponding fee (the fee is based on the gas price) that will be used when theRngWitnet::startDraw()
is called. Also, requests that have already been requested for the same block number have theirmsg.value
refunded.Lastly, the funds are always sent to the
msg.sender
inrequestors[msg.sender]
, corresponding to theRequestor
, andRngWitnet
is the creator, which has no way of specifying a different address to send the funds to.Thus, as
PoolTogether
incentives the participation of anyone, some users using smart wallets may request randomness, paying the fee and leaving the excess in theRequestor
contract, and then when they try to withdraw, it fails.Impact
Stuck tokens due to not being able to withdraw funds from the
Requestor
.Code Snippet
https://github.com/sherlock-audit/2024-05-pooltogether/blob/main/pt-v5-rng-witnet/src/Requestor.sol#L36-L40
Tool used
Manual Review
Vscode
Recommendation
Use
address.call{value: address(this).balance)}("")
to send the funds.Duplicate of #80