Platform: Code4rena
Start Date: 23/05/2022
Pot Size: $75,000 USDC
Total HM: 23
Participants: 75
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 13
Id: 130
League: ETH
Rank: 59/75
Findings: 1
Award: $70.31
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0xNazgul, 0xf15ers, 0xkatana, Chom, ControlCplusControlV, DavidGialdi, Deivitto, Dravee, ElKu, Fitraldys, Funen, GimelSec, MadWookie, MaratCerby, MiloTruck, Picodes, Randyyy, TerrierLover, TomJ, Tomio, UnusualTurtle, WatchPug, Waze, _Adam, asutorufos, c3phas, catchup, csanuragjain, delfin454000, djxploit, fatherOfBlocks, gzeon, hake, hansfriese, horsefacts, orion, oyc_109, pauliax, reassor, rfa, rotcivegaf, sach1r0, saian, sashik_eth, simon135, supernova, teddav, z3s
70.3057 USDC - $70.31
You can use the babylonian method to calculate the square root, since its simpler it will make you save gas.
Replace lines L10-L21 with this:
function sqrt(uint x) internal pure returns (uint y) { unchecked { uint z = (x + 1) / 2; y = x; while (z < y) { y = z; z = (x / z + z) / 2; } }}
source: #1
You can use the babylonian method to calculate the cube root, since its simpler it will make you save gas.
Replace lines L22-L33 with this:
function cbrt(uint256 x) internal pure returns (uint256 y) { unchecked { uint z = (x + 1) / 3; y = x; while (z < y) { y = z; z = (x / z + z) / 3; } }}
source: #1
++i
or better unchecked{ ++i; }
instead of i++
++i
costs less gas compared to i++
or i += 1
for unsigned integer, as pre-increment is cheaper. This statement is true even with the optimizer enabled.
If you use unchecked{ ++i; }
you will be saving a lot more of gas, because the overflow checks are turn of, so used when you know its safe.
Recommendation:
diff --git a/contracts/contracts/Velo.sol b/contracts/contracts/Velo.sol index 17915c9..0408934 100644 --- a/contracts/contracts/Velo.sol +++ b/contracts/contracts/Velo.sol @@ -40,15 +40,23 @@ contract Velo { } function _mint(address _to, uint _amount) internal returns (bool) { - balanceOf[_to] += _amount; totalSupply += _amount; + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[_to] += _amount; + } emit Transfer(address(0x0), _to, _amount); return true; } function _transfer(address _from, address _to, uint _value) internal returns (bool) { balanceOf[_from] -= _value; - balanceOf[_to] += _value; + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[_to] += _value; + } emit Transfer(_from, _to, _value); return true; }
diff --git a/contracts/contracts/Gauge.sol b/contracts/contracts/Gauge.sol index 64ee49d..e24c8b1 100644 --- a/contracts/contracts/Gauge.sol +++ b/contracts/contracts/Gauge.sol @@ -176,12 +176,13 @@ contract Gauge { uint bribeStart = block.timestamp - (block.timestamp % (7 days)) + BRIBE_LAG; uint numRewards = sb.rewardsListLength(); - for (uint i = 0; i < numRewards; i++) { + for (uint i = 0; i < numRewards;) { address token = sb.rewards(i); uint epochRewards = sb.deliverReward(token, bribeStart); if (epochRewards > 0) { _notifyBribeAmount(token, epochRewards, bribeStart); } + unchecked { ++i; } } } @@ -350,7 +351,7 @@ contract Gauge { IVoter(voter).distribute(address(this)); _unlocked = 2; - for (uint i = 0; i < tokens.length; i++) { + for (uint i = 0; i < tokens.length;) { (rewardPerTokenStored[tokens[i]], lastUpdateTime[tokens[i]]) = _updateRewardPerToken(tokens[i]); uint _reward = earned(tokens[i], account); @@ -359,6 +360,8 @@ contract Gauge { if (_reward > 0) _safeTransfer(tokens[i], account, _reward); emit ClaimRewards(msg.sender, tokens[i], _reward); + + unchecked { ++i; } } uint _derivedBalance = derivedBalances[account]; @@ -402,7 +405,7 @@ contract Gauge { uint _startIndex = getPriorSupplyIndex(_startTimestamp); uint _endIndex = Math.min(supplyNumCheckpoints-1, maxRuns); - for (uint i = _startIndex; i < _endIndex; i++) { + for (uint i = _startIndex; i < _endIndex;) { SupplyCheckpoint memory sp0 = supplyCheckpoints[i]; if (sp0.supply > 0) { SupplyCheckpoint memory sp1 = supplyCheckpoints[i+1]; @@ -411,6 +414,7 @@ contract Gauge { _writeRewardPerTokenCheckpoint(token, reward, _endTime); _startTimestamp = _endTime; } + unchecked { ++i; } } return (reward, _startTimestamp); @@ -423,9 +427,10 @@ contract Gauge { function _updateRewardForAllTokens() internal { uint length = rewards.length; - for (uint i; i < length; i++) { + for (uint i; i < length;) { address token = rewards[i]; (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token); + unchecked { ++i; } } } @@ -445,7 +450,7 @@ contract Gauge { uint _endIndex = supplyNumCheckpoints-1; if (_endIndex > 0) { - for (uint i = _startIndex; i < _endIndex; i++) { + for (uint i = _startIndex; i < _endIndex;) { SupplyCheckpoint memory sp0 = supplyCheckpoints[i]; if (sp0.supply > 0) { SupplyCheckpoint memory sp1 = supplyCheckpoints[i+1]; @@ -454,6 +459,7 @@ contract Gauge { _writeRewardPerTokenCheckpoint(token, reward, _endTime); _startTimestamp = _endTime; } + unchecked { ++i; } } } @@ -481,7 +487,7 @@ contract Gauge { uint reward = 0; if (_endIndex > 0) { - for (uint i = _startIndex; i < _endIndex; i++) { + for (uint i = _startIndex; i < _endIndex;) { Checkpoint memory cp0 = checkpoints[account][i]; Checkpoint memory cp1 = checkpoints[account][i+1]; (uint _rewardPerTokenStored0,) = getPriorRewardPerToken(token, cp0.timestamp); @@ -489,6 +495,7 @@ contract Gauge { if (cp0.voted) { reward += cp0.balanceOf * (_rewardPerTokenStored1 - _rewardPerTokenStored0) / PRECISION; } + unchecked { ++i; } } } diff --git a/contracts/contracts/Minter.sol b/contracts/contracts/Minter.sol index 9821ea4..1c690f0 100644 --- a/contracts/contracts/Minter.sol +++ b/contracts/contracts/Minter.sol @@ -54,8 +54,9 @@ contract Minter { require(initializer == msg.sender); _velo.mint(address(this), max); _velo.approve(address(_ve), type(uint).max); - for (uint i = 0; i < claimants.length; i++) { + for (uint i = 0; i < claimants.length;) { _ve.create_lock_for(amounts[i], LOCK, claimants[i]); + unchecked { ++i; } } initializer = address(0); active_period = ((block.timestamp + WEEK) / WEEK) * WEEK; diff --git a/contracts/contracts/Pair.sol b/contracts/contracts/Pair.sol index b42154f..3e06667 100644 --- a/contracts/contracts/Pair.sol +++ b/contracts/contracts/Pair.sol @@ -254,8 +254,9 @@ contract Pair { function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) { uint [] memory _prices = sample(tokenIn, amountIn, granularity, 1); uint priceAverageCumulative; - for (uint i = 0; i < _prices.length; i++) { + for (uint i = 0; i < _prices.length;) { priceAverageCumulative += _prices[i]; + unchecked { ++i; } } return priceAverageCumulative / granularity; } @@ -386,7 +387,7 @@ contract Pair { } function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) { - for (uint i = 0; i < 255; i++) { + for (uint i = 0; i < 255;) { uint y_prev = y; uint k = _f(x0, y); if (k < xy) { @@ -405,6 +406,7 @@ contract Pair { return y; } } + unchecked { ++i; } } return y; } diff --git a/contracts/contracts/RewardsDistributor.sol b/contracts/contracts/RewardsDistributor.sol index a2d4585..25efe20 100644 --- a/contracts/contracts/RewardsDistributor.sol +++ b/contracts/contracts/RewardsDistributor.sol @@ -72,7 +72,7 @@ contract RewardsDistributor { uint this_week = t / WEEK * WEEK; uint next_week = 0; - for (uint i = 0; i < 20; i++) { + for (uint i = 0; i < 20;) { next_week = this_week + WEEK; if (block.timestamp < next_week) { if (since_last == 0 && block.timestamp == t) { @@ -90,6 +90,7 @@ contract RewardsDistributor { } t = next_week; this_week = next_week; + unchecked { ++i; } } emit CheckpointToken(block.timestamp, to_distribute); } @@ -102,7 +103,7 @@ contract RewardsDistributor { function _find_timestamp_epoch(address ve, uint _timestamp) internal view returns (uint) { uint _min = 0; uint _max = IVotingEscrow(ve).epoch(); - for (uint i = 0; i < 128; i++) { + for (uint i = 0; i < 128;) { if (_min >= _max) break; uint _mid = (_min + _max + 2) / 2; IVotingEscrow.Point memory pt = IVotingEscrow(ve).point_history(_mid); @@ -111,6 +112,7 @@ contract RewardsDistributor { } else { _max = _mid - 1; } + unchecked { ++i; } } return _min; } @@ -118,7 +120,7 @@ contract RewardsDistributor { function _find_timestamp_user_epoch(address ve, uint tokenId, uint _timestamp, uint max_user_epoch) internal view returns (uint) { uint _min = 0; uint _max = max_user_epoch; - for (uint i = 0; i < 128; i++) { + for (uint i = 0; i < 128;) { if (_min >= _max) break; uint _mid = (_min + _max + 2) / 2; IVotingEscrow.Point memory pt = IVotingEscrow(ve).user_point_history(tokenId, _mid); @@ -127,6 +129,7 @@ contract RewardsDistributor { } else { _max = _mid -1; } + unchecked { ++i; } } return _min; } @@ -145,7 +148,7 @@ contract RewardsDistributor { uint rounded_timestamp = block.timestamp / WEEK * WEEK; IVotingEscrow(ve).checkpoint(); - for (uint i = 0; i < 20; i++) { + for (uint i = 0; i < 20;) { if (t > rounded_timestamp) { break; } else { @@ -158,6 +161,7 @@ contract RewardsDistributor { ve_supply[t] = Math.max(uint(int256(pt.bias - pt.slope * dt)), 0); } t += WEEK; + unchecked { ++i; } } time_cursor = t; } @@ -192,7 +196,7 @@ contract RewardsDistributor { IVotingEscrow.Point memory old_user_point; - for (uint i = 0; i < 50; i++) { + for (uint i = 0; i < 50;) { if (week_cursor >= _last_token_time) break; if (week_cursor >= user_point.ts && user_epoch <= max_user_epoch) { @@ -212,6 +216,7 @@ contract RewardsDistributor { } week_cursor += WEEK; } + unchecked { ++i; } } user_epoch = Math.min(max_user_epoch, user_epoch - 1); @@ -249,7 +254,7 @@ contract RewardsDistributor { IVotingEscrow.Point memory old_user_point; - for (uint i = 0; i < 50; i++) { + for (uint i = 0; i < 50;) { if (week_cursor >= _last_token_time) break; if (week_cursor >= user_point.ts && user_epoch <= max_user_epoch) { @@ -269,6 +274,7 @@ contract RewardsDistributor { } week_cursor += WEEK; } + unchecked { ++i; } } return to_distribute; @@ -298,7 +304,7 @@ contract RewardsDistributor { address _voting_escrow = voting_escrow; uint total = 0; - for (uint i = 0; i < _tokenIds.length; i++) { + for (uint i = 0; i < _tokenIds.length;) { uint _tokenId = _tokenIds[i]; if (_tokenId == 0) break; uint amount = _claim(_tokenId, _voting_escrow, _last_token_time); @@ -306,6 +312,7 @@ contract RewardsDistributor { IVotingEscrow(_voting_escrow).deposit_for(_tokenId, amount); total += amount; } + unchecked { ++i; } } if (total != 0) { token_last_balance -= total; diff --git a/contracts/contracts/Router.sol b/contracts/contracts/Router.sol index 6ef3e70..991e0cc 100644 --- a/contracts/contracts/Router.sol +++ b/contracts/contracts/Router.sol @@ -87,11 +87,12 @@ contract Router { require(routes.length >= 1, 'Router: INVALID_PATH'); amounts = new uint[](routes.length+1); amounts[0] = amountIn; - for (uint i = 0; i < routes.length; i++) { + for (uint i = 0; i < routes.length;) { address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable); if (IPairFactory(factory).isPair(pair)) { amounts[i+1] = IPair(pair).getAmountOut(amounts[i], routes[i].from); } + unchecked { ++i; } } } @@ -313,7 +314,7 @@ contract Router { // **** SWAP **** // requires the initial amount to have already been sent to the first pair function _swap(uint[] memory amounts, route[] memory routes, address _to) internal virtual { - for (uint i = 0; i < routes.length; i++) { + for (uint i = 0; i < routes.length;) { (address token0,) = sortTokens(routes[i].from, routes[i].to); uint amountOut = amounts[i + 1]; (uint amount0Out, uint amount1Out) = routes[i].from == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); @@ -321,6 +322,7 @@ contract Router { IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap( amount0Out, amount1Out, to, new bytes(0) ); + unchecked { ++i; } } } diff --git a/contracts/contracts/VelodromeLibrary.sol b/contracts/contracts/VelodromeLibrary.sol index 64bf5a9..1cf6065 100644 --- a/contracts/contracts/VelodromeLibrary.sol +++ b/contracts/contracts/VelodromeLibrary.sol @@ -21,7 +21,7 @@ contract VelodromeLibrary { } function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) { - for (uint i = 0; i < 255; i++) { + for (uint i = 0; i < 255;) { uint y_prev = y; uint k = _f(x0, y); if (k < xy) { @@ -40,6 +40,7 @@ contract VelodromeLibrary { return y; } } + unchecked { ++i; } } return y; } diff --git a/contracts/contracts/Voter.sol b/contracts/contracts/Voter.sol index 23453f1..c511b2b 100644 --- a/contracts/contracts/Voter.sol +++ b/contracts/contracts/Voter.sol @@ -73,8 +73,9 @@ contract Voter { function initialize(address[] memory _tokens, address _minter) external { require(msg.sender == minter); - for (uint i = 0; i < _tokens.length; i++) { + for (uint i = 0; i < _tokens.length;) { _whitelist(_tokens[i]); + unchecked { ++i; } } minter = _minter; } @@ -100,7 +101,7 @@ contract Voter { uint _poolVoteCnt = _poolVote.length; uint256 _totalWeight = 0; - for (uint i = 0; i < _poolVoteCnt; i ++) { + for (uint i = 0; i < _poolVoteCnt;) { address _pool = _poolVote[i]; uint256 _votes = votes[_tokenId][_pool]; @@ -114,6 +115,7 @@ contract Voter { IGauge(gauges[_pool]).setVoteStatus(IVotingEscrow(_ve).ownerOf(_tokenId), false); emit Abstained(_tokenId, _votes); } + unchecked{ ++i; } } totalWeight -= uint256(_totalWeight); usedWeights[_tokenId] = 0; @@ -125,8 +127,9 @@ contract Voter { uint _poolCnt = _poolVote.length; uint256[] memory _weights = new uint256[](_poolCnt); - for (uint i = 0; i < _poolCnt; i ++) { + for (uint i = 0; i < _poolCnt;) { _weights[i] = votes[_tokenId][_poolVote[i]]; + unchecked{ ++i; } } _vote(_tokenId, _poolVote, _weights); @@ -140,11 +143,12 @@ contract Voter { uint256 _totalWeight = 0; uint256 _usedWeight = 0; - for (uint i = 0; i < _poolCnt; i++) { + for (uint i = 0; i < _poolCnt;) { _totalVoteWeight += _weights[i]; + unchecked { ++i; } } - for (uint i = 0; i < _poolCnt; i++) { + for (uint i = 0; i < _poolCnt;) { address _pool = _poolVote[i]; address _gauge = gauges[_pool]; @@ -163,6 +167,7 @@ contract Voter { IGauge(gauges[_pool]).setVoteStatus(IVotingEscrow(_ve).ownerOf(_tokenId), true); emit Voted(msg.sender, _tokenId, _poolWeight); } + unchecked { ++i; } } if (_usedWeight > 0) IVotingEscrow(_ve).voting(_tokenId); totalWeight += uint256(_totalWeight); @@ -263,14 +268,16 @@ contract Voter { } function updateFor(address[] memory _gauges) external { - for (uint i = 0; i < _gauges.length; i++) { + for (uint i = 0; i < _gauges.length;) { _updateFor(_gauges[i]); + unchecked { ++i; } } } function updateForRange(uint start, uint end) public { - for (uint i = start; i < end; i++) { + for (uint i = start; i < end;) { _updateFor(gauges[pools[i]]); + unchecked { ++i; } } } @@ -301,14 +308,16 @@ contract Voter { } function claimRewards(address[] memory _gauges, address[][] memory _tokens) external { - for (uint i = 0; i < _gauges.length; i++) { + for (uint i = 0; i < _gauges.length;) { IGauge(_gauges[i]).getReward(msg.sender, _tokens[i]); + unchecked{ ++i; } } } function distributeFees(address[] memory _gauges) external { - for (uint i = 0; i < _gauges.length; i++) { + for (uint i = 0; i < _gauges.length;) { IGauge(_gauges[i]).claimFees(); + unchecked{ ++i; } } } @@ -337,14 +346,16 @@ contract Voter { } function distribute(uint start, uint finish) public { - for (uint x = start; x < finish; x++) { + for (uint x = start; x < finish;) { distribute(gauges[pools[x]]); + unchecked{ ++x; } } } function distribute(address[] memory _gauges) external { - for (uint x = 0; x < _gauges.length; x++) { + for (uint x = 0; x < _gauges.length;) { distribute(_gauges[x]); + unchecked{ ++x; } } } diff --git a/contracts/contracts/VotingEscrow.sol b/contracts/contracts/VotingEscrow.sol index b83e9c7..1f469b2 100644 --- a/contracts/contracts/VotingEscrow.sol +++ b/contracts/contracts/VotingEscrow.sol @@ -134,7 +134,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { uint temp = value; uint digits; while (temp != 0) { - digits++; + unchecked{ digits++; } temp /= 10; } bytes memory buffer = new bytes(digits); @@ -629,7 +629,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { // Go over weeks to fill history and calculate what the current point is { uint t_i = (last_checkpoint / WEEK) * WEEK; - for (uint i = 0; i < 255; ++i) { + for (uint i = 0; i < 255;) { // Hopefully it won't happen that this won't get used in 5 years! // If it does, users will be able to withdraw but vote weight will be broken t_i += WEEK; @@ -659,6 +659,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { } else { point_history[_epoch] = last_point; } + unchecked{ ++i; } } } @@ -786,7 +787,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { require(unlock_time > block.timestamp, 'Can only lock until time in the future'); require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max'); - ++tokenId; + unchecked{ ++tokenId; } uint _tokenId = tokenId; _mint(_to, _tokenId); @@ -883,7 +884,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { // Binary search uint _min = 0; uint _max = max_epoch; - for (uint i = 0; i < 128; ++i) { + for (uint i = 0; i < 128;) { // Will be always enough for 128-bit numbers if (_min >= _max) { break; @@ -894,6 +895,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { } else { _max = _mid - 1; } + unchecked{ ++i; } } return _min; } @@ -939,7 +941,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { // Binary search uint _min = 0; uint _max = user_point_epoch[_tokenId]; - for (uint i = 0; i < 128; ++i) { + for (uint i = 0; i < 128;) { // Will be always enough for 128-bit numbers if (_min >= _max) { break; @@ -950,6 +952,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { } else { _max = _mid - 1; } + unchecked{ ++i; } } Point memory upoint = user_point_history[_tokenId][_min]; @@ -1014,7 +1017,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { function _supply_at(Point memory point, uint t) internal view returns (uint) { Point memory last_point = point; uint t_i = (last_point.ts / WEEK) * WEEK; - for (uint i = 0; i < 255; ++i) { + for (uint i = 0; i < 255;) { t_i += WEEK; int128 d_slope = 0; if (t_i > t) { @@ -1028,6 +1031,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { } last_point.slope += d_slope; last_point.ts = t_i; + unchecked{ ++i; } } if (last_point.bias < 0) { @@ -1143,9 +1147,10 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { } uint[] storage _tokenIds = checkpoints[account][nCheckpoints - 1].tokenIds; uint votes = 0; - for (uint i = 0; i < _tokenIds.length; i++) { + for (uint i = 0; i < _tokenIds.length;) { uint tId = _tokenIds[i]; votes = votes + _balanceOfNFT(tId, block.timestamp); + unchecked { ++i; } } return votes; } @@ -1190,10 +1195,11 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { // Sum votes uint[] storage _tokenIds = checkpoints[account][_checkIndex].tokenIds; uint votes = 0; - for (uint i = 0; i < _tokenIds.length; i++) { + for (uint i = 0; i < _tokenIds.length;) { uint tId = _tokenIds[i]; // Use the provided input timestamp here to get the right decay votes = votes + _balanceOfNFT(tId, timestamp); + unchecked { ++i; } } return votes; } @@ -1222,11 +1228,12 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { nextSrcRepNum ].tokenIds; // All the same except _tokenId - for (uint i = 0; i < srcRepOld.length; i++) { + for (uint i = 0; i < srcRepOld.length;) { uint tId = srcRepOld[i]; if (tId != _tokenId) { srcRepNew.push(tId); } + unchecked{ ++i; } } numCheckpoints[srcRep] = srcRepNum + 1; @@ -1246,9 +1253,10 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { dstRepOld.length + 1 <= MAX_DELEGATES, "dstRep would have too many tokenIds" ); - for (uint i = 0; i < dstRepOld.length; i++) { + for (uint i = 0; i < dstRepOld.length;) { uint tId = dstRepOld[i]; dstRepNew.push(tId); + unchecked { ++i; } } dstRepNew.push(_tokenId); @@ -1292,11 +1300,12 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { nextSrcRepNum ].tokenIds; // All the same except what owner owns - for (uint i = 0; i < srcRepOld.length; i++) { + for (uint i = 0; i < srcRepOld.length;) { uint tId = srcRepOld[i]; if (idToOwner[tId] != owner) { srcRepNew.push(tId); } + unchecked{ ++i; } } numCheckpoints[srcRep] = srcRepNum + 1; @@ -1317,14 +1326,16 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { "dstRep would have too many tokenIds" ); // All the same - for (uint i = 0; i < dstRepOld.length; i++) { + for (uint i = 0; i < dstRepOld.length;) { uint tId = dstRepOld[i]; dstRepNew.push(tId); + unchecked{ ++i; } } // Plus all that's owned - for (uint i = 0; i < ownerTokenCount; i++) { + for (uint i = 0; i < ownerTokenCount;) { uint tId = ownerToNFTokenIdList[owner][i]; dstRepNew.push(tId); + unchecked{ ++i; } } numCheckpoints[dstRep] = dstRepNum + 1; diff --git a/contracts/contracts/governance/L2Governor.sol b/contracts/contracts/governance/L2Governor.sol index 36c92da..3c60612 100644 --- a/contracts/contracts/governance/L2Governor.sol +++ b/contracts/contracts/governance/L2Governor.sol @@ -323,9 +323,10 @@ abstract contract L2Governor is Context, ERC165, EIP712, IGovernor, IERC721Recei bytes32 /*descriptionHash*/ ) internal virtual { string memory errorMessage = "Governor: call reverted without message"; - for (uint256 i = 0; i < targets.length; ++i) { + for (uint256 i = 0; i < targets.length;) { (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]); Address.verifyCallResult(success, returndata, errorMessage); + unchecked{ ++i; } } } @@ -340,10 +341,11 @@ abstract contract L2Governor is Context, ERC165, EIP712, IGovernor, IERC721Recei bytes32 /*descriptionHash*/ ) internal virtual { if (_executor() != address(this)) { - for (uint256 i = 0; i < targets.length; ++i) { + for (uint256 i = 0; i < targets.length;) { if (targets[i] == address(this)) { _governanceCall.pushBack(keccak256(calldatas[i])); } + unchecked{ ++i; } } } }
diff --git a/contracts/contracts/Gauge.sol b/contracts/contracts/Gauge.sol index 64ee49d..df08f78 100644 --- a/contracts/contracts/Gauge.sol +++ b/contracts/contracts/Gauge.sol @@ -350,7 +350,8 @@ contract Gauge { IVoter(voter).distribute(address(this)); _unlocked = 2; - for (uint i = 0; i < tokens.length; i++) { + uint256 length = tokens.length; + for (uint i = 0; i < length; i++) { (rewardPerTokenStored[tokens[i]], lastUpdateTime[tokens[i]]) = _updateRewardPerToken(tokens[i]); uint _reward = earned(tokens[i], account); diff --git a/contracts/contracts/Minter.sol b/contracts/contracts/Minter.sol index 9821ea4..a580107 100644 --- a/contracts/contracts/Minter.sol +++ b/contracts/contracts/Minter.sol @@ -54,7 +54,8 @@ contract Minter { require(initializer == msg.sender); _velo.mint(address(this), max); _velo.approve(address(_ve), type(uint).max); - for (uint i = 0; i < claimants.length; i++) { + uint256 length = claimants.length; + for (uint i = 0; i < length; i++) { _ve.create_lock_for(amounts[i], LOCK, claimants[i]); } initializer = address(0); diff --git a/contracts/contracts/Router.sol b/contracts/contracts/Router.sol index 6ef3e70..a68b9a1 100644 --- a/contracts/contracts/Router.sol +++ b/contracts/contracts/Router.sol @@ -84,10 +84,11 @@ contract Router { // performs chained getAmountOut calculations on any number of pairs function getAmountsOut(uint amountIn, route[] memory routes) public view returns (uint[] memory amounts) { - require(routes.length >= 1, 'Router: INVALID_PATH'); - amounts = new uint[](routes.length+1); + uint256 length = routes.length; + require(length >= 1, 'Router: INVALID_PATH'); + amounts = new uint[](length+1); amounts[0] = amountIn; - for (uint i = 0; i < routes.length; i++) { + for (uint i = 0; i < length; i++) { address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable); if (IPairFactory(factory).isPair(pair)) { amounts[i+1] = IPair(pair).getAmountOut(amounts[i], routes[i].from); @@ -313,11 +314,12 @@ contract Router { // **** SWAP **** // requires the initial amount to have already been sent to the first pair function _swap(uint[] memory amounts, route[] memory routes, address _to) internal virtual { - for (uint i = 0; i < routes.length; i++) { + uint256 length = routes.length; + for (uint i = 0; i < length; i++) { (address token0,) = sortTokens(routes[i].from, routes[i].to); uint amountOut = amounts[i + 1]; (uint amount0Out, uint amount1Out) = routes[i].from == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); - address to = i < routes.length - 1 ? pairFor(routes[i+1].from, routes[i+1].to, routes[i+1].stable) : _to; + address to = i < length - 1 ? pairFor(routes[i+1].from, routes[i+1].to, routes[i+1].stable) : _to; IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap( amount0Out, amount1Out, to, new bytes(0) );
require
statement use != 0
instead of > 0
for uintsProof: While it may seem that > 0
is cheaper than !=
, this is only true without the optimizer enabled and outside a require
statement. If you enable the optimizer at 10k AND you’re in a require statement, this will save gas.
Source: https://twitter.com/gzeon/status/1485428085885640706
index 4e5bec6..68677ac 100644 --- a/contracts/contracts/Bribe.sol +++ b/contracts/contracts/Bribe.sol @@ -39,7 +39,7 @@ contract Bribe is IBribe { } function notifyRewardAmount(address token, uint amount) external lock { - require(amount > 0); + require(amount != 0); if (!isReward[token]) { require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens"); } @@ -83,21 +83,21 @@ contract Bribe is IBribe { function deliverReward(address token, uint epochStart) external lock returns (uint) { require(msg.sender == gauge); uint rewardPerEpoch = tokenRewardsPerEpoch[token][epochStart]; - if (rewardPerEpoch > 0) { + if (rewardPerEpoch != 0) { _safeTransfer(token, address(gauge), rewardPerEpoch); } return rewardPerEpoch; } function _safeTransfer(address token, address to, uint256 value) internal { - require(token.code.length > 0); + require(token.code.length != 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); } function _safeTransferFrom(address token, address from, address to, uint256 value) internal { - require(token.code.length > 0); + require(token.code.length != 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); diff --git a/contracts/contracts/Gauge.sol b/contracts/contracts/Gauge.sol index 64ee49d..19a9efe 100644 --- a/contracts/contracts/Gauge.sol +++ b/contracts/contracts/Gauge.sol @@ -509,7 +509,7 @@ contract Gauge { } function deposit(uint amount, uint tokenId) public lock { - require(amount > 0); + require(amount != 0); _updateRewardForAllTokens(); @@ -589,7 +589,7 @@ contract Gauge { function notifyRewardAmount(address token, uint amount) external lock { require(token != stake); - require(amount > 0); + require(amount != 0); if (!isReward[token]) { require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens"); } @@ -610,7 +610,7 @@ contract Gauge { _safeTransferFrom(token, msg.sender, address(this), amount); rewardRate[token] = (amount + _left) / DURATION; } - require(rewardRate[token] > 0); + require(rewardRate[token] != 0); uint balance = IERC20(token).balanceOf(address(this)); require(rewardRate[token] <= balance / DURATION, "Provided reward too high"); periodFinish[token] = adjustedTstamp + DURATION; @@ -662,21 +662,21 @@ contract Gauge { } function _safeTransfer(address token, address to, uint256 value) internal { - require(token.code.length > 0); + require(token.code.length != 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); } function _safeTransferFrom(address token, address from, address to, uint256 value) internal { - require(token.code.length > 0); + require(token.code.length != 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); } function _safeApprove(address token, address spender, uint256 value) internal { - require(token.code.length > 0); + require(token.code.length != 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, spender, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); diff --git a/contracts/contracts/Pair.sol b/contracts/contracts/Pair.sol index b42154f..79a12de 100644 --- a/contracts/contracts/Pair.sol +++ b/contracts/contracts/Pair.sol @@ -300,7 +300,7 @@ contract Pair { } else { liquidity = Math.min(_amount0 * _totalSupply / _reserve0, _amount1 * _totalSupply / _reserve1); } - require(liquidity > 0, 'ILM'); // Pair: INSUFFICIENT_LIQUIDITY_MINTED + require(liquidity != 0, 'ILM'); // Pair: INSUFFICIENT_LIQUIDITY_MINTED _mint(to, liquidity); _update(_balance0, _balance1, _reserve0, _reserve1); @@ -319,7 +319,7 @@ contract Pair { uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee amount0 = _liquidity * _balance0 / _totalSupply; // using balances ensures pro-rata distribution amount1 = _liquidity * _balance1 / _totalSupply; // using balances ensures pro-rata distribution - require(amount0 > 0 && amount1 > 0, 'ILB'); // Pair: INSUFFICIENT_LIQUIDITY_BURNED + require(amount0 != 0 && amount1 != 0, 'ILB'); // Pair: INSUFFICIENT_LIQUIDITY_BURNED _burn(address(this), _liquidity); _safeTransfer(_token0, to, amount0); _safeTransfer(_token1, to, amount1); @@ -333,7 +333,7 @@ contract Pair { // this low-level function should be called from a contract which performs important safety checks function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock { require(!PairFactory(factory).isPaused()); - require(amount0Out > 0 || amount1Out > 0, 'IOA'); // Pair: INSUFFICIENT_OUTPUT_AMOUNT + require(amount0Out != 0 || amount1Out != 0, 'IOA'); // Pair: INSUFFICIENT_OUTPUT_AMOUNT (uint _reserve0, uint _reserve1) = (reserve0, reserve1); require(amount0Out < _reserve0 && amount1Out < _reserve1, 'IL'); // Pair: INSUFFICIENT_LIQUIDITY @@ -350,7 +350,7 @@ contract Pair { } uint amount0In = _balance0 > _reserve0 - amount0Out ? _balance0 - (_reserve0 - amount0Out) : 0; uint amount1In = _balance1 > _reserve1 - amount1Out ? _balance1 - (_reserve1 - amount1Out) : 0; - require(amount0In > 0 || amount1In > 0, 'IIA'); // Pair: INSUFFICIENT_INPUT_AMOUNT + require(amount0In != 0 || amount1In != 0, 'IIA'); // Pair: INSUFFICIENT_INPUT_AMOUNT { // scope for reserve{0,1}Adjusted, avoids stack too deep errors (address _token0, address _token1) = (token0, token1); if (amount0In > 0) _update0(amount0In * PairFactory(factory).getFee(stable) / 10000); // accrue fees for token0 and move them out of pool diff --git a/contracts/contracts/Router.sol b/contracts/contracts/Router.sol index 6ef3e70..65cb510 100644 --- a/contracts/contracts/Router.sol +++ b/contracts/contracts/Router.sol @@ -55,8 +55,8 @@ contract Router { // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset function quoteLiquidity(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) { - require(amountA > 0, 'Router: INSUFFICIENT_AMOUNT'); - require(reserveA > 0 && reserveB > 0, 'Router: INSUFFICIENT_LIQUIDITY'); + require(amountA != 0, 'Router: INSUFFICIENT_AMOUNT'); + require(reserveA > 0 && reserveB != 0, 'Router: INSUFFICIENT_LIQUIDITY'); amountB = amountA * reserveB / reserveA; } diff --git a/contracts/contracts/VotingEscrow.sol b/contracts/contracts/VotingEscrow.sol index b83e9c7..1212a3a 100644 --- a/contracts/contracts/VotingEscrow.sol +++ b/contracts/contracts/VotingEscrow.sol @@ -770,7 +770,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { LockedBalance memory _locked = locked[_tokenId]; require(_value > 0); // dev: need non-zero value - require(_locked.amount > 0, 'No existing lock found'); + require(_locked.amount != 0, 'No existing lock found'); require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw'); _deposit_for(_tokenId, _value, 0, _locked, DepositType.DEPOSIT_FOR_TYPE); } @@ -817,7 +817,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { LockedBalance memory _locked = locked[_tokenId]; assert(_value > 0); // dev: need non-zero value - require(_locked.amount > 0, 'No existing lock found'); + require(_locked.amount != 0, 'No existing lock found'); require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw'); _deposit_for(_tokenId, _value, 0, _locked, DepositType.INCREASE_LOCK_AMOUNT); @@ -832,7 +832,7 @@ contract VotingEscrow is IERC721, IERC721Metadata, IVotes { uint unlock_time = (block.timestamp + _lock_duration) / WEEK * WEEK; // Locktime is rounded down to weeks require(_locked.end > block.timestamp, 'Lock expired'); - require(_locked.amount > 0, 'Nothing is locked'); + require(_locked.amount != 0, 'Nothing is locked'); require(unlock_time > _locked.end, 'Can only increase lock duration'); require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max');
Gauge.sol
--- a/contracts/contracts/Gauge.sol +++ b/contracts/contracts/Gauge.sol @@ -101,19 +101,19 @@ contract Gauge { factory = msg.sender; - IBribe(bribe).setGauge(address(this)); - address _token = IVotingEscrow(_ve).token(); - IBribe(bribe).addRewardToken(_token); + IBribe(_bribe).setGauge(address(this)); + address _token = IVotingEscrow(__ve).token(); + IBribe(_bribe).addRewardToken(_token); isReward[_token] = true; rewards.push(_token); isForPair = _isForPair; if (isForPair) { - (address _token0, address _token1) = IPair(stake).tokens(); - IBribe(bribe).addRewardToken(_token0); + (address _token0, address _token1) = IPair(_stake).tokens(); + IBribe(_bribe).addRewardToken(_token0); isReward[_token0] = true; rewards.push(_token0); - IBribe(bribe).addRewardToken(_token1); + IBribe(_bribe).addRewardToken(_token1); isReward[_token1] = true; rewards.push(_token1); } ``` #### On function _claimFees ```diff --- a/contracts/contracts/Gauge.sol +++ b/contracts/contracts/Gauge.sol @@ -136,22 +136,26 @@ contract Gauge { if (!isForPair) { return (0, 0); } - (claimed0, claimed1) = IPair(stake).claimFees(); + + address _stake = stake; + address _bribe = bribe; + + (claimed0, claimed1) = IPair(_stake).claimFees(); if (claimed0 > 0 || claimed1 > 0) { uint _fees0 = fees0 + claimed0; uint _fees1 = fees1 + claimed1; - (address _token0, address _token1) = IPair(stake).tokens(); + (address _token0, address _token1) = IPair(_stake).tokens(); if (_fees0 / DURATION > 0) { fees0 = 0; - _safeApprove(_token0, bribe, _fees0); - IBribe(bribe).notifyRewardAmount(_token0, _fees0); + _safeApprove(_token0, _bribe, _fees0); + IBribe(_bribe).notifyRewardAmount(_token0, _fees0); } else { fees0 = _fees0; } if (_fees1 / DURATION > 0) { fees1 = 0; - _safeApprove(_token1, bribe, _fees1); - IBribe(bribe).notifyRewardAmount(_token1, _fees1); + _safeApprove(_token1, _bribe, _fees1); + IBribe(_bribe).notifyRewardAmount(_token1, _fees1); } else { fees1 = _fees1; }
earned
--- a/contracts/contracts/Gauge.sol +++ b/contracts/contracts/Gauge.sol @@ -480,10 +480,12 @@ contract Gauge { uint reward = 0; + mapping (uint => Checkpoint) storage _checkpoints = checkpoints[account]; + if (_endIndex > 0) { for (uint i = _startIndex; i < _endIndex; i++) { - Checkpoint memory cp0 = checkpoints[account][i]; - Checkpoint memory cp1 = checkpoints[account][i+1]; + Checkpoint memory cp0 = _checkpoints[i]; + Checkpoint memory cp1 = _checkpoints[i+1]; (uint _rewardPerTokenStored0,) = getPriorRewardPerToken(token, cp0.timestamp); (uint _rewardPerTokenStored1,) = getPriorRewardPerToken(token, cp1.timestamp); if (cp0.voted) { @@ -492,7 +494,7 @@ contract Gauge { } } - Checkpoint memory cp = checkpoints[account][_endIndex]; + Checkpoint memory cp = _checkpoints[_endIndex]; uint lastCpWeeksVoteEnd = cp.timestamp - (cp.timestamp % (7 days)) + BRIBE_LAG + DURATION; if (block.timestamp > lastCpWeeksVoteEnd) { (uint _rewardPerTokenStored,) = getPriorRewardPerToken(token, cp.timestamp);
getPriorBalanceIndex
--- a/contracts/contracts/Gauge.sol +++ b/contracts/contracts/Gauge.sol @@ -208,14 +208,15 @@ contract Gauge { if (nCheckpoints == 0) { return 0; } - + + mapping (uint => Checkpoint) storage _checkpoints = checkpoints[account]; // First check most recent balance - if (checkpoints[account][nCheckpoints - 1].timestamp <= timestamp) { + if (_checkpoints[nCheckpoints - 1].timestamp <= timestamp) { return (nCheckpoints - 1); } // Next check implicit zero balance - if (checkpoints[account][0].timestamp > timestamp) { + if (_checkpoints[0].timestamp > timestamp) { return 0; } @@ -223,7 +224,7 @@ contract Gauge { uint upper = nCheckpoints - 1; while (upper > lower) { uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow - Checkpoint memory cp = checkpoints[account][center]; + Checkpoint memory cp = _checkpoints[center]; if (cp.timestamp == timestamp) { return center; } else if (cp.timestamp < timestamp) {
--- a/contracts/contracts/Minter.sol +++ b/contracts/contracts/Minter.sol @@ -52,8 +52,10 @@ contract Minter { uint max // sum amounts / max = % ownership of top protocols, so if initial 20m is distributed, and target is 25% protocol ownership, then max - 4 x 20m = 80m ) external { require(initializer == msg.sender); - _velo.mint(address(this), max); - _velo.approve(address(_ve), type(uint).max); + IVelo __velo = _velo; + IVotingEscrow __ve = _ve; + __velo.mint(address(this), max); + __velo.approve(address(_ve), type(uint).max); for (uint i = 0; i < claimants.length; i++) { _ve.create_lock_for(amounts[i], LOCK, claimants[i]); }
CREATE2
to clone contracts instead of new
On factories you could use create2
tp deploy contracts, lines:
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/factories/BribeFactory.sol#L10
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/factories/GaugeFactory.sol#L24
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/factories/GaugeFactory.sol#L30
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/factories/PairFactory.sol#L95
source: https://docs.openzeppelin.com/contracts/4.x/api/utils#Create2-deploy-uint256-bytes32-bytes- https://blog.openzeppelin.com/getting-the-most-out-of-create2/
Use Custom Errors instead of Revert Strings to save Gas Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");
), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Source: https://blog.soliditylang.org/2021/04/21/custom-errors/
These contracts are on solidity 0.8.13 and you should use custom errors:
Bribe.sol
, Gaguge.sol
, Minter.sol
, Pair.sol
, PairFees.sol
, RewardsDistributor.sol
, Router.sol
, Velo.sol
, VeloGobernor.sol
, Voter.sol
, VotingEscrow.sol
, BribeFactory.sol
, GaugeFactory.sol
and PairFactory.sol
Lines to change for a custom error: https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Bribe.sol#L31 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Bribe.sol#L44 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Bribe.sol#L69 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Gauge.sol#L594 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Gauge.sol#L615 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Gauge.sol#L627 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Gauge.sol#L635 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Gauge.sol#L642
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Minter.sol#L65 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Minter.sol#L70 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Minter.sol#L75 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Minter.sol#L76
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Pair.sol#L361 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Pair.sol#L467 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Pair.sol#L485
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L40 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L42 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L58 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L59 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L87 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L177 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L182 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L249 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L250 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L341 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L356 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L369 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L371 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L382 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L384 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L406 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Router.sol#L410
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VeloGovernor.sol#L40 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VeloGovernor.sol#L45 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VeloGovernor.sol#L46
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Voter.sol#L192 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Voter.sol#L194 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Voter.sol#L211 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Voter.sol#L212 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Voter.sol#L218 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/Voter.sol#L219
https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L163 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L307 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L518 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L773 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L774 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L786 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L787 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L820 https://github.com/code-423n4/2022-05-velodrome/blob/main/contracts/contracts/VotingEscrow.sol#L163
#0 - pooltypes
2022-06-10T03:00:38Z
Duplicate of #131
#1 - GalloDaSballo
2022-06-29T23:39:07Z
The while loop is the same, in lack of math, I'll give you 80 gas for the unchecked, please provide gas-golfing proving the gas savings in your reports
Same as above, please provide gas math 80
Will give 20 gas per unchecked or 5 gas for every i++ -> ++i
40 + 44 * 20 = 880 920
Saves 6 gas per instance 4 * 6 = 24
Saves 6 gas per instance, only when using Solidity <= 0.8.11, because we're on 0.8.13 the finding no longer saves gas
Saves 97 gas - 3 per instance 11 * 97 - 3 = 1064
I don't believe this will save gas, perhaps the pointer to storage but in lack of an explanation I won't give it points
Same for the other storage
pointer
Saves 97 - 3 per instance per var
97 * 2 - 3 * 2 = 188
Doesn't save gas
In lack of any gas golfing I can't give it points.
Overall the report has some merit, some of the findings can save a decent amount of gas. I believe the warden could have gone the extra step and added estimates for gas savings to make the report more actionable.
Additionally, linking the same refactoring 30+ times just makes the report longer without adding much value, i'd recommend trying to keep it short or if you're feeling generous just PR into the sponsor's repo
Total Gas Saved 2356