Platform: Code4rena
Start Date: 31/08/2023
Pot Size: $55,000 USDC
Total HM: 5
Participants: 30
Period: 6 days
Judge: hickuphh3
Total Solo HM: 2
Id: 282
League: ETH
Rank: 29/30
Findings: 1
Award: $27.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: c3phas
Also found by: 0x11singh99, 0x3b, 0xta, JCK, K42, ReyAdmirado, SAQ, Sathish9098, hunter_w3b, kaveyjoe, lsaudit, turvy_fuzz
27.0048 USDC - $27.00
Possible Optimization 1 =
bitmap
here for role-based access control.After Optimization:
require(accessControlBitmap[msg.sender] & ROLE_TICKET_BROKER != 0, "unauthorized");
Estimated Gas Saved = Approximately 800 gas.
Possible Optimization 2 =
N
blocks to avoid frequent external
calls. Make sure to ensure that the value of N
is chosen carefully to balance gas savings and data freshness.After Optimization:
if (block.number > lastRoundBlock + N) { currentRoundInitialized = roundsManager().currentRoundInitialized(); lastRoundBlock = block.number; }
Estimated Gas Saved = Approximately 1500 gas.
Possible Optimization =
After Optimization:
bool roleChanged = (previousDelegate ^ newDelegate) != 0; if (roleChanged) { emit DelegateChanged(_account, previousDelegate, newDelegate); }
EQ
and ISZERO
opcodes by using bitwise XOR
.Possible Optimizations =
After Optimization for updateCumulativeFeeFactor():
// Early return if _fees is zero, avoiding unnecessary calculations if (_fees == 0) return; // Initialize cumulativeFeeFactor if it's zero if (earningsPool.cumulativeFeeFactor == 0) { earningsPool.cumulativeFeeFactor = prevCumulativeFeeFactor; // Use the previous factor directly } // Update the cumulativeFeeFactor earningsPool.cumulativeFeeFactor = earningsPool.cumulativeFeeFactor.add( PreciseMathUtils.percOf(prevCumulativeRewardFactor, _fees, earningsPool.totalStake) );
After Optimization for updateCumulativeRewardFactor():
// Early return if _rewards is zero, avoiding unnecessary calculations if (_rewards == 0) return; // Initialize cumulativeRewardFactor if it's zero if (earningsPool.cumulativeRewardFactor == 0) { earningsPool.cumulativeRewardFactor = prevCumulativeRewardFactor; // Use the previous factor directly } // Update the cumulativeRewardFactor earningsPool.cumulativeRewardFactor = earningsPool.cumulativeRewardFactor.add( PreciseMathUtils.percOf(prevCumulativeRewardFactor, _rewards, earningsPool.totalStake) );
_fees == 0:
~5000 gas (due to avoided SSTORE),
avoiding ADD
operation: ~3 gas = Total: ~5003 gas. Therefore for both potential 10006 gas saved.Possible Optimization 1 =
After Optimization:
// Directly compare with the last element, eliminating the need for a separate variable. if (val < array[array.length - 1]) { revert DecreasingValues(val, array[array.length - 1]); } if (val != array[array.length - 1]) { array.push(val); }
Possible Optimization 2 =
if
statement with logical OR (||)
to combine conditions in pushSorted().After Optimization:
// Combine conditions using logical OR to reduce the number of conditional jumps. if (val < array[array.length - 1] || val != array[array.length - 1]) { revert DecreasingValues(val, array[array.length - 1]); } else { array.push(val); }
Possible Optimization =
uint256
to store all three vote counts (againstVotes, forVotes, abstainVotes) by partitioning the uint256
into three 85-bit
segments. This will reduce the storage space and consequently the gas for storage operations.After Optimization in the ProposalTally struct:
struct ProposalTally { uint256 voteCounts; // againstVotes in bits 0-84, forVotes in bits 85-169, abstainVotes in bits 170-254 mapping(address => ProposalVoterState) voters; }
Then update _countVote() like so:
// Use bitwise operations to update the vote counts. uint256 shift = uint256(support) * 85; uint256 mask = uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) >> (256 - 85); tally.voteCounts = (tally.voteCounts & ~(mask << shift)) | ((_weight & mask) << shift);
Then update proposalVotes() like so:
function proposalVotes(uint256 _proposalId) public view returns ( uint256 againstVotes, uint256 forVotes, uint256 abstainVotes ) { uint256 voteCounts = _proposalTallies[_proposalId].voteCounts; uint256 mask = uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) >> (256 - 85); againstVotes = (voteCounts >> 0) & mask; forVotes = (voteCounts >> 85) & mask; abstainVotes = (voteCounts >> 170) & mask; }
Then update _quorumReached():
function _quorumReached(uint256 _proposalId) internal view override returns (bool) { (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) = proposalVotes(_proposalId); uint256 totalVotes = againstVotes + forVotes + abstainVotes; return totalVotes >= quorum(proposalSnapshot(_proposalId)); }
Finally the _voteSucceeded() Function:
function _voteSucceeded(uint256 _proposalId) internal view override returns (bool) { (uint256 againstVotes, uint256 forVotes, ) = proposalVotes(_proposalId); uint256 opinionatedVotes = againstVotes + forVotes; return forVotes >= MathUtils.percOf(opinionatedVotes, quota); }
2^85 − 1
, which is a very large number and unlikely to be reached.Possible Optimization =
struct
to reduce the gas cost of the stack operations.After Optimization:
// Define a struct for initialization parameters struct InitParams { uint256 initialVotingDelay; uint256 initialVotingPeriod; uint256 initialProposalThreshold; uint256 initialQuorum; uint256 quota; } // Modify initialize function to accept the struct function initialize(InitParams memory params) public initializer { __Governor_init("LivepeerGovernor"); __GovernorSettings_init(params.initialVotingDelay, params.initialVotingPeriod, params.initialProposalThreshold); __GovernorTimelockControl_init(treasury()); __GovernorVotes_init(votes()); __GovernorVotesQuorumFraction_init(params.initialQuorum); __GovernorCountingOverridable_init(params.quota); }
#0 - c4-judge
2023-09-21T10:46:22Z
HickupHH3 marked the issue as grade-b