Platform: Code4rena
Start Date: 22/02/2022
Pot Size: $30,000 USDC
Total HM: 1
Participants: 22
Period: 3 days
Judge: leastwood
Id: 93
League: ETH
Rank: 1/22
Findings: 2
Award: $7,687.95
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: kirk-baird
LOW : 1. Title : permitAndMulticall() can be frontrun, that will lead to the user must reasign the TX
Impact : In the permitAndMulticall() it takes from
as a user input, since all tx in the blockchain is public, a malicious user might frontrun the permitAndMulticall() and providing 0 data, which will increment the nonce of the from
user in the erc20permit, by the time the original from
user tx get executed it will be revert since the nonce already inflate, and the original user must re-sign.
Mitigation : remove the from parameter, and change the from to msg.sender.
Title : Code didnt match with the documentation for representative flow
Impact : In the https://dev.pooltogether.com/protocol/contracts/v4-twab-delegator/
it explain that the the delegator must stake first before the representative can createDelegation, but in the https://github.com/pooltogether/v4-twab-delegator/blob/2b6d42506187dd7096043e2dfec65fa06ab18577/contracts/TWABDelegator.sol#L400
the delegator can set a representative and the code didnt check if the delegator already have stake a token or not, after setting a representative the representative than allow to call createDelegation with the delegator stake a token first.
#0 - PierrickGT
2022-03-02T17:59:35Z
Title : permitAndMulticall() can be frontrun, that will lead to the user must reasign the TX
Duplicate of https://github.com/code-423n4/2022-02-pooltogether-findings/issues/20
Title : Code didnt match with the documentation for representative flow
This is an example flow, the order of operations can differ and the code don't have to match with it. It is indeed possible to set a representative before staking but without any staked amount, a representative will only be able to create a delegation and not fund it.
#1 - 0xleastwood
2022-03-13T05:38:01Z
I think it is fair to have this upgraded to medium
severity considering the fact that the warden has outlined the potential issue. While it is mostly up to wardens to effectively identify the correct severity for an issue, I think in this instance, the warden has provided a clear attack vector. As such, I'll ignore this for QA report consideration and just bump it to medium
.
GAS : 1. Title : using uint96 in a struct can save gas
Impact : By changing the call struct inside delegation contract from using uint256 to uint96 can save some gas, because the current struct is using 3 slot storage, by changing it to uint96, the address and uint96 will be placed in the same slot, therefore it only take 2 slot spaces.
Title : unnecesary check on _requireDelegatorOrRepresentative()
Impact : There is no need to check if the representatives[_delegator][msg.sender] == true
, because if the delegator already set the msg.sender as the representative the value would be true
, by removing == true
will save some gas
Mitigation :
function _requireDelegatorOrRepresentative(address _delegator) internal view { require( _delegator == msg.sender || representatives[_delegator][msg.sender], "TWABDelegator/not-delegator-or-rep" ); }
#0 - PierrickGT
2022-03-02T00:43:47Z
using uint96 in a struct can save gas
It actually becomes more gas expensive to store a uint96
instead of a uint256
and this because value
will need to be downcasted from uint256
to uint96
when calling executeCall
.
I did some gas golfing with the createDelegation
function and the results are the following.
With uint256
(on average): 135,687
With uint96
(on average): 135,745
unnecesary check on _requireDelegatorOrRepresentative()
Duplicate of https://github.com/code-423n4/2022-02-pooltogether-findings/issues/15