Platform: Code4rena
Start Date: 26/05/2022
Pot Size: $75,000 USDT
Total HM: 31
Participants: 71
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 18
Id: 126
League: ETH
Rank: 11/71
Findings: 3
Award: $2,215.35
π Selected for report: 1
π Solo Findings: 1
π Selected for report: kirk-baird
Also found by: Dravee, IllIllI, Koustre, Ruhum, VAD37, csanuragjain, gzeon, unforgiven, xiaoming90
80.6857 USDT - $80.69
https://github.com/code-423n4/2022-05-vetoken/blob/main/contracts/VE3DRewardPool.sol#L134-L139 https://github.com/code-423n4/2022-05-vetoken/blob/main/contracts/VE3DRewardPool.sol#L214-L216
Whenever a user stakes or withdraws funds through the VE3DRewardPool contract, the contract loops over the array. It calls the stake()
/withdraw()
function for each of the addresses. If the size of the array is too big, the transaction might reach the block gas limit. The contract would be unusable until the array is cleaned up through clearExtraRewards()
.
rewardManager
calls addExtraReward()
multiple times. At some point the array is too big.stake()
, stakeFor()
, and withdraw()
by users will failnone
Add a cap to the number of elements in the array. Same as in BaseRewardPool
#0 - jetbrain10
2022-06-15T15:56:00Z
will add 8 as cap for ve3d rewardpool
#1 - GalloDaSballo
2022-07-26T01:14:25Z
Dup of #136
π Selected for report: Ruhum
2082.6377 USDT - $2,082.64
https://github.com/code-423n4/2022-05-vetoken/blob/main/contracts/VE3DRewardPool.sol#L134-L139 https://github.com/code-423n4/2022-05-vetoken/blob/main/contracts/VE3DRewardPool.sol#L214-L216 https://github.com/code-423n4/2022-05-vetoken/blob/main/contracts/BaseRewardPool.sol#L121
When the same address is included twice it might cause issues depending on the contract. I checked the current convex contract to see which addresses were added to the extraRewards
array. For cvxCRV Rewards there's only 0x7091dbb7fcbA54569eF1387Ac89Eb2a5C9F6d2EA which is the VirtualBalanceRewardPool contract.
rewardManager
adds the same address twice through addExtraReward()
stake()
twice with the same amount:function stake(uint256 _amount) public updateReward(msg.sender) { require(_amount > 0, "RewardPool : Cannot stake 0"); //also stake to linked rewards uint256 length = extraRewards.length; for (uint256 i = 0; i < length; i++) { IRewards(extraRewards[i]).stake(msg.sender, _amount); } //add supply _totalSupply = _totalSupply.add(_amount); //add to sender balance sheet _balances[msg.sender] = _balances[msg.sender].add(_amount); //take tokens from sender stakingToken.safeTransferFrom(msg.sender, address(this), _amount); emit Staked(msg.sender, _amount); }
none
Prevent the same addresses from being added multiple times to the extraRewards
array.
#0 - jetbrain10
2022-06-15T15:56:46Z
will add code to check if this is same reward address
#1 - GalloDaSballo
2022-07-26T01:53:42Z
The warden has shown how, due to a misconfiguration, rewards could be disbursed twice, breaking protocol invariants.
Because this is contingent on admin privilege, I believe Medium Severity to be appropriate
π Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, Cityscape, Dravee, ElKu, FSchmoede, Funen, GalloDaSballo, Hawkeye, Kaiziron, MiloTruck, Randyyy, RoiEvenHaim, Ruhum, SecureZeroX, SmartSek, TerrierLover, TomJ, Tomio, WatchPug, Waze, _Adam, asutorufos, c3phas, catchup, cogitoergosumsw, delfin454000, ellahi, fatherOfBlocks, gzeon, hansfriese, horsefacts, jonatascm, minhquanym, oyc_109, pauliax, reassor, robee, sach1r0, saian, sashik_eth, simon135, z3s
52.0224 USDT - $52.02
When looping over an array the condition of the loop is checked for every iteration. If you read the length of a storage variable multiple times it can get quite expensive. Instead, cache the length of memory and check that.
// bad for (uint i; i < arr.length; i++) { } // good uint length = arr.length; for (uint i; i < length; i++) { }
There are quite a number of instances where this can be used. Just search for "for (" and you'll find them.
Whenever you read the same storage variable twice, you should cache it in memory. It will save you a SLOAD.
An example would be platformFee
in _earmarkRewards()
L526-L528
#0 - GalloDaSballo
2022-07-18T23:27:20Z
Less than 200 gas saved
#1 - GalloDaSballo
2022-07-18T23:27:45Z
Very basic against even the other "less than 500 gas" reports