Platform: Code4rena
Start Date: 02/08/2023
Pot Size: $42,000 USDC
Total HM: 13
Participants: 45
Period: 5 days
Judge: hickuphh3
Total Solo HM: 5
Id: 271
League: ETH
Rank: 20/45
Findings: 2
Award: $129.80
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Rolezn
Also found by: 0xhex, 0xta, JCK, K42, Rageur, Raihan, ReyAdmirado, SAQ, SY_S, dharma09, hunter_w3b, petrichor, shamsulhaq123, wahedtalash77
30.6063 USDC - $30.61
Possible Optimization =
SSTORE
operations by packing multiple smaller variables into a single storage slot.// Before uint112 _lastNonZeroAmountIn; uint112 _lastNonZeroAmountOut; uint96 _amountInForPeriod; uint96 _amountOutForPeriod; uint16 _period; uint48 _lastAuctionTime;
After Optimization:
// After struct AuctionState { uint112 lastNonZeroAmountIn; uint112 lastNonZeroAmountOut; uint96 amountInForPeriod; uint96 amountOutForPeriod; uint16 period; uint48 lastAuctionTime; } AuctionState public auctionState;
Possible Optimization 1 =
SSTORE
operations by packing multiple smaller variables into a single storage slot.uint64 internal _auctionDurationSeconds; UD2x18 internal _auctionTargetTimeFraction;
After Optimization:
struct AuctionParameters { uint64 auctionDurationSeconds; UD2x18 auctionTargetTimeFraction; } AuctionParameters internal auctionParameters;
Estimated gas saved = This optimization doesn't directly save gas on execution, but it does reduce the amount of storage used, which can lead to gas savings when updating these variables. Each SSTORE
operation costs 5,000 gas, so if we can reduce the number of these operations, we can save gas.
Possible Optimization 2 =
After Optimization:
function rngComplete( uint256 _randomNumber, uint256 _rngCompletedAt, address _rewardRecipient, uint32 _sequenceId, AuctionResult calldata _rngAuctionResult ) external returns (bytes32) { if (_sequenceHasCompleted(_sequenceId)) revert SequenceAlreadyCompleted(); uint64 _auctionElapsedSeconds = uint64(block.timestamp < _rngCompletedAt ? 0 : block.timestamp - _rngCompletedAt); if (_auctionElapsedSeconds > (_auctionDurationSeconds-1)) revert AuctionExpired(); // Calculate the reward fraction and set the draw auction results UD2x18 rewardFraction = _fractionalReward(_auctionElapsedSeconds); _auctionResults = AuctionResult({ rewardFraction: rewardFraction, recipient: _rewardRecipient }); _lastSequenceId = _sequenceId; AuctionResult[] memory auctionResults = new AuctionResult[](2); auctionResults[0] = _rngAuctionResult; auctionResults[1] = _auctionResults; uint32 drawId = prizePool.closeDraw(_randomNumber); uint256 futureReserve = prizePool.reserve() + prizePool.reserveForOpenDraw(); uint256[] memory _rewards = RewardLib.rewards(auctionResults, futureReserve); emit RngSequenceCompleted( _sequenceId, drawId, _rewardRecipient, _auctionElapsedSeconds, rewardFraction ); for (uint8 i = 0; i < _rewards.length; i++) { uint104 _reward = uint104(_rewards[i]); if (_reward > 0) { prizePool.withdrawReserve(auctionResults[i].recipient, _reward); emit AuctionRewardDistributed(_sequenceId, auctionResults[i].recipient, i, _reward); } } return bytes32(uint(drawId)); }
SSTORE
operations.#0 - c4-judge
2023-08-14T11:09:06Z
HickupHH3 marked the issue as grade-b
🌟 Selected for report: 3agle
Also found by: 0xSmartContract, 0xmystery, DedOhWale, K42, cholakov, hunter_w3b
The PoolTogether-v5 ecosystem is composed of several key components:
Vaults: Vaults are the primary storage mechanism for user deposits. They also handle the distribution of prizes. Each vault is associated with a specific ERC20 token.
Prize Pools: Prize pools are the pools of funds that users contribute to. The interest earned on these funds is used to provide the prizes for the pool.
Draw Auction: The Draw Auction is a mechanism that selects winners for the prizes in a fair and transparent manner.
Liquidation Pairs: Liquidation pairs are used to facilitate the conversion of one token into another. This is used for the liquidation of yield.
Claimer: The Claimer is a contract that handles the claiming of prizes by winners.
TWAB Controller: The TWAB (Time-Weighted Average Balance) Controller is a contract that tracks the balances of users over time. This is used to calculate a user's chance of winning a prize.
The architecture of PoolTogether-v5 is robust and well-designed. However, there are a few areas where improvements could be made:
Gas Optimizations: As discussed in my gas report, there are a few areas in the contracts where gas optimizations could be implemented. These include reducing the number of SLOAD operations and using events to emit less frequently used data instead of storing them in contract storage.
Upgradeability: The contracts do not appear to have any upgradeability mechanisms in place. This could potentially limit the ability of the protocol to adapt and evolve over time.
The PoolTogether-v5 protocol is designed to be decentralized, with no single point of failure. However, there are some centralization risks:
Admin Privileges: Some contracts in the codebase have functions that can only be called by the contract owner. This could potentially be a centralization risk if the owner account is compromised.
External Dependencies: The protocol relies on external contracts for some of its functionality, such as the ERC20 token contracts and the Uniswap contracts used for liquidation. If these external contracts were to fail or be compromised, it could impact the functioning of the PoolTogether-v5 protocol.
16 hours
#0 - c4-judge
2023-08-14T10:56:59Z
HickupHH3 marked the issue as grade-b