Velodrome Finance contest - IllIllI's results

A base layer AMM on Optimism, inspired by Solidly.

General Information

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

Velodrome Finance

Findings Distribution

Researcher Performance

Rank: 5/75

Findings: 5

Award: $5,064.40

🌟 Selected for report: 4

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: IllIllI

Also found by: kenzo

Labels

bug
3 (High Risk)
sponsor confirmed

Awards

2912.922 USDC - $2,912.92

External Links

Lines of code

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L517-L528

Vulnerability details

Impact

Users can get unlimited votes which leads to them:

  1. gaining control over governance
  2. getting undeserved rewards
  3. having their pools favored due to gauge values

Proof of Concept

_mint() calls _moveTokenDelegates() to set up delegation...

File: contracts/contracts/VotingEscrow.sol   #1

462       function _mint(address _to, uint _tokenId) internal returns (bool) {
463           // Throws if `_to` is zero address
464           assert(_to != address(0));
465           // TODO add delegates
466           // checkpoint for gov
467           _moveTokenDelegates(address(0), delegates(_to), _tokenId);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L462-L467

and _transferFrom() calls _moveTokenDelegates() to transfer delegates...

File: contracts/contracts/VotingEscrow.sol   #2

301       function _transferFrom(
302           address _from,
303           address _to,
304           uint _tokenId,
305           address _sender
306       ) internal {
307           require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");
308           // Check requirements
309           require(_isApprovedOrOwner(_sender, _tokenId));
310           // Clear approval. Throws if `_from` is not the current owner
311           _clearApproval(_from, _tokenId);
312           // Remove NFT. Throws if `_tokenId` is not a valid NFT
313           _removeTokenFrom(_from, _tokenId);
314           // TODO delegates
315           // auto re-delegate
316           _moveTokenDelegates(delegates(_from), delegates(_to), _tokenId);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L301-L316

but _burn() does not transfer them back to address(0)

File: contracts/contracts/VotingEscrow.sol   #3

517       function _burn(uint _tokenId) internal {
518           require(_isApprovedOrOwner(msg.sender, _tokenId), "caller is not owner nor approved");
519   
520           address owner = ownerOf(_tokenId);
521   
522           // Clear approval
523           approve(address(0), _tokenId);
524           // TODO add delegates
525           // Remove token
526           _removeTokenFrom(msg.sender, _tokenId);
527           emit Transfer(owner, address(0), _tokenId);
528       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L517-L528

A user can deposit a token, lock it, wait for the lock to expire, transfer the token to another address, and repeat. During each iteration, a new NFT is minted and checkpointed. Calls to getPastVotes() will show the wrong values, since it will think the account still holds the delegation of the burnt NFT. Bribes and gauges also look at the checkpoints and will also have the wrong information

Tools Used

Code inspection

Call _moveTokenDelegates(owner,address(0)) in _burn()

#0 - pooltypes

2022-06-10T02:44:12Z

Nice catch! We intended to fix this issue (see TODO), included in our mainnet deploy. Thanks for surfacing

#1 - GalloDaSballo

2022-06-26T23:37:25Z

The warden has shown an exploit that, leveraging the _moveTokenDelegates function, which is not present in burn can allow any attacker to inflate their votes.

The sponsor has confirmed and they indeed have mitigated the issue in their deployed code: https://optimistic.etherscan.io/address/0x9c7305eb78a432ced5C4D14Cac27E8Ed569A2e26#code#L873

Findings Information

🌟 Selected for report: MiloTruck

Also found by: 0x52, Dravee, IllIllI, MaratCerby, WatchPug, unforgiven

Labels

bug
duplicate
2 (Med Risk)

Awards

147.433 USDC - $147.43

External Links

Lines of code

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L720-L753

Vulnerability details

Some ERC20 tokens, such as USDT, allow for charging a fee any time transfer() or transferFrom() is called. If a contract does not allow for amounts to change after transfers, subsequent transfer operations based on the original amount will revert() due to the contract having an insufficient balance.

Note that even though a token may not currently charge a fee, a future upgrade to the token may institute one.

Impact

If there is only one user that has deposited a fee-on-transfer token, that user will be unable to withdraw their funds, because the amount available will be less than the amount stated during deposit, and therefore the token's transfer() call will revert during withdrawal. For more users, consider what happens if the token has a 10% fee-on-transfer fee - deposits will be underfunded by 10%, and the users trying to withdraw the last 10% of deposits/rewards will have their calls revert due to the contract not holding enough tokens. If a whale does a large withdrawal, the extra 10% that that whale gets will mean that many users will not be able to withdraw anything at all.

Proof of Concept

The _value input parameter is what's stored in _locked.amount, and this is stored before the actual transfer is done on line 748:

File: contracts/contracts/VotingEscrow.sol   #1

720       function _deposit_for(
721           uint _tokenId,
722           uint _value,
723           uint unlock_time,
724           LockedBalance memory locked_balance,
725           DepositType deposit_type
726       ) internal {
727           LockedBalance memory _locked = locked_balance;
728           uint supply_before = supply;
729   
730           supply = supply_before + _value;
731           LockedBalance memory old_locked;
732           (old_locked.amount, old_locked.end) = (_locked.amount, _locked.end);
733           // Adding to existing lock, or if a lock is expired - creating a new one
734           _locked.amount += int128(int256(_value));
735           if (unlock_time != 0) {
736               _locked.end = unlock_time;
737           }
738           locked[_tokenId] = _locked;
739   
740           // Possibilities:
741           // Both old_locked.end could be current or expired (>/< block.timestamp)
742           // value == 0 (extend lock) or value > 0 (add to lock or extend lock)
743           // _locked.end > block.timestamp (always)
744           _checkpoint(_tokenId, old_locked, _locked);
745   
746           address from = msg.sender;
747           if (_value != 0 && deposit_type != DepositType.MERGE_TYPE) {
748               assert(IERC20(token).transferFrom(from, address(this), _value));
749           }
750   
751           emit Deposit(from, _tokenId, _value, _locked.end, deposit_type, block.timestamp);
752           emit Supply(supply_before, supply_before + _value);
753       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L720-L753

During withdrawal, the amount previously stored (line 850) is transferred (line 861), which will be wrong for fee-on-transfer tokens:

File: contracts/contracts/VotingEscrow.sol   #2

850           uint value = uint(int256(_locked.amount));
851   
852           locked[_tokenId] = LockedBalance(0,0);
853           uint supply_before = supply;
854           supply = supply_before - value;
855   
856           // old_locked can have either expired <= timestamp or zero end
857           // _locked has only 0 end
858           // Both can have >= 0 amount
859           _checkpoint(_tokenId, _locked, LockedBalance(0,0));
860   
861           assert(IERC20(token).transfer(msg.sender, value));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L850-L861

Tools Used

Code inspection

Measure the contract balance before and after the call to transferFrom(), and use the difference between the two as the amount, rather than the amount stated

#0 - pooltypes

2022-06-13T18:38:56Z

Duplicate of #222

#1 - GalloDaSballo

2022-06-28T22:40:13Z

Dup of #222

Findings Information

🌟 Selected for report: IllIllI

Also found by: Ruhum, rotcivegaf

Labels

bug
2 (Med Risk)
sponsor disputed

Awards

524.326 USDC - $524.33

External Links

Lines of code

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L462-L472 https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L378-L406

Vulnerability details

Impact

veNFTs may be sent to contracts that cannot handle them, and therefore all rewards and voting power, as well as the underlying are locked forever

Proof of Concept

The original code had the following warning:

    * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
    * are aware of the ERC721 protocol to prevent tokens from being forever locked.

https://github.com/solidlyexchange/solidly/blob/eac1ef02ca8736138b25062c02646ea43d5bb6e4/contracts/ve.sol#L143-L144

The minting code, which creates the locks, does not do this check:

File: contracts/contracts/VotingEscrow.sol   #1

462       function _mint(address _to, uint _tokenId) internal returns (bool) {
463           // Throws if `_to` is zero address
464           assert(_to != address(0));
465           // TODO add delegates
466           // checkpoint for gov
467           _moveTokenDelegates(address(0), delegates(_to), _tokenId);
468           // Add NFT. Throws if `_tokenId` is owned by someone
469           _addTokenTo(_to, _tokenId);
470           emit Transfer(address(0), _to, _tokenId);
471           return true;
472       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L462-L472

Once a lock is already minted, if it's transfered to a contract, the code does attempt to check for the issue:

File: contracts/contracts/VotingEscrow.sol   #2

378       ///      If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if
379       ///      the return value is not `bytes4(keccak256("onERC721Received(address,address,uint,bytes)"))`.
380       /// @param _from The current owner of the NFT.
381       /// @param _to The new owner.
382       /// @param _tokenId The NFT to transfer.
383       /// @param _data Additional data with no specified format, sent in call to `_to`.
384       function safeTransferFrom(
385           address _from,
386           address _to,
387           uint _tokenId,
388           bytes memory _data
389       ) public {
390           _transferFrom(_from, _to, _tokenId, msg.sender);
391   
392           if (_isContract(_to)) {
393               // Throws if transfer destination is a contract which does not implement 'onERC721Received'
394               try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4) {} catch (
395                   bytes memory reason
396               ) {
397                   if (reason.length == 0) {
398                       revert('ERC721: transfer to non ERC721Receiver implementer');
399                   } else {
400                       assembly {
401                           revert(add(32, reason), mload(reason))
402                       }
403                   }
404               }
405           }
406       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L378-L406

While the transfer function does in fact execute onERC721Received, it doesn't actually do a check of the bytes4 variable - it only checks for a non-zero length. The ERC721 standard says that for the function call to be valid, it must return the bytes4 function selector, otherwise it's invalid. If a user of the escrow system uses a contract that is attempting to explicitly reject NFTs by returning zero in its onERC721Received() function, the VotingEscrow will interpret that response as success and will transfer the NFT, potentially locking it forever. If the lock is minted to a contract, no checks are done, and the NFT can be locked forever.

Tools Used

Code inspection

Call onERC721Received() in _mint() and ensure that the return value equals IERC721Receiver.onERC721Received.selector in both _mint() and safeTransferFrom()

#0 - GalloDaSballo

2022-06-28T22:47:07Z

While I would be surprised by any realistic scenario of a smart contract that would implement onERC721Received and not return the specified selector, I have to acknowledge that technically we can get multiple false positives, especially when dealing with contracts that have a fallback function .

That said, to me that was not sufficient to constitute a valid finding.

However, I went and checked the EIP-721 and the standard definition of safeTransferFrom, which can be verified here: https://eips.ethereum.org/EIPS/eip-721

<img width="930" alt="Screenshot 2022-06-29 at 00 45 38" src="https://user-images.githubusercontent.com/13383782/176316795-f88b77b3-d9ac-4aaa-8835-cb1e2bd17d55.png">

According to the standard the function must check for the return value, and for that reason I believe the finding to be valid.

While I would be surprised if this causes any issue in the real world, because the function is non-conformant to the standard and technically a loss can happen because of it, I believe Medium Severity to be appropriate

#1 - GalloDaSballo

2022-06-28T22:48:09Z

To confirm, I doubt any meaningful loss will ever happen.

However this finding is basically: "Incorrect implementation of onERC721Received" and it's a valid finding at that

Summary

Low Risk Issues

IssueInstances
1Math.max(<x>,0) used with int cast to uint4
2approveMax variable causes problems2
3Front-runable initializer1
4Incorrect comments1
5Only a billion checkpoints available1
6Unsafe use of transfer()/transferFrom() with IERC202
7require() should be used instead of assert()18
8_safeMint() should be used rather than _mint() wherever possible1
9Missing checks for address(0x0) when assigning values to address state variables33
10Open TODOs4

Total: 67 instances over 10 issues

Non-critical Issues

IssueInstances
1Use two-phase ownership transfers4
2Avoid the use of sensitive terms3
3Consider addings checks for signature malleability2
4Return values of approve() not checked2
5require()/revert() statements should have descriptive reason strings93
6public functions not called by the contract should be declared external instead9
7Non-assembly method available1
8constants should be defined rather than using magic numbers95
9Redundant cast4
10Numeric values having to do with time should use time units for readability7
11Large multiples of ten should use scientific notation (e.g. 1e6) rather than decimal literals (e.g. 1000000), for readability3
12Missing event for critical parameter change16
13Expressions for constant values such as a call to keccak256(), should use immutable rather than constant2
14Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)3
15Constant redefined elsewhere6
16Variable names that consist of all capital letters should be reserved for constant/immutable variables1
17Typos7
18File is missing NatSpec13
19NatSpec is incomplete13
20Event is missing indexed fields31
21Not using the named return variables anywhere in the function is confusing12

Total: 327 instances over 21 issues

Low Risk Issues

1. Math.max(<x>,0) used with int cast to uint

The code casts an int to a uint before passing it to Math.max(). It seems as though the Math.max() call is attempting to prevent values from being negative, but since the int is being cast to uint, the value will never be negative, and instead will overflow if either the multiplication involving the slope and timestamp is positive. I wasn't able to find a scenario where this is the case, but this seems very dangerous, and the Math.max() call is sending misleading signals, so I suggest moving it to inside the cast to uint

There are 4 instances of this issue:

File: contracts/contracts/RewardsDistributor.sol   #1

139:         return Math.max(uint(int256(pt.bias - pt.slope * (int128(int256(_timestamp - pt.ts))))), 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L139

File: contracts/contracts/RewardsDistributor.sol   #2

158:                 ve_supply[t] = Math.max(uint(int256(pt.bias - pt.slope * dt)), 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L158

File: contracts/contracts/RewardsDistributor.sol   #3

208:                 uint balance_of = Math.max(uint(int256(old_user_point.bias - dt * old_user_point.slope)), 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L208

File: contracts/contracts/RewardsDistributor.sol   #4

265:                 uint balance_of = Math.max(uint(int256(old_user_point.bias - dt * old_user_point.slope)), 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L265

2. approveMax variable causes problems

The approveMax variable controls whether the liquidity value is used, or the maximum is used. If the result of that check doesn't match what was provided during signature generation, the permit() call will fail, so in the best case the variable has no effect. In the worst case it leads to incorrect state handling

There are 2 instances of this issue:

File: contracts/contracts/Router.sol   #1

290              uint value = approveMax ? type(uint).max : liquidity;
291:             IPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L290-L291

File: contracts/contracts/Router.sol   #2

308          uint value = approveMax ? type(uint).max : liquidity;
309:         IPair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L308-L309

3. Front-runable initializer

There is nothing preventing another account from calling the initializer before the contract owner. In the best case, the owner is forced to waste gas and re-deploy. In the worst case, the owner does not notice that his/her call reverts, and everyone starts using a contract under the control of an attacker

There is 1 instance of this issue:

File: contracts/contracts/Bribe.sol   #1

30     function setGauge(address _gauge) external {
31       require(gauge == address(0), "gauge already set");
32       gauge = _gauge;
33:    }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L30-L33

4. Incorrect comments

The WEEK variable is used to calculate a specific timestamp. However, if there's a leap year, then the calculation will shift, and the timestamp will no longer be 00:00 UTC

There is 1 instance of this issue:

File: contracts/contracts/Minter.sol   #1

14:      uint internal constant WEEK = 86400 * 7; // allows minting once per week (reset every Thursday 00:00 UTC)

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L14

5. Only a billion checkpoints available

A user can only have a billion checkpoints which, if the user is a DAO, may cause issues down the line, especially if the last checkpoint involved delegating and can thereafter not be undone

There is 1 instance of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

535:     mapping(uint => Point[1000000000]) public user_point_history; // user -> Point[user_epoch]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L535

6. Unsafe use of transfer()/transferFrom() with IERC20

Some tokens do not implement the ERC20 standard properly but are still accepted by most code that accepts ERC20 tokens. For example Tether (USDT)'s transfer() and transferFrom() functions on L1 do not return booleans as the specification requires, and instead have no return value. When these sorts of tokens are cast to IERC20, their function signatures do not match and therefore the calls made, revert (see this link for a test case). Use OpenZeppelin’s SafeERC20's safeTransfer()/safeTransferFrom() instead

There are 2 instances of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

748:              assert(IERC20(token).transferFrom(from, address(this), _value));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L748

File: contracts/contracts/VotingEscrow.sol   #2

861:          assert(IERC20(token).transfer(msg.sender, value));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L861

7. require() should be used instead of assert()

Prior to solidity version 0.8.0, hitting an assert consumes the remainder of the transaction's available gas rather than returning it, as require()/revert() do. assert() should be avoided even past solidity version 0.8.0 as its documentation states that "The assert function creates an error of type Panic(uint256). ... Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix".

There are 18 instances of this issue:

File: contracts/contracts/Router.sol

36:           assert(msg.sender == address(weth)); // only accept ETH via fallback from the WETH contract

181:                  assert(amountAOptimal <= amountADesired);

227:          assert(weth.transfer(pair, amountETH));

373:          assert(weth.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L36

File: contracts/contracts/VotingEscrow.sol

262:          assert(_operator != msg.sender);

272:          assert(idToOwner[_tokenId] == _owner);

447:          assert(idToOwner[_tokenId] == address(0));

464:          assert(_to != address(0));

508:          assert(idToOwner[_tokenId] == _from);

748:              assert(IERC20(token).transferFrom(from, address(this), _value));

815:          assert(_isApprovedOrOwner(msg.sender, _tokenId));

819:          assert(_value > 0); // dev: need non-zero value

829:          assert(_isApprovedOrOwner(msg.sender, _tokenId));

845:          assert(_isApprovedOrOwner(msg.sender, _tokenId));

861:          assert(IERC20(token).transfer(msg.sender, value));

937:          assert(_block <= block.number);

991:          assert(_block <= block.number);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L262

File: contracts/contracts/RewardsDistributor.sol

98:           assert(msg.sender == depositor);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L98

8. _safeMint() should be used rather than _mint() wherever possible

_mint() is discouraged in favor of _safeMint() which ensures that the recipient is either an EOA or implements IERC721Receiver. Both OpenZeppelin and solmate have versions of this function

There is 1 instance of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

791:          _mint(_to, _tokenId);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L791

9. Missing checks for address(0x0) when assigning values to address state variables

There are 33 instances of this issue:

File: contracts/contracts/Minter.sol

66:           pendingTeam = _team;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L66

File: contracts/contracts/redeem/RedemptionSender.sol

22:           weve = _weve;

24:           endpoint = _endpoint;

25:           optimismReceiver = _optimismReceiver;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L22

File: contracts/contracts/redeem/RedemptionReceiver.sol

28:           endpoint = _endpoint;

53:           fantomSender = _fantomSender;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L28

File: contracts/contracts/Router.sol

30:           factory = _factory;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L30

File: contracts/contracts/Gauge.sol

97:           stake = _stake;

98:           bribe = _bribe;

99:           _ve = __ve;

100:          voter = _voter;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L97

File: contracts/contracts/VotingEscrow.sol

88:           token = token_addr;

1061:         voter = _voter;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L88

File: contracts/contracts/VeloGovernor.sol

41:           team = newTeam;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L41

File: contracts/contracts/RewardsDistributor.sol

54:           token = _token;

55:           voting_escrow = _voting_escrow;

320:          depositor = _depositor;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L54

File: contracts/contracts/PairFees.sol

15:           token0 = _token0;

16:           token1 = _token1;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/PairFees.sol#L15

File: contracts/contracts/Velo.sol

28:           minter = _minter;

33:           redemptionReceiver = _receiver;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L28

File: contracts/contracts/Bribe.sol

32:       gauge = _gauge;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L32

File: contracts/contracts/factories/GaugeFactory.sol

14:           pairFactory = _pairFactory;

19:           team = _team;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/GaugeFactory.sol#L14

File: contracts/contracts/factories/PairFactory.sol

42:           pendingPauser = _pauser;

57:           pendingFeeManager = _feeManager;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L42

File: contracts/contracts/Voter.sol

55:           _ve = __ve;

56:           factory = _factory;

58:           gaugefactory = _gauges;

59:           bribefactory = _bribes;

79:           minter = _minter;

84:           governor = _governor;

89:           emergencyCouncil = _council;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L55

10. Open TODOs

Code architecture, incentives, and error handling/reporting questions/issues should be resolved before deployment

There are 4 instances of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

314:          // TODO delegates

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L314

File: contracts/contracts/VotingEscrow.sol   #2

465:          // TODO add delegates

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L465

File: contracts/contracts/VotingEscrow.sol   #3

524:          // TODO add delegates

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L524

File: contracts/contracts/VelodromeLibrary.sol   #4

9:        IRouter internal immutable router; // TODO make modifiable?

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol#L9

Non-critical Issues

1. Use two-phase ownership transfers

Consider adding a two-phase transfer, where the current owner nominates the next owner, and the next owner has to call accept*() to become the new owner. This prevents passing the ownership to an account that is unable to use it.

There are 4 instances of this issue:

File: contracts/contracts/factories/GaugeFactory.sol   #1

17       function setTeam(address _team) external {
18           require(msg.sender == team);
19           team = _team;
20:      }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/GaugeFactory.sol#L17-L20

File: contracts/contracts/VeloGovernor.sol   #2

39       function setTeam(address newTeam) external {
40           require(msg.sender == team, "not team");
41           team = newTeam;
42:      }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L39-L42

File: contracts/contracts/Voter.sol   #3

82       function setGovernor(address _governor) public {
83           require(msg.sender == governor);
84           governor = _governor;
85:      }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L82-L85

File: contracts/contracts/Voter.sol   #4

87       function setEmergencyCouncil(address _council) public {
88           require(msg.sender == emergencyCouncil);
89           emergencyCouncil = _council;
90:      }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L87-L90

2. Avoid the use of sensitive terms

Use alternative variants, e.g. allowlist/denylist instead of whitelist/blacklist

There are 3 instances of this issue:

File: contracts/contracts/Voter.sol   #1

38:      mapping(address => bool) public isWhitelisted;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L38

File: contracts/contracts/Voter.sol   #2

52:      event Whitelisted(address indexed whitelister, address indexed token);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L52

File: contracts/contracts/Voter.sol   #3

178:     function whitelist(address _token) public {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L178

3. Consider addings checks for signature malleability

Use OpenZeppelin's ECDSA contract rather than calling ecrecover() directly

There are 2 instances of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

1377:        address signatory = ecrecover(digest, v, r, s);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1377

File: contracts/contracts/Pair.sol   #2

484:         address recoveredAddress = ecrecover(digest, v, r, s);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L484

4. Return values of approve() not checked

Not all IERC20 implementations revert() when there's a failure in approve(). The function signature has a boolean return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually approving anything

There are 2 instances of this issue:

File: contracts/contracts/RewardsDistributor.sol   #1

57:           IERC20(_token).approve(_voting_escrow, type(uint).max);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L57

File: contracts/contracts/Voter.sol   #2

198:          IERC20(base).approve(_gauge, type(uint).max);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L198

5. require()/revert() statements should have descriptive reason strings

There are 93 instances of this issue:

File: contracts/contracts/Minter.sol

54:           require(initializer == msg.sender);

128:              require(_velo.transfer(team, _teamEmissions));

129:              require(_velo.transfer(address(_rewards_distributor), _growth));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L54

File: contracts/contracts/Router.sol

164:          require(amountADesired >= amountAMin);

165:          require(amountBDesired >= amountBMin);

245:          require(IPair(pair).transferFrom(msg.sender, pair, liquidity)); // send liquidity to pair

410:          require(token.code.length > 0);

413:          require(success && (data.length == 0 || abi.decode(data, (bool))));

417:          require(token.code.length > 0);

420:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L164

File: contracts/contracts/Gauge.sol

125:          require(_unlocked == 1);

174:          require(msg.sender == voter);

189:          require(msg.sender == voter);

348:          require(msg.sender == account || msg.sender == voter);

512:          require(amount > 0);

521:              require(IVotingEscrow(_ve).ownerOf(tokenId) == msg.sender);

526:              require(tokenIds[msg.sender] == tokenId);

564:              require(tokenId == tokenIds[msg.sender]);

591:          require(token != stake);

592:          require(amount > 0);

609:              require(amount > _left);

613:          require(rewardRate[token] > 0);

628:          require(rewards[i] == oldToken);

640:          require(msg.sender == bribe);

654:              require(amount > _left);

665:          require(token.code.length > 0);

668:          require(success && (data.length == 0 || abi.decode(data, (bool))));

672:          require(token.code.length > 0);

675:          require(success && (data.length == 0 || abi.decode(data, (bool))));

679:          require(token.code.length > 0);

682:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L125

File: contracts/contracts/VotingEscrow.sol

112:          require(_entered_state == _not_entered);

242:          require(owner != address(0));

244:          require(_approved != owner);

248:          require(senderIsOwner || senderIsApprovedForAll);

309:          require(_isApprovedOrOwner(_sender, _tokenId));

772:          require(_value > 0); // dev: need non-zero value

785:          require(_value > 0); // dev: need non-zero value

1060:         require(msg.sender == voter);

1065:         require(msg.sender == voter);

1070:         require(msg.sender == voter);

1075:         require(msg.sender == voter);

1080:         require(msg.sender == voter);

1086:         require(_from != _to);

1087:         require(_isApprovedOrOwner(msg.sender, _from));

1088:         require(_isApprovedOrOwner(msg.sender, _to));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L112

File: contracts/contracts/RewardsDistributor.sol

319:          require(msg.sender == depositor);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L319

File: contracts/contracts/PairFees.sol

20:           require(token.code.length > 0);

23:           require(success && (data.length == 0 || abi.decode(data, (bool))));

28:           require(msg.sender == pair);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/PairFees.sol#L20

File: contracts/contracts/Velo.sol

27:           require(msg.sender == minter);

32:           require(msg.sender == minter);

69:           require(msg.sender == minter);

75:           require(msg.sender == redemptionReceiver);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L27

File: contracts/contracts/Bribe.sol

24:         require(_unlocked == 1);

42:         require(amount > 0);

67:       require(msg.sender == gauge);

76:       require(msg.sender == gauge);

77:       require(rewards[i] == oldToken);

84:       require(msg.sender == gauge);

93:         require(token.code.length > 0);

96:         require(success && (data.length == 0 || abi.decode(data, (bool))));

100:        require(token.code.length > 0);

103:        require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L24

File: contracts/contracts/factories/GaugeFactory.sol

18:           require(msg.sender == team);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/GaugeFactory.sol#L18

File: contracts/contracts/factories/PairFactory.sol

41:           require(msg.sender == pauser);

46:           require(msg.sender == pendingPauser);

51:           require(msg.sender == pauser);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L41

File: contracts/contracts/Pair.sol

111:          require(_unlocked == 1);

335:          require(!PairFactory(factory).isPaused());

522:          require(token.code.length > 0);

525:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L111

File: contracts/contracts/Voter.sol

68:           require(_unlocked == 1);

75:           require(msg.sender == minter);

83:           require(msg.sender == governor);

88:           require(msg.sender == emergencyCouncil);

93:           require(IVotingEscrow(_ve).isApprovedOrOwner(msg.sender, _tokenId));

153:                  require(votes[_tokenId][_pool] == 0);

154:                  require(_poolWeight != 0);

173:          require(IVotingEscrow(_ve).isApprovedOrOwner(msg.sender, tokenId));

174:          require(_poolVote.length == _weights.length);

179:          require(msg.sender == governor);

184:          require(!isWhitelisted[_token]);

225:          require(isGauge[msg.sender]);

226:          require(isAlive[msg.sender]); // killed gauges cannot attach tokens to themselves

232:          require(isGauge[msg.sender]);

233:          require(isAlive[msg.sender]);

238:          require(isGauge[msg.sender]);

244:          require(isGauge[msg.sender]);

286:          require(isAlive[_gauge]); // killed gauges cannot be updated

316:          require(isAlive[_gauge]); // killed gauges cannot distribute

352:          require(token.code.length > 0);

355:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L68

6. public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public.

There are 9 instances of this issue:

File: contracts/contracts/redeem/RedemptionSender.sol

28        function redeemWEVE(
29            uint256 amount,
30            address zroPaymentAddress,
31:           bytes memory zroTransactionParams

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L28-L31

File: contracts/contracts/Gauge.sol

163:      function getVotingStage(uint timestamp) public pure returns (VotingStage) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L163

File: contracts/contracts/VotingEscrow.sol

1184      function getPastVotes(address account, uint timestamp)
1185          public
1186          view
1187:         returns (uint)

1349:     function delegate(address delegatee) public {

1354      function delegateBySig(
1355          address delegatee,
1356          uint nonce,
1357          uint expiry,
1358          uint8 v,
1359          bytes32 r,
1360:         bytes32 s

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1184-L1187

File: contracts/contracts/factories/PairFactory.sol

76:       function getFee(bool _stable) public view returns(uint256) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L76

File: contracts/contracts/Voter.sol

82:       function setGovernor(address _governor) public {

87:       function setEmergencyCouncil(address _council) public {

178:      function whitelist(address _token) public {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L82

7. Non-assembly method available

assembly{ id := chainid() } => uint256 id = block.chainid;, assembly { size := extcodesize() } => uint256 size = address().code.length

There is 1 instance of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

367:              size := extcodesize(account)

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L367

8. constants should be defined rather than using magic numbers

There are 95 instances of this issue:

File: contracts/contracts/Minter.sol

/// @audit 15000000e18
22:       uint public weekly = 15000000e18;

/// @audit 30
41:           teamRate = 30; // 30 bps = 0.03%

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L22

File: contracts/contracts/redeem/RedemptionSender.sol

/// @audit 11
21:           require(_optimismChainId == 11 || _optimismChainId == 10011, "CHAIN_ID_NOT_OP");

/// @audit 10011
21:           require(_optimismChainId == 11 || _optimismChainId == 10011, "CHAIN_ID_NOT_OP");

/// @audit 0x000000000000000000000000000000000000dEaD
37:                   0x000000000000000000000000000000000000dEaD,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L21

File: contracts/contracts/redeem/RedemptionReceiver.sol

/// @audit 12
24:           require(_fantomChainId == 12 || _fantomChainId == 10012, "CHAIN_ID_NOT_FTM");

/// @audit 10012
24:           require(_fantomChainId == 12 || _fantomChainId == 10012, "CHAIN_ID_NOT_FTM");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L24

File: contracts/contracts/Gauge.sol

/// @audit 7
164:          uint modTime = timestamp % (7 days);

/// @audit 7
176:          uint bribeStart = block.timestamp - (block.timestamp % (7 days)) + BRIBE_LAG;

/// @audit 7
496:          uint lastCpWeeksVoteEnd = cp.timestamp - (cp.timestamp % (7 days)) + BRIBE_LAG + DURATION;

/// @audit 7
597:          uint bribeStart = block.timestamp - (block.timestamp % (7 days)) + BRIBE_LAG;

/// @audit 7
598:          uint adjustedTstamp = block.timestamp < bribeStart ? bribeStart : bribeStart + 7 days;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L164

File: contracts/contracts/VotingEscrow.sol

/// @audit 48
143:              buffer[digits] = bytes1(uint8(48 + uint(value % 10)));

/// @audit 255
632:              for (uint i = 0; i < 255; ++i) {

/// @audit 128
886:          for (uint i = 0; i < 128; ++i) {

/// @audit 128
942:          for (uint i = 0; i < 128; ++i) {

/// @audit 255
1017:         for (uint i = 0; i < 255; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L143

File: contracts/contracts/VeloGovernor.sol

/// @audit 4
26:           L2GovernorVotesQuorumFraction(4) // 4%

/// @audit 15
32:           return 15 minutes; // 1 block

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L26

File: contracts/contracts/VelodromeLibrary.sol

/// @audit 1e18
16:           return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
16:           return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
16:           return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
16:           return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
16:           return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
16:           return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 3
20:           return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 1e18
20:           return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 1e18
20:           return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 1e18
20:           return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 1e18
20:           return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 255
24:           for (uint i = 0; i < 255; i++) {

/// @audit 1e18
28:                   uint dy = (xy - k)*1e18/_d(x0, y);

/// @audit 1e18
31:                   uint dy = (k - xy)*1e18/_d(x0, y);

/// @audit 1e18
50:           a = _getAmountOut(sample, tokenIn, r0, r1, t0, dec0, dec1, st) * 1e18 / sample;

/// @audit 1e18
51:           b = _getAmountOut(amountIn, tokenIn, r0, r1, t0, dec0, dec1, st) * 1e18 / amountIn;

/// @audit 1e18
57:           a = _getAmountOut(sample, tokenIn, r0, r1, t0, dec0, dec1, st) * 1e18 / sample;

/// @audit 1e18
58:           b = _getAmountOut(amountIn, tokenIn, r0, r1, t0, dec0, dec1, st) * 1e18 / amountIn;

/// @audit 1e18
64:           return _getAmountOut(sample, tokenIn, r0, r1, t0, dec0, dec1, st) * 1e18 / sample;

/// @audit 1e18
75:           return _getAmountOut(amountIn, tokenIn, r0, r1, t0, dec0, dec1, st) * 1e18 / amountIn;

/// @audit 1e18
81:               _reserve0 = _reserve0 * 1e18 / decimals0;

/// @audit 1e18
82:               _reserve1 = _reserve1 * 1e18 / decimals1;

/// @audit 1e18
84:               amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1;

/// @audit 1e18
84:               amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1;

/// @audit 1e18
86:               return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18;

/// @audit 1e18
95:               uint _x = x * 1e18 / decimals0;

/// @audit 1e18
96:               uint _y = y * 1e18 / decimals1;

/// @audit 1e18
97:               uint _a = (_x * _y) / 1e18;

/// @audit 1e18
98:               uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18);

/// @audit 1e18
98:               uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18);

/// @audit 1e18
99:               return _a * _b / 1e18;  // x3y+y3x >= k

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol#L16

File: contracts/contracts/RewardsDistributor.sol

/// @audit 20
75:           for (uint i = 0; i < 20; i++) {

/// @audit 128
105:          for (uint i = 0; i < 128; i++) {

/// @audit 128
121:          for (uint i = 0; i < 128; i++) {

/// @audit 20
148:          for (uint i = 0; i < 20; i++) {

/// @audit 50
195:          for (uint i = 0; i < 50; i++) {

/// @audit 50
252:          for (uint i = 0; i < 50; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L75

File: contracts/contracts/Velo.sol

/// @audit 0x0
45:           emit Transfer(address(0x0), _to, _amount);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L45

File: contracts/contracts/Bribe.sol

/// @audit 7
36:       uint bribeStart = timestamp - (timestamp % (7 days)) + BRIBE_LAG;

/// @audit 7
38:       return timestamp < bribeEnd ? bribeStart : bribeStart + 7 days;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L36

File: contracts/contracts/Pair.sol

/// @audit 1e18
153:          uint256 _ratio = amount * 1e18 / totalSupply; // 1e18 adjustment is removed during claim

/// @audit 1e18
163:          uint256 _ratio = amount * 1e18 / totalSupply;

/// @audit 1e18
184:                  uint _share = _supplied * _delta0 / 1e18; // add accrued difference for each supplied token

/// @audit 1e18
188:                  uint _share = _supplied * _delta1 / 1e18;

/// @audit 10000
356:          if (amount0In > 0) _update0(amount0In * PairFactory(factory).getFee(stable) / 10000); // accrue fees for token0 and move them out of pool

/// @audit 10000
357:          if (amount1In > 0) _update1(amount1In * PairFactory(factory).getFee(stable) / 10000); // accrue fees for token1 and move them out of pool

/// @audit 1e18
381:          return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
381:          return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
381:          return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
381:          return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
381:          return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 1e18
381:          return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;

/// @audit 3
385:          return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 1e18
385:          return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 1e18
385:          return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 1e18
385:          return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 1e18
385:          return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);

/// @audit 255
389:          for (uint i = 0; i < 255; i++) {

/// @audit 1e18
393:                  uint dy = (xy - k)*1e18/_d(x0, y);

/// @audit 1e18
396:                  uint dy = (k - xy)*1e18/_d(x0, y);

/// @audit 10000
414:          amountIn -= amountIn * PairFactory(factory).getFee(stable) / 10000; // remove fee from amount received

/// @audit 1e18
421:              _reserve0 = _reserve0 * 1e18 / decimals0;

/// @audit 1e18
422:              _reserve1 = _reserve1 * 1e18 / decimals1;

/// @audit 1e18
424:              amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1;

/// @audit 1e18
424:              amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1;

/// @audit 1e18
426:              return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18;

/// @audit 1e18
435:              uint _x = x * 1e18 / decimals0;

/// @audit 1e18
436:              uint _y = y * 1e18 / decimals1;

/// @audit 1e18
437:              uint _a = (_x * _y) / 1e18;

/// @audit 1e18
438:              uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18);

/// @audit 1e18
438:              uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18);

/// @audit 1e18
439:              return _a * _b / 1e18;  // x3y+y3x >= k

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L153

File: contracts/contracts/Voter.sol

/// @audit 0x0
190:          require(gauges[_pool] == address(0x0), "exists");

/// @audit 1e18
258:          uint256 _ratio = amount * 1e18 / totalWeight; // 1e18 adjustment is removed during claim

/// @audit 1e18
295:                  uint _share = uint(_supplied) * _delta / 1e18; // add accrued difference for each supplied token

/// @audit 7
317:          uint dayCalc = block.timestamp % (7 days);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L190

9. Redundant cast

The type of the variable is the same as the type to which the variable is being cast

There are 4 instances of this issue:

File: contracts/contracts/Bribe.sol   #1

/// @audit address(gauge)
87:         _safeTransfer(token, address(gauge), rewardPerEpoch);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L87

File: contracts/contracts/Voter.sol   #2

/// @audit uint256(_totalWeight)
118:          totalWeight -= uint256(_totalWeight);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L118

File: contracts/contracts/Voter.sol   #3

/// @audit uint256(_totalWeight)
168:          totalWeight += uint256(_totalWeight);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L168

File: contracts/contracts/Voter.sol   #4

/// @audit uint256(_usedWeight)
169:          usedWeights[_tokenId] = uint256(_usedWeight);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L169

10. Numeric values having to do with time should use time units for readability

There are units for seconds, minutes, hours, days, and weeks

There are 7 instances of this issue:

File: contracts/contracts/Minter.sol

/// @audit 86400
14:       uint internal constant WEEK = 86400 * 7; // allows minting once per week (reset every Thursday 00:00 UTC)

/// @audit 15000000e18
22:       uint public weekly = 15000000e18;

/// @audit 86400
24:       uint internal constant LOCK = 86400 * 7 * 52 * 4;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L14

File: contracts/contracts/VotingEscrow.sol

/// @audit 86400
542:      uint internal constant MAXTIME = 4 * 365 * 86400;

/// @audit 86400
543:      int128 internal constant iMAXTIME = 4 * 365 * 86400;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L542

File: contracts/contracts/RewardsDistributor.sol

/// @audit 86400
30:       uint constant WEEK = 7 * 86400;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L30

File: contracts/contracts/Pair.sol

/// @audit 1800
45:       uint constant periodSize = 1800;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L45

11. Large multiples of ten should use scientific notation (e.g. 1e6) rather than decimal literals (e.g. 1000000), for readability

There are 3 instances of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

535:      mapping(uint => Point[1000000000]) public user_point_history; // user -> Point[user_epoch]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L535

File: contracts/contracts/RewardsDistributor.sol   #2

38:       uint[1000000000000000] public tokens_per_week;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L38

File: contracts/contracts/RewardsDistributor.sol   #3

44:       uint[1000000000000000] public ve_supply;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L44

12. Missing event for critical parameter change

There are 16 instances of this issue:

File: contracts/contracts/Minter.sol

64        function setTeam(address _team) external {
65            require(msg.sender == team, "not team");
66            pendingTeam = _team;
67:       }

74        function setTeamRate(uint _teamRate) external {
75            require(msg.sender == team, "not team");
76            require(_teamRate <= MAX_TEAM_RATE, "rate too high");
77            teamRate = _teamRate;
78:       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L64-L67

File: contracts/contracts/VotingEscrow.sol

1059      function setVoter(address _voter) external {
1060          require(msg.sender == voter);
1061          voter = _voter;
1062:     }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1059-L1062

File: contracts/contracts/VeloGovernor.sol

39        function setTeam(address newTeam) external {
40            require(msg.sender == team, "not team");
41            team = newTeam;
42:       }

44        function setProposalNumerator(uint256 numerator) external {
45            require(msg.sender == team, "not team");
46            require(numerator <= MAX_PROPOSAL_NUMERATOR, "numerator too high");
47            proposalNumerator = numerator;
48:       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L39-L42

File: contracts/contracts/RewardsDistributor.sol

318       function setDepositor(address _depositor) external {
319           require(msg.sender == depositor);
320           depositor = _depositor;
321:      }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L318-L321

File: contracts/contracts/Velo.sol

26        function setMinter(address _minter) external {
27            require(msg.sender == minter);
28            minter = _minter;
29:       }

31        function setRedemptionReceiver(address _receiver) external {
32            require(msg.sender == minter);
33            redemptionReceiver = _receiver;
34:       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L26-L29

File: contracts/contracts/Bribe.sol

30      function setGauge(address _gauge) external {
31        require(gauge == address(0), "gauge already set");
32        gauge = _gauge;
33:     }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L30-L33

File: contracts/contracts/factories/GaugeFactory.sol

17        function setTeam(address _team) external {
18            require(msg.sender == team);
19            team = _team;
20:       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/GaugeFactory.sol#L17-L20

File: contracts/contracts/factories/PairFactory.sol

40        function setPauser(address _pauser) external {
41            require(msg.sender == pauser);
42            pendingPauser = _pauser;
43:       }

50        function setPause(bool _state) external {
51            require(msg.sender == pauser);
52            isPaused = _state;
53:       }

55        function setFeeManager(address _feeManager) external {
56            require(msg.sender == feeManager, 'not fee manager');
57            pendingFeeManager = _feeManager;
58:       }

65        function setFee(bool _stable, uint256 _fee) external {
66            require(msg.sender == feeManager, 'not fee manager');
67            require(_fee <= MAX_FEE, 'fee too high');
68            require(_fee != 0, 'fee must be nonzero');
69            if (_stable) {
70                stableFee = _fee;
71            } else {
72                volatileFee = _fee;
73            }
74:       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L40-L43

File: contracts/contracts/Voter.sol

82        function setGovernor(address _governor) public {
83            require(msg.sender == governor);
84            governor = _governor;
85:       }

87        function setEmergencyCouncil(address _council) public {
88            require(msg.sender == emergencyCouncil);
89            emergencyCouncil = _council;
90:       }

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L82-L85

13. Expressions for constant values such as a call to keccak256(), should use immutable rather than constant

There are 2 instances of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

1106:     bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1106

File: contracts/contracts/VotingEscrow.sol   #2

1109:     bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1109

14. Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18)

There are 3 instances of this issue:

File: contracts/contracts/Router.sol   #1

21:       uint internal constant MINIMUM_LIQUIDITY = 10**3;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L21

File: contracts/contracts/Gauge.sol   #2

36:       uint internal constant PRECISION = 10 ** 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L36

File: contracts/contracts/Pair.sol   #3

30:       uint internal constant MINIMUM_LIQUIDITY = 10**3;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L30

15. Constant redefined elsewhere

Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant in a library. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.

There are 6 instances of this issue:

File: contracts/contracts/Gauge.sol

/// @audit seen in /var/tmp/hh/contracts/contracts/Minter.sol 
16:       address public immutable _ve; // the ve token used for gauges

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L16

File: contracts/contracts/Velo.sol

/// @audit seen in /var/tmp/hh/contracts/contracts/VotingEscrow.sol 
6:        string public constant name = "Velodrome";

/// @audit seen in /var/tmp/hh/contracts/contracts/VotingEscrow.sol 
7:        string public constant symbol = "VELO";

/// @audit seen in /var/tmp/hh/contracts/contracts/VotingEscrow.sol 
8:        uint8 public constant decimals = 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L6

File: contracts/contracts/Pair.sol

/// @audit seen in /var/tmp/hh/contracts/contracts/Velo.sol 
15:       uint8 public constant decimals = 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L15

File: contracts/contracts/Voter.sol

/// @audit seen in /var/tmp/hh/contracts/contracts/Gauge.sol 
16:       address public immutable _ve; // the ve token that governs these contracts

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L16

16. Variable names that consist of all capital letters should be reserved for constant/immutable variables

If the variable needs to be different based on which class it comes from, a view/pure function should be used instead (e.g. like this).

There is 1 instance of this issue:

File: contracts/contracts/Pair.sol   #1

25:       bytes32 internal DOMAIN_SEPARATOR;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L25

17. Typos

There are 7 instances of this issue:

File: contracts/contracts/redeem/RedemptionReceiver.sol

/// @audit FTM
14:       uint16 public immutable fantomChainId; // 12 for FTM, 10012 for FTM testnet

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L14

File: contracts/contracts/VotingEscrow.sol

/// @audit blocktimes
38:        * and per block could be fairly bad b/c Ethereum changes blocktimes.

/// @audit Exeute
295:      /// @dev Exeute transfer of a NFT.

/// @audit Pevious
575:      /// @param old_locked Pevious locked amount / end lock time for the user

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L38

File: contracts/contracts/PairFees.sol

/// @audit localy
10:       address internal immutable token0; // token0 of pair, saved localy and statically for gas optimization

/// @audit localy
11:       address internal immutable token1; // Token1 of pair, saved localy and statically for gas optimization

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/PairFees.sol#L10

File: contracts/contracts/Pair.sol

/// @audit obervations
37:       // Structure to capture time period obervations every 30 minutes, used for local oracles

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L37

18. File is missing NatSpec

There are 13 instances of this issue:

File: contracts/contracts/Minter.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol

File: contracts/contracts/Router.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol

File: contracts/contracts/VeloGovernor.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol

File: contracts/contracts/VelodromeLibrary.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol

File: contracts/contracts/RewardsDistributor.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol

File: contracts/contracts/PairFees.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/PairFees.sol

File: contracts/contracts/Velo.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol

File: contracts/contracts/Bribe.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol

File: contracts/contracts/factories/GaugeFactory.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/GaugeFactory.sol

File: contracts/contracts/factories/BribeFactory.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/BribeFactory.sol

File: contracts/contracts/factories/PairFactory.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol

File: contracts/contracts/Pair.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol

File: contracts/contracts/Voter.sol

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol

19. NatSpec is incomplete

There are 13 instances of this issue:

File: contracts/contracts/VotingEscrow.sol

/// @audit Missing: '@return'
160       /// @dev Returns current token URI metadata
161       /// @param _tokenId Token ID to fetch URI for.
162:      function tokenURI(uint _tokenId) external view returns (string memory) {

/// @audit Missing: '@return'
184       /// @dev Returns the address of the owner of the NFT.
185       /// @param _tokenId The identifier for an NFT.
186:      function ownerOf(uint _tokenId) public view returns (address) {

/// @audit Missing: '@return'
191       ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.
192       /// @param _owner Address for whom to query the balance.
193:      function _balance(address _owner) internal view returns (uint) {

/// @audit Missing: '@return'
198       ///      Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid.
199       /// @param _owner Address for whom to query the balance.
200:      function balanceOf(address _owner) external view returns (uint) {

/// @audit Missing: '@return'
216       /// @dev Get the approved address for a single NFT.
217       /// @param _tokenId ID of the NFT to query the approval of.
218:      function getApproved(uint _tokenId) external view returns (address) {

/// @audit Missing: '@return'
223       /// @param _owner The address that owns the NFTs.
224       /// @param _operator The address that acts on behalf of the owner.
225:      function isApprovedForAll(address _owner, address _operator) external view returns (bool) {

/// @audit Missing: '@return'
412       /// @dev Interface identification is specified in ERC-165.
413       /// @param _interfaceID Id of the interface
414:      function supportsInterface(bytes4 _interfaceID) external view returns (bool) {

/// @audit Missing: '@return'
780       /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
781       /// @param _to Address to deposit
782:      function _create_lock(uint _value, uint _lock_duration, address _to) internal returns (uint) {

/// @audit Missing: '@return'
798       /// @param _value Amount to deposit
799       /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
800:      function create_lock(uint _value, uint _lock_duration) external nonreentrant returns (uint) {

/// @audit Missing: '@return'
806       /// @param _lock_duration Number of seconds to lock tokens for (rounded down to nearest week)
807       /// @param _to Address to deposit
808:      function create_lock_for(uint _value, uint _lock_duration, address _to) external nonreentrant returns (uint) {

/// @audit Missing: '@param _tokenId'
812       /// @notice Deposit `_value` additional tokens for `_tokenId` without modifying the unlock time
813       /// @param _value Amount of tokens to deposit and add to the lock
814:      function increase_amount(uint _tokenId, uint _value) external nonreentrant {

/// @audit Missing: '@param _tokenId'
826       /// @notice Extend the unlock time for `_tokenId`
827       /// @param _lock_duration New number of seconds until tokens unlock
828:      function increase_unlock_time(uint _tokenId, uint _lock_duration) external nonreentrant {

/// @audit Missing: '@param t'
1043      /// @notice Calculate total voting power
1044      /// @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility
1045      /// @return Total voting power
1046:     function totalSupplyAtT(uint t) public view returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L160-L162

20. Event is missing indexed fields

Each event should use three indexed fields if there are three or more fields

There are 31 instances of this issue:

File: contracts/contracts/Minter.sol

32:       event Mint(address indexed sender, uint weekly, uint circulating_supply, uint circulating_emission);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L32

File: contracts/contracts/Gauge.sol

90:       event Deposit(address indexed from, uint tokenId, uint amount);

91:       event Withdraw(address indexed from, uint tokenId, uint amount);

92:       event NotifyReward(address indexed from, address indexed reward, uint amount);

93:       event ClaimFees(address indexed from, uint claimed0, uint claimed1);

94:       event ClaimRewards(address indexed from, address indexed reward, uint amount);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L90

File: contracts/contracts/VotingEscrow.sol

51        event Deposit(
52            address indexed provider,
53            uint tokenId,
54            uint value,
55            uint indexed locktime,
56            DepositType deposit_type,
57            uint ts
58:       );

59:       event Withdraw(address indexed provider, uint tokenId, uint value, uint ts);

60:       event Supply(uint prevSupply, uint supply);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L51-L58

File: contracts/contracts/RewardsDistributor.sol

18        event CheckpointToken(
19            uint time,
20            uint tokens
21:       );

23        event Claimed(
24            uint tokenId,
25            uint amount,
26            uint claim_epoch,
27            uint max_epoch
28:       );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L18-L21

File: contracts/contracts/Velo.sol

17:       event Transfer(address indexed from, address indexed to, uint value);

18:       event Approval(address indexed owner, address indexed spender, uint value);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L17

File: contracts/contracts/Bribe.sol

19:     event NotifyReward(address indexed from, address indexed reward, uint epoch, uint amount);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L19

File: contracts/contracts/factories/PairFactory.sol

26:       event PairCreated(address indexed token0, address indexed token1, bool stable, address pair, uint);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L26

File: contracts/contracts/Pair.sol

72:       event Fees(address indexed sender, uint amount0, uint amount1);

73:       event Mint(address indexed sender, uint amount0, uint amount1);

74:       event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);

75        event Swap(
76            address indexed sender,
77            uint amount0In,
78            uint amount1In,
79            uint amount0Out,
80            uint amount1Out,
81            address indexed to
82:       );

83:       event Sync(uint reserve0, uint reserve1);

84:       event Claim(address indexed sender, address indexed recipient, uint amount0, uint amount1);

86:       event Transfer(address indexed from, address indexed to, uint amount);

87:       event Approval(address indexed owner, address indexed spender, uint amount);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L72

File: contracts/contracts/Voter.sol

44:       event Voted(address indexed voter, uint tokenId, uint256 weight);

45:       event Abstained(uint tokenId, uint256 weight);

46:       event Deposit(address indexed lp, address indexed gauge, uint tokenId, uint amount);

47:       event Withdraw(address indexed lp, address indexed gauge, uint tokenId, uint amount);

48:       event NotifyReward(address indexed sender, address indexed reward, uint amount);

49:       event DistributeReward(address indexed sender, address indexed gauge, uint amount);

50:       event Attach(address indexed owner, address indexed gauge, uint tokenId);

51:       event Detach(address indexed owner, address indexed gauge, uint tokenId);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L44

21. Not using the named return variables anywhere in the function is confusing

Consider changing the variable to be an unnamed one

There are 12 instances of this issue:

File: contracts/contracts/Router.sol

/// @audit amount
71:       function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable) {

/// @audit stable
71:       function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L71

File: contracts/contracts/Gauge.sol

/// @audit claimed0
131:      function claimFees() external lock returns (uint claimed0, uint claimed1) {

/// @audit claimed1
131:      function claimFees() external lock returns (uint claimed0, uint claimed1) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L131

File: contracts/contracts/Pair.sol

/// @audit dec0
125:      function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) {

/// @audit dec1
125:      function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) {

/// @audit r0
125:      function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) {

/// @audit r1
125:      function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) {

/// @audit st
125:      function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) {

/// @audit t0
125:      function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) {

/// @audit t1
125:      function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) {

/// @audit amountOut
254:      function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L125

#0 - liveactionllama

2022-05-31T17:15:53Z

Warden created this issue as a placeholder, because their submission was too large for the contest form. They then emailed their md file to our team on 05/30/2022 at 11:12 UTC. I've updated this issue with their md file content.

#1 - GalloDaSballo

2022-07-04T00:43:23Z

1. Math.max(<x>,0) used with int cast to uint

Great find, Valid Low

2. approveMax variable causes problems

I believe NC is more appropriate, caller can call the wrong one

3. Front-runable initializer

Because of the architecture of the system this is a Valid and Low finding

4. Return values of approve() not checked

Because token imp is known, this finding is not valid

5. require()/revert() statements should have descriptive reason strings

Valid NC

6. public functions not called by the contract should be declared external instead

You cannot go from public to external (just tested), only from external to public, that said all of these are not override so while the comment is wrong, the advice of using external is valid

16. Variable names that consist of all capital letters should be reserved for constant/immutable variables

Valid NC

17. Typos

Valid NC

#2 - GalloDaSballo

2022-07-04T21:26:52Z

7. require() should be used instead of assert()

Valid Low

8. constants should be defined rather than using magic numbers

Valid Non-Critical

9. Missing checks for address(0x0) when assigning values to address state variables

Valid Low

10. Open TODOs

Valid NC

4. Incorrect comments

Wouldn't the timestamp shift by 24 hours every 4 years? It will always end at 00 just on a different day

5. Only a billion checkpoints available

Valid NC

1. Use two-phase ownership transfers

Valid NC

2. Avoid the use of sensitive terms

Valid NC

3. Consider addings checks for signature malleability

In lack of a POC I cannot give it points, with a POC this finding is potentially Med / High Severity

4. Return values of approve() not checked

Because the implementation is known the finding is not valid

## 5. require()/revert() statements should have descriptive reason strings Valid NC

6. public functions not called by the contract should be declared external instead

Valid NC

7. Non-assembly method available

Because the code compiles to the same result, there is no advantage, hence will not give it points

8. constants should be defined rather than using magic number

Valid NC

9. Redundant cast

Valid NC

10. Numeric values having to do with time should use time units for readability

I would save since the constant is declared it would be more appropriate to re-use it. Valid NC

11. Large multiples of ten should use scientific notation (e.g. 1e6) rather than decimal literals (e.g. 1000000), for readability | 3

Valid NC

12. Missing event for critical parameter change | 16

Valid NC

13. Expressions for constant values such as a call to keccak256(), should use immutable rather than constant | 2

Not anymore, debunked here

14. Use scientific notation (e.g. 1e18) rather than exponentiation (e.g. 10**18) | 3

Because it's for constants I don't think it makes a difference

15. Constant redefined elsewhere | 6

I don't think this suggestion is actionable

18. File is missing NatSpec | 13

Valid NC

19. NatSpec is incomplete | 13

Disagree with the finding, not all fields need natspec as it's optional

20. Event is missing indexed fields | 31

Valid NC, although most of these wouldn't be useful

21. Not using the named return variables anywhere in the function is confusing

Valid NC

#3 - GalloDaSballo

2022-07-04T21:28:02Z

All in all a really thorough report, which would benefit by mentioning a finding once, and then listing all other occurrences, especially when the code is pretty much the same

That said, one of the best reports

#4 - GalloDaSballo

2022-07-04T21:30:33Z

5 L, 21 NC

#5 - GalloDaSballo

2022-07-11T21:53:46Z

L-1. Math.max(,0) used with int cast to uint

L-2. Front-runable initializer

L-3. require() should be used instead of assert()

L-4. _safeMint() should be used rather than _mint() wherever possible

L-5. Missing checks for address(0x0) when assigning values to address state variables


NC-1. approveMax variable causes problems

NC-2. require()/revert() statements should have descriptive reason strings

NC-3. public functions not called by the contract should be declared external instead

NC-4. Variable names that consist of all capital letters should be reserved for constant/immutable variables

NC-5. Typos

NC-6. constants should be defined rather than using magic numbers

NC-7. Open TODOs

NC-8. Only a billion checkpoints available

NC-9. Use two-phase ownership transfers

NC-10. Avoid the use of sensitive terms

NC-11. require()/revert() statements should have descriptive reason strings

NC-12. public functions not called by the contract should be declared external instead

NC-13. constants should be defined rather than using magic number

NC-14. Redundant cast

NC-15. Numeric values having to do with time should use time units for readability

NC-16. Large multiples of ten should use scientific notation (e.g. 1e6) rather than decimal literals (e.g. 1000000), for readability

NC-17. Missing event for critical parameter change

NC-18. File is missing NatSpec

NC-20. Event is missing indexed fields

NC-21. Not using the named return variables anywhere in the function is confusing

Summary

Gas Optimizations

IssueInstances
1Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate8
2State variables only set in the constructor should be declared immutable5
3State variables can be packed into fewer storage slots1
4Using calldata instead of memory for read-only arguments in external functions saves gas13
5State variables should be cached in stack variables rather than re-reading them from storage39
6Multiple accesses of a mapping should use a local variable cache34
7<x> += <y> costs more gas than <x> = <x> + <y> for state variables21
8internal functions only called once can be inlined to save gas25
9<array>.length should not be looked up in every loop of a for-loop17
10++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops41
11require()/revert() strings longer than 32 bytes cost extra gas14
12keccak256() should only need to be called on a specific string literal once1
13Using bools for storage incurs overhead14
14Using > 0 costs more gas than != 0 when used on a uint in a require() statement6
15It costs more gas to initialize variables to zero than to let the default of zero be applied68
16++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)37
17Splitting require() statements that use && saves gas17
18Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead45
19abi.encode() is less efficient than abi.encodePacked()1
20Using private rather than public for constants, saves gas18
21Duplicated require()/revert() checks should be refactored to a modifier or function30
22Division by two should use bit shifting9
23Stack variable used as a cheaper cache for a state variable is only used once1
24require() or revert() statements that check input arguments should be at the top of the function13
25Use custom errors rather than revert()/require() strings to save deployment gas86

Total: 564 instances over 25 issues

Gas Optimizations

1. Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.

There are 8 instances of this issue:

File: contracts/contracts/Gauge.sol   #1

39        mapping(address => uint) public rewardRate;
40        mapping(address => uint) public periodFinish;
41        mapping(address => uint) public lastUpdateTime;
42        mapping(address => uint) public rewardPerTokenStored;
43    
44        mapping(address => mapping(address => uint)) public lastEarn;
45        mapping(address => mapping(address => uint)) public userRewardPerTokenStored;
46    
47:       mapping(address => uint) public tokenIds;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L39-L47

File: contracts/contracts/Gauge.sol   #2

75        mapping (address => mapping (uint => Checkpoint)) public checkpoints;
76        /// @notice The number of checkpoints for each account
77:       mapping (address => uint) public numCheckpoints;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L75-L77

File: contracts/contracts/Gauge.sol   #3

83        mapping (address => mapping (uint => RewardPerTokenCheckpoint)) public rewardPerTokenCheckpoints;
84        /// @notice The number of checkpoints for each token
85:       mapping (address => uint) public rewardPerTokenNumCheckpoints;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L83-L85

File: contracts/contracts/VotingEscrow.sol   #4

1116      mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;
1117  
1118      /// @notice The number of checkpoints for each account
1119      mapping(address => uint32) public numCheckpoints;
1120  
1121      /// @notice A record of states for signing / validating signatures
1122:     mapping(address => uint) public nonces;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1116-L1122

File: contracts/contracts/Pair.sol   #5

65        mapping(address => uint) public supplyIndex0;
66        mapping(address => uint) public supplyIndex1;
67    
68        // tracks the amount of unclaimed, but claimable tokens off of fees for token0 and token1
69        mapping(address => uint) public claimable0;
70:       mapping(address => uint) public claimable1;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L65-L70

File: contracts/contracts/Voter.sol   #6

30        mapping(address => address) public gauges; // pool => gauge
31        mapping(address => address) public poolForGauge; // gauge => pool
32        mapping(address => address) public bribes; // gauge => bribe
33:       mapping(address => uint256) public weights; // pool => weight

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L30-L33

File: contracts/contracts/Voter.sol   #7

37        mapping(address => bool) public isGauge;
38        mapping(address => bool) public isWhitelisted;
39:       mapping(address => bool) public isAlive;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L37-L39

File: contracts/contracts/Voter.sol   #8

253       mapping(address => uint) internal supplyIndex;
254:      mapping(address => uint) public claimable;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L253-L254

2. State variables only set in the constructor should be declared immutable

Avoids a Gsset (20000 gas) in the constructor, and replaces each Gwarmacces (100 gas) with a PUSH32 (3 gas).

There are 5 instances of this issue:

File: contracts/contracts/Gauge.sol   #1

20:       bool public isForPair;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L20

File: contracts/contracts/RewardsDistributor.sol   #2

32:       uint public start_time;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L32

File: contracts/contracts/RewardsDistributor.sol   #3

40:       address public voting_escrow;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L40

File: contracts/contracts/Pair.sol   #4

13:       string public name;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L13

File: contracts/contracts/Pair.sol   #5

14:       string public symbol;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L14

3. State variables can be packed into fewer storage slots

If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables can also be cheaper

There is 1 instance of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

/// @audit Variable ordering with 23 slots instead of the current 24:
/// @audit  mapping(32):point_history, mapping(32):supportedInterfaces, uint256(32):tokenId, mapping(32):idToOwner, mapping(32):ownerToNFTokenCount, mapping(32):idToApprovals, mapping(32):ownerToOperators, mapping(32):ownership_change, mapping(32):ownerToNFTokenIdList, mapping(32):tokenToOwnerIndex, mapping(32):user_point_epoch, mapping(32):user_point_history, mapping(32):locked, uint256(32):epoch, mapping(32):slope_changes, uint256(32):supply, mapping(32):attachments, mapping(32):voted, mapping(32):_delegates, mapping(32):checkpoints, mapping(32):numCheckpoints, mapping(32):nonces, address(20):voter, uint8(1):_entered_state
67:       address public voter;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L67

4. Using calldata instead of memory for read-only arguments in external functions saves gas

When a function with a memory array is called externally, the abi.decode() step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution.

If the array is passed to an internal function which passes the array to another internal function where the array is modified and therefore memory is used in the external call, it's still more gass-efficient to use calldata when the external function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one

There are 13 instances of this issue:

File: contracts/contracts/Minter.sol   #1

50:           address[] memory claimants,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L50

File: contracts/contracts/Minter.sol   #2

51:           uint[] memory amounts,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L51

File: contracts/contracts/redeem/RedemptionReceiver.sol   #3

74:           bytes memory srcAddress,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L74

File: contracts/contracts/redeem/RedemptionReceiver.sol   #4

76:           bytes memory payload

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L76

File: contracts/contracts/Router.sol   #5

394:          uint[] memory amounts,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L394

File: contracts/contracts/Gauge.sol   #6

347:      function getReward(address account, address[] memory tokens) external lock {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L347

File: contracts/contracts/RewardsDistributor.sol   #7

294:      function claim_many(uint[] memory _tokenIds) external returns (bool) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L294

File: contracts/contracts/Voter.sol   #8

74:       function initialize(address[] memory _tokens, address _minter) external {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L74

File: contracts/contracts/Voter.sol   #9

265:      function updateFor(address[] memory _gauges) external {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L265

File: contracts/contracts/Voter.sol   #10

303:      function claimRewards(address[] memory _gauges, address[][] memory _tokens) external {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L303

File: contracts/contracts/Voter.sol   #11

303:      function claimRewards(address[] memory _gauges, address[][] memory _tokens) external {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L303

File: contracts/contracts/Voter.sol   #12

309:      function distributeFees(address[] memory _gauges) external {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L309

File: contracts/contracts/Voter.sol   #13

345:      function distribute(address[] memory _gauges) external {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L345

5. State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replace each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

There are 39 instances of this issue:

File: contracts/contracts/Minter.sol   #1

/// @audit weekly
119:              uint _growth = calculate_growth(weekly);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L119

File: contracts/contracts/Minter.sol   #2

/// @audit weekly
120:              uint _teamEmissions = (teamRate * (_growth + weekly)) /

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L120

File: contracts/contracts/Minter.sol   #3

/// @audit weekly
122:              uint _required = _growth + weekly + _teamEmissions;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L122

File: contracts/contracts/Minter.sol   #4

/// @audit weekly
133:              _velo.approve(address(_voter), weekly);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L133

File: contracts/contracts/Minter.sol   #5

/// @audit weekly
134:              _voter.notifyRewardAmount(weekly);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L134

File: contracts/contracts/Minter.sol   #6

/// @audit weekly
136:              emit Mint(msg.sender, weekly, circulating_supply(), circulating_emission());

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L136

File: contracts/contracts/Minter.sol   #7

/// @audit pendingTeam
71:           team = pendingTeam;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L71

File: contracts/contracts/Minter.sol   #8

/// @audit teamRate
121:                  (PRECISION - teamRate);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L121

File: contracts/contracts/redeem/RedemptionReceiver.sol   #9

/// @audit fantomSender
82:                   addressFromPackedBytes(srcAddress) == fantomSender,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L82

File: contracts/contracts/redeem/RedemptionReceiver.sol   #10

/// @audit eligibleWEVE
69:           shareOfVELO = (amountWEVE * redeemableVELO) / eligibleWEVE;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L69

File: contracts/contracts/Router.sol   #11

/// @audit routes[i].from
93:                   amounts[i+1] = IPair(pair).getAmountOut(amounts[i], routes[i].from);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L93

File: contracts/contracts/Router.sol   #12

/// @audit routes[i].from
321:              IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap(

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L321

File: contracts/contracts/Router.sol   #13

/// @audit routes[i].to
321:              IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap(

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L321

File: contracts/contracts/Router.sol   #14

/// @audit routes[0].from
343:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L343

File: contracts/contracts/Router.sol   #15

/// @audit routes[0].from
343:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L343

File: contracts/contracts/Router.sol   #16

/// @audit routes[0].to
343:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L343

File: contracts/contracts/Router.sol   #17

/// @audit routes[0].stable
343:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L343

File: contracts/contracts/Router.sol   #18

/// @audit routes[0].from
358:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L358

File: contracts/contracts/Router.sol   #19

/// @audit routes[0].from
373:          assert(weth.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L373

File: contracts/contracts/Router.sol   #20

/// @audit routes[0].from
386:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L386

File: contracts/contracts/Router.sol   #21

/// @audit routes[0].from
399:          _safeTransferFrom(routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L399

File: contracts/contracts/Gauge.sol   #22

/// @audit isForPair
111:          if (isForPair) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L111

File: contracts/contracts/Gauge.sol   #23

/// @audit derivedSupply
379:          return rewardPerTokenStored[token] + ((lastTimeRewardApplicable(token) - Math.min(lastUpdateTime[token], periodFinish[token])) * rewardRate[token] * PRECISION / derivedSupply);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L379

File: contracts/contracts/Gauge.sol   #24

/// @audit tokenIds
526:              require(tokenIds[msg.sender] == tokenId);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L526

File: contracts/contracts/Gauge.sol   #25

/// @audit supplyNumCheckpoints
403:          uint _endIndex = Math.min(supplyNumCheckpoints-1, maxRuns);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L403

File: contracts/contracts/Gauge.sol   #26

/// @audit supplyNumCheckpoints
445:          uint _endIndex = supplyNumCheckpoints-1;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L445

File: contracts/contracts/VotingEscrow.sol   #27

/// @audit tokenId
100:          emit Transfer(address(this), address(0), tokenId);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L100

File: contracts/contracts/VotingEscrow.sol   #28

/// @audit tokenId
790:          uint _tokenId = tokenId;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L790

File: contracts/contracts/RewardsDistributor.sol   #29

/// @audit voting_escrow
288:              IVotingEscrow(voting_escrow).deposit_for(_tokenId, amount);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L288

File: contracts/contracts/Velo.sol   #30

/// @audit redemptionReceiver
76:           _mint(redemptionReceiver, amount);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L76

File: contracts/contracts/Bribe.sol   #31

/// @audit gauge
87:         _safeTransfer(token, address(gauge), rewardPerEpoch);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L87

File: contracts/contracts/factories/GaugeFactory.sol   #32

/// @audit last_gauge
25:           return last_gauge;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/GaugeFactory.sol#L25

File: contracts/contracts/factories/GaugeFactory.sol   #33

/// @audit last_gauge
31:           return last_gauge;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/GaugeFactory.sol#L31

File: contracts/contracts/factories/BribeFactory.sol   #34

/// @audit last_gauge
11:           return last_gauge;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/BribeFactory.sol#L11

File: contracts/contracts/factories/PairFactory.sol   #35

/// @audit pendingPauser
47:           pauser = pendingPauser;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L47

File: contracts/contracts/factories/PairFactory.sol   #36

/// @audit pendingFeeManager
62:           feeManager = pendingFeeManager;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L62

File: contracts/contracts/Pair.sol   #37

/// @audit DOMAIN_SEPARATOR
480:                  DOMAIN_SEPARATOR,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L480

File: contracts/contracts/Pair.sol   #38

/// @audit reserve0
220:          emit Sync(reserve0, reserve1);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L220

File: contracts/contracts/Pair.sol   #39

/// @audit reserve1
220:          emit Sync(reserve0, reserve1);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L220

6. Multiple accesses of a mapping should use a local variable cache

The instances below point to the second+ access of a value inside a mapping, within a function. Caching a mapping's value in a local storage variable when the value is accessed multiple times, saves ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.

There are 34 instances of this issue:

File: contracts/contracts/Router.sol   #1

/// @audit routes[i]
91:               address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L91

File: contracts/contracts/Router.sol   #2

/// @audit routes[i]
91:               address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L91

File: contracts/contracts/Router.sol   #3

/// @audit routes[i]
93:                   amounts[i+1] = IPair(pair).getAmountOut(amounts[i], routes[i].from);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L93

File: contracts/contracts/Router.sol   #4

/// @audit routes[i]
317:              (address token0,) = sortTokens(routes[i].from, routes[i].to);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L317

File: contracts/contracts/Router.sol   #5

/// @audit routes[i]
319:              (uint amount0Out, uint amount1Out) = routes[i].from == token0 ? (uint(0), amountOut) : (amountOut, uint(0));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L319

File: contracts/contracts/Router.sol   #6

/// @audit routes[<etc>]
320:              address to = i < routes.length - 1 ? pairFor(routes[i+1].from, routes[i+1].to, routes[i+1].stable) : _to;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L320

File: contracts/contracts/Router.sol   #7

/// @audit routes[<etc>]
320:              address to = i < routes.length - 1 ? pairFor(routes[i+1].from, routes[i+1].to, routes[i+1].stable) : _to;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L320

File: contracts/contracts/Router.sol   #8

/// @audit routes[i]
321:              IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap(

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L321

File: contracts/contracts/Router.sol   #9

/// @audit routes[i]
321:              IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap(

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L321

File: contracts/contracts/Router.sol   #10

/// @audit routes[i]
321:              IPair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap(

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L321

File: contracts/contracts/Router.sol   #11

/// @audit routes[0]
338:          routes[0].to = tokenTo;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L338

File: contracts/contracts/Router.sol   #12

/// @audit routes[0]
339:          routes[0].stable = stable;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L339

File: contracts/contracts/Router.sol   #13

/// @audit routes[0]
343:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L343

File: contracts/contracts/Router.sol   #14

/// @audit routes[0]
343:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L343

File: contracts/contracts/Router.sol   #15

/// @audit routes[0]
343:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L343

File: contracts/contracts/Router.sol   #16

/// @audit routes[0]
343:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L343

File: contracts/contracts/Router.sol   #17

/// @audit routes[0]
358:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L358

File: contracts/contracts/Router.sol   #18

/// @audit routes[0]
358:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L358

File: contracts/contracts/Router.sol   #19

/// @audit routes[0]
358:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L358

File: contracts/contracts/Router.sol   #20

/// @audit routes[0]
373:          assert(weth.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L373

File: contracts/contracts/Router.sol   #21

/// @audit routes[0]
373:          assert(weth.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L373

File: contracts/contracts/Router.sol   #22

/// @audit routes[0]
373:          assert(weth.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L373

File: contracts/contracts/Router.sol   #23

/// @audit routes[0]
386:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L386

File: contracts/contracts/Router.sol   #24

/// @audit routes[0]
386:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L386

File: contracts/contracts/Router.sol   #25

/// @audit routes[0]
386:              routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L386

File: contracts/contracts/Router.sol   #26

/// @audit routes[0]
399:          _safeTransferFrom(routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L399

File: contracts/contracts/Router.sol   #27

/// @audit routes[0]
399:          _safeTransferFrom(routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L399

File: contracts/contracts/Router.sol   #28

/// @audit routes[0]
399:          _safeTransferFrom(routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L399

File: contracts/contracts/Gauge.sol   #29

/// @audit supplyCheckpoints[<etc>]
331:              supplyCheckpoints[_nCheckPoints - 1].supply = derivedSupply;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L331

File: contracts/contracts/VotingEscrow.sol   #30

/// @audit point_history[0]
91:           point_history[0].ts = block.timestamp;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L91

File: contracts/contracts/Pair.sol   #31

/// @audit observations[nextIndex]
279:              uint _reserve0 = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L279

File: contracts/contracts/Pair.sol   #32

/// @audit observations[i]
279:              uint _reserve0 = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L279

File: contracts/contracts/Pair.sol   #33

/// @audit observations[nextIndex]
280:              uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L280

File: contracts/contracts/Pair.sol   #34

/// @audit observations[i]
280:              uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L280

7. <x> += <y> costs more gas than <x> = <x> + <y> for state variables

There are 21 instances of this issue:

File: contracts/contracts/redeem/RedemptionReceiver.sol   #1

92:               (redeemedWEVE += amountWEVE) <= eligibleWEVE,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L92

File: contracts/contracts/Gauge.sol   #2

365:          derivedSupply -= _derivedBalance;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L365

File: contracts/contracts/Gauge.sol   #3

368:          derivedSupply += _derivedBalance;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L368

File: contracts/contracts/Gauge.sol   #4

517:          totalSupply += amount;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L517

File: contracts/contracts/Gauge.sol   #5

532:          derivedSupply -= _derivedBalance;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L532

File: contracts/contracts/Gauge.sol   #6

535:          derivedSupply += _derivedBalance;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L535

File: contracts/contracts/Gauge.sol   #7

559:          totalSupply -= amount;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L559

File: contracts/contracts/Gauge.sol   #8

572:          derivedSupply -= _derivedBalance;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L572

File: contracts/contracts/Gauge.sol   #9

575:          derivedSupply += _derivedBalance;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L575

File: contracts/contracts/RewardsDistributor.sol   #10

289:              token_last_balance -= amount;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L289

File: contracts/contracts/RewardsDistributor.sol   #11

311:              token_last_balance -= total;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L311

File: contracts/contracts/Velo.sol   #12

44:           totalSupply += _amount;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L44

File: contracts/contracts/Pair.sol   #13

155:              index0 += _ratio;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L155

File: contracts/contracts/Pair.sol   #14

165:              index1 += _ratio;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L165

File: contracts/contracts/Pair.sol   #15

208:              reserve0CumulativeLast += _reserve0 * timeElapsed;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L208

File: contracts/contracts/Pair.sol   #16

209:              reserve1CumulativeLast += _reserve1 * timeElapsed;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L209

File: contracts/contracts/Pair.sol   #17

447:          totalSupply += amount;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L447

File: contracts/contracts/Pair.sol   #18

454:          totalSupply -= amount;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L454

File: contracts/contracts/Voter.sol   #19

118:          totalWeight -= uint256(_totalWeight);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L118

File: contracts/contracts/Voter.sol   #20

168:          totalWeight += uint256(_totalWeight);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L168

File: contracts/contracts/Voter.sol   #21

260:              index += _ratio;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L260

8. internal functions only called once can be inlined to save gas

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

There are 25 instances of this issue:

File: contracts/contracts/Router.sol   #1

409:      function _safeTransfer(address token, address to, uint256 value) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L409

File: contracts/contracts/Gauge.sol   #2

390:      function _batchRewardPerToken(address token, uint maxRuns) internal returns (uint, uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L390

File: contracts/contracts/Gauge.sol   #3

648:      function _notifyBribeAmount(address token, uint amount, uint epochStart) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L648

File: contracts/contracts/VotingEscrow.sol   #4

149:      function _tokenURI(uint _tokenId, uint _balanceOf, uint _locked_end, uint _value) internal pure returns (string memory output) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L149

File: contracts/contracts/VotingEscrow.sol   #5

270:      function _clearApproval(address _owner, uint _tokenId) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L270

File: contracts/contracts/VotingEscrow.sol   #6

361:      function _isContract(address account) internal view returns (bool) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L361

File: contracts/contracts/VotingEscrow.sol   #7

436:      function _addTokenToOwnerList(address _to, uint _tokenId) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L436

File: contracts/contracts/VotingEscrow.sol   #8

462:      function _mint(address _to, uint _tokenId) internal returns (bool) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L462

File: contracts/contracts/VotingEscrow.sol   #9

477:      function _removeTokenFromOwnerList(address _from, uint _tokenId) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L477

File: contracts/contracts/VotingEscrow.sol   #10

934:      function _balanceOfAtNFT(uint _tokenId, uint _block) internal view returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L934

File: contracts/contracts/VotingEscrow.sol   #11

1278      function _moveAllDelegates(
1279          address owner,
1280          address srcRep,
1281:         address dstRep

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1278-L1281

File: contracts/contracts/VelodromeLibrary.sol   #12

15:       function _f(uint x0, uint y) internal pure returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol#L15

File: contracts/contracts/VelodromeLibrary.sol   #13

23:       function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol#L23

File: contracts/contracts/VelodromeLibrary.sol   #14

93:       function _k(uint x, uint y, bool stable, uint decimals0, uint decimals1) internal pure returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol#L93

File: contracts/contracts/RewardsDistributor.sol   #15

64        function _checkpoint_token() internal {
65:           uint token_balance = IERC20(token).balanceOf(address(this));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L64-L65

File: contracts/contracts/RewardsDistributor.sol   #16

102:      function _find_timestamp_epoch(address ve, uint _timestamp) internal view returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L102

File: contracts/contracts/RewardsDistributor.sol   #17

226:      function _claimable(uint _tokenId, address ve, uint _last_token_time) internal view returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L226

File: contracts/contracts/Bribe.sol   #18

92:     function _safeTransfer(address token, address to, uint256 value) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L92

File: contracts/contracts/Bribe.sol   #19

99:     function _safeTransferFrom(address token, address from, address to, uint256 value) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L99

File: contracts/contracts/Pair.sol   #20

151:      function _update0(uint amount) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L151

File: contracts/contracts/Pair.sol   #21

161:      function _update1(uint amount) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L161

File: contracts/contracts/Pair.sol   #22

380:      function _f(uint x0, uint y) internal pure returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L380

File: contracts/contracts/Pair.sol   #23

388:      function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L388

File: contracts/contracts/Pair.sol   #24

452:      function _burn(address dst, uint amount) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L452

File: contracts/contracts/Voter.sol   #25

351:      function _safeTransferFrom(address token, address from, address to, uint256 value) internal {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L351

9. <array>.length should not be looked up in every loop of a for-loop

The overheads outlined below are PER LOOP, excluding the first loop

  • storage arrays incur a Gwarmaccess (100 gas)
  • memory arrays use MLOAD (3 gas)
  • calldata arrays use CALLDATALOAD (3 gas)

Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

There are 17 instances of this issue:

File: contracts/contracts/Minter.sol   #1

57:           for (uint i = 0; i < claimants.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L57

File: contracts/contracts/Router.sol   #2

90:           for (uint i = 0; i < routes.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L90

File: contracts/contracts/Router.sol   #3

316:          for (uint i = 0; i < routes.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L316

File: contracts/contracts/Gauge.sol   #4

353:          for (uint i = 0; i < tokens.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L353

File: contracts/contracts/VotingEscrow.sol   #5

1146:         for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1146

File: contracts/contracts/VotingEscrow.sol   #6

1193:         for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1193

File: contracts/contracts/VotingEscrow.sol   #7

1225:                 for (uint i = 0; i < srcRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1225

File: contracts/contracts/VotingEscrow.sol   #8

1249:                 for (uint i = 0; i < dstRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1249

File: contracts/contracts/VotingEscrow.sol   #9

1295:                 for (uint i = 0; i < srcRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1295

File: contracts/contracts/VotingEscrow.sol   #10

1320:                 for (uint i = 0; i < dstRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1320

File: contracts/contracts/RewardsDistributor.sol   #11

301:          for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L301

File: contracts/contracts/Pair.sol   #12

257:          for (uint i = 0; i < _prices.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L257

File: contracts/contracts/Voter.sol   #13

76:           for (uint i = 0; i < _tokens.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L76

File: contracts/contracts/Voter.sol   #14

266:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L266

File: contracts/contracts/Voter.sol   #15

304:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L304

File: contracts/contracts/Voter.sol   #16

310:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L310

File: contracts/contracts/Voter.sol   #17

346:          for (uint x = 0; x < _gauges.length; x++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L346

10. ++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop

There are 41 instances of this issue:

File: contracts/contracts/Minter.sol   #1

57:           for (uint i = 0; i < claimants.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L57

File: contracts/contracts/Router.sol   #2

90:           for (uint i = 0; i < routes.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L90

File: contracts/contracts/Router.sol   #3

316:          for (uint i = 0; i < routes.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L316

File: contracts/contracts/Gauge.sol   #4

179:          for (uint i = 0; i < numRewards; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L179

File: contracts/contracts/Gauge.sol   #5

353:          for (uint i = 0; i < tokens.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L353

File: contracts/contracts/Gauge.sol   #6

405:          for (uint i = _startIndex; i < _endIndex; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L405

File: contracts/contracts/Gauge.sol   #7

426:          for (uint i; i < length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L426

File: contracts/contracts/Gauge.sol   #8

448:              for (uint i = _startIndex; i < _endIndex; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L448

File: contracts/contracts/Gauge.sol   #9

484:              for (uint i = _startIndex; i < _endIndex; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L484

File: contracts/contracts/VotingEscrow.sol   #10

632:              for (uint i = 0; i < 255; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L632

File: contracts/contracts/VotingEscrow.sol   #11

886:          for (uint i = 0; i < 128; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L886

File: contracts/contracts/VotingEscrow.sol   #12

942:          for (uint i = 0; i < 128; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L942

File: contracts/contracts/VotingEscrow.sol   #13

1017:         for (uint i = 0; i < 255; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1017

File: contracts/contracts/VotingEscrow.sol   #14

1146:         for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1146

File: contracts/contracts/VotingEscrow.sol   #15

1193:         for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1193

File: contracts/contracts/VotingEscrow.sol   #16

1225:                 for (uint i = 0; i < srcRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1225

File: contracts/contracts/VotingEscrow.sol   #17

1249:                 for (uint i = 0; i < dstRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1249

File: contracts/contracts/VotingEscrow.sol   #18

1295:                 for (uint i = 0; i < srcRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1295

File: contracts/contracts/VotingEscrow.sol   #19

1320:                 for (uint i = 0; i < dstRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1320

File: contracts/contracts/VotingEscrow.sol   #20

1325:                 for (uint i = 0; i < ownerTokenCount; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1325

File: contracts/contracts/VelodromeLibrary.sol   #21

24:           for (uint i = 0; i < 255; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol#L24

File: contracts/contracts/RewardsDistributor.sol   #22

75:           for (uint i = 0; i < 20; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L75

File: contracts/contracts/RewardsDistributor.sol   #23

105:          for (uint i = 0; i < 128; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L105

File: contracts/contracts/RewardsDistributor.sol   #24

121:          for (uint i = 0; i < 128; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L121

File: contracts/contracts/RewardsDistributor.sol   #25

148:          for (uint i = 0; i < 20; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L148

File: contracts/contracts/RewardsDistributor.sol   #26

195:          for (uint i = 0; i < 50; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L195

File: contracts/contracts/RewardsDistributor.sol   #27

252:          for (uint i = 0; i < 50; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L252

File: contracts/contracts/RewardsDistributor.sol   #28

301:          for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L301

File: contracts/contracts/Pair.sol   #29

257:          for (uint i = 0; i < _prices.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L257

File: contracts/contracts/Pair.sol   #30

389:          for (uint i = 0; i < 255; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L389

File: contracts/contracts/Voter.sol   #31

76:           for (uint i = 0; i < _tokens.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L76

File: contracts/contracts/Voter.sol   #32

103:          for (uint i = 0; i < _poolVoteCnt; i ++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L103

File: contracts/contracts/Voter.sol   #33

128:          for (uint i = 0; i < _poolCnt; i ++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L128

File: contracts/contracts/Voter.sol   #34

143:          for (uint i = 0; i < _poolCnt; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L143

File: contracts/contracts/Voter.sol   #35

147:          for (uint i = 0; i < _poolCnt; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L147

File: contracts/contracts/Voter.sol   #36

266:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L266

File: contracts/contracts/Voter.sol   #37

272:          for (uint i = start; i < end; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L272

File: contracts/contracts/Voter.sol   #38

304:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L304

File: contracts/contracts/Voter.sol   #39

310:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L310

File: contracts/contracts/Voter.sol   #40

340:          for (uint x = start; x < finish; x++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L340

File: contracts/contracts/Voter.sol   #41

346:          for (uint x = 0; x < _gauges.length; x++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L346

11. require()/revert() strings longer than 32 bytes cost extra gas

There are 14 instances of this issue:

File: contracts/contracts/Router.sol   #1

341:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L341

File: contracts/contracts/Router.sol   #2

356:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L356

File: contracts/contracts/Router.sol   #3

371:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L371

File: contracts/contracts/Router.sol   #4

384:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L384

File: contracts/contracts/Router.sol   #5

406:          require(success, 'TransferHelper: ETH_TRANSFER_FAILED');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L406

File: contracts/contracts/VotingEscrow.sol   #6

398:                      revert('ERC721: transfer to non ERC721Receiver implementer');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L398

File: contracts/contracts/VotingEscrow.sol   #7

774:          require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L774

File: contracts/contracts/VotingEscrow.sol   #8

786:          require(unlock_time > block.timestamp, 'Can only lock until time in the future');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L786

File: contracts/contracts/VotingEscrow.sol   #9

821:          require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L821

File: contracts/contracts/VotingEscrow.sol   #10

1245                  require(
1246                      dstRepOld.length + 1 <= MAX_DELEGATES,
1247                      "dstRep would have too many tokenIds"
1248:                 );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1245-L1248

File: contracts/contracts/VotingEscrow.sol   #11

1315                  require(
1316                      dstRepOld.length + ownerTokenCount <= MAX_DELEGATES,
1317                      "dstRep would have too many tokenIds"
1318:                 );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1315-L1318

File: contracts/contracts/VotingEscrow.sol   #12

1378          require(
1379              signatory != address(0),
1380              "VotingEscrow::delegateBySig: invalid signature"
1381:         );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1378-L1381

File: contracts/contracts/VotingEscrow.sol   #13

1382          require(
1383              nonce == nonces[signatory]++,
1384              "VotingEscrow::delegateBySig: invalid nonce"
1385:         );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1382-L1385

File: contracts/contracts/VotingEscrow.sol   #14

1386          require(
1387              block.timestamp <= expiry,
1388              "VotingEscrow::delegateBySig: signature expired"
1389:         );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1386-L1389

12. keccak256() should only need to be called on a specific string literal once

It should be saved to an immutable variable, and the variable used instead. If the hash is being used as a part of a function selector, the cast to bytes4 should also only be done once

There is 1 instance of this issue:

File: contracts/contracts/Pair.sol   #1

470:                  keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L470

13. Using bools for storage incurs overhead

    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27 Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from 'false' to 'true', after having been 'true' in the past

There are 14 instances of this issue:

File: contracts/contracts/Gauge.sol   #1

20:       bool public isForPair;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L20

File: contracts/contracts/Gauge.sol   #2

53:       mapping(address => bool) public isReward;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L53

File: contracts/contracts/VotingEscrow.sol   #3

71:       mapping(bytes4 => bool) internal supportedInterfaces;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L71

File: contracts/contracts/VotingEscrow.sol   #4

212:      mapping(address => mapping(address => bool)) internal ownerToOperators;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L212

File: contracts/contracts/VotingEscrow.sol   #5

1057:     mapping(uint => bool) public voted;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1057

File: contracts/contracts/Bribe.sol   #6

17:     mapping(address => bool) public isReward;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L17

File: contracts/contracts/factories/PairFactory.sol   #7

8:        bool public isPaused;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L8

File: contracts/contracts/factories/PairFactory.sol   #8

18:       mapping(address => mapping(address => mapping(bool => address))) public getPair;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L18

File: contracts/contracts/factories/PairFactory.sol   #9

20:       mapping(address => bool) public isPair; // simplified check if its a pair, given that `stable` flag might not be available in peripherals

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L20

File: contracts/contracts/factories/PairFactory.sol   #10

24:       bool internal _temp;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L24

File: contracts/contracts/Pair.sol   #11

18:       bool public immutable stable;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L18

File: contracts/contracts/Voter.sol   #12

37:       mapping(address => bool) public isGauge;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L37

File: contracts/contracts/Voter.sol   #13

38:       mapping(address => bool) public isWhitelisted;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L38

File: contracts/contracts/Voter.sol   #14

39:       mapping(address => bool) public isAlive;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L39

14. Using > 0 costs more gas than != 0 when used on a uint in a require() statement

This change saves 6 gas per instance

There are 6 instances of this issue:

File: contracts/contracts/Router.sol   #1

58:           require(amountA > 0, 'Router: INSUFFICIENT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L58

File: contracts/contracts/Gauge.sol   #2

512:          require(amount > 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L512

File: contracts/contracts/Gauge.sol   #3

592:          require(amount > 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L592

File: contracts/contracts/VotingEscrow.sol   #4

772:          require(_value > 0); // dev: need non-zero value

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L772

File: contracts/contracts/VotingEscrow.sol   #5

785:          require(_value > 0); // dev: need non-zero value

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L785

File: contracts/contracts/Bribe.sol   #6

42:         require(amount > 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L42

15. It costs more gas to initialize variables to zero than to let the default of zero be applied

There are 68 instances of this issue:

File: contracts/contracts/Minter.sol   #1

57:           for (uint i = 0; i < claimants.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L57

File: contracts/contracts/Router.sol   #2

90:           for (uint i = 0; i < routes.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L90

File: contracts/contracts/Router.sol   #3

112:          uint _totalSupply = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L112

File: contracts/contracts/Router.sol   #4

316:          for (uint i = 0; i < routes.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L316

File: contracts/contracts/Gauge.sol   #5

179:          for (uint i = 0; i < numRewards; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L179

File: contracts/contracts/Gauge.sol   #6

222:          uint lower = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L222

File: contracts/contracts/Gauge.sol   #7

254:          uint lower = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L254

File: contracts/contracts/Gauge.sol   #8

286:          uint lower = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L286

File: contracts/contracts/Gauge.sol   #9

353:          for (uint i = 0; i < tokens.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L353

File: contracts/contracts/Gauge.sol   #10

481:          uint reward = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L481

File: contracts/contracts/Gauge.sol   #11

551:          uint tokenId = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L551

File: contracts/contracts/VotingEscrow.sol   #12

584:          int128 old_dslope = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L584

File: contracts/contracts/VotingEscrow.sol   #13

585:          int128 new_dslope = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L585

File: contracts/contracts/VotingEscrow.sol   #14

632:              for (uint i = 0; i < 255; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L632

File: contracts/contracts/VotingEscrow.sol   #15

636:                  int128 d_slope = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L636

File: contracts/contracts/VotingEscrow.sol   #16

884:          uint _min = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L884

File: contracts/contracts/VotingEscrow.sol   #17

886:          for (uint i = 0; i < 128; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L886

File: contracts/contracts/VotingEscrow.sol   #18

940:          uint _min = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L940

File: contracts/contracts/VotingEscrow.sol   #19

942:          for (uint i = 0; i < 128; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L942

File: contracts/contracts/VotingEscrow.sol   #20

960:          uint d_block = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L960

File: contracts/contracts/VotingEscrow.sol   #21

961:          uint d_t = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L961

File: contracts/contracts/VotingEscrow.sol   #22

996:          uint dt = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L996

File: contracts/contracts/VotingEscrow.sol   #23

1017:         for (uint i = 0; i < 255; ++i) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1017

File: contracts/contracts/VotingEscrow.sol   #24

1019:             int128 d_slope = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1019

File: contracts/contracts/VotingEscrow.sol   #25

1145:         uint votes = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1145

File: contracts/contracts/VotingEscrow.sol   #26

1146:         for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1146

File: contracts/contracts/VotingEscrow.sol   #27

1168:         uint32 lower = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1168

File: contracts/contracts/VotingEscrow.sol   #28

1192:         uint votes = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1192

File: contracts/contracts/VotingEscrow.sol   #29

1193:         for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1193

File: contracts/contracts/VotingEscrow.sol   #30

1225:                 for (uint i = 0; i < srcRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1225

File: contracts/contracts/VotingEscrow.sol   #31

1249:                 for (uint i = 0; i < dstRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1249

File: contracts/contracts/VotingEscrow.sol   #32

1295:                 for (uint i = 0; i < srcRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1295

File: contracts/contracts/VotingEscrow.sol   #33

1320:                 for (uint i = 0; i < dstRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1320

File: contracts/contracts/VotingEscrow.sol   #34

1325:                 for (uint i = 0; i < ownerTokenCount; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1325

File: contracts/contracts/VelodromeLibrary.sol   #35

24:           for (uint i = 0; i < 255; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol#L24

File: contracts/contracts/RewardsDistributor.sol   #36

73:           uint next_week = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L73

File: contracts/contracts/RewardsDistributor.sol   #37

75:           for (uint i = 0; i < 20; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L75

File: contracts/contracts/RewardsDistributor.sol   #38

103:          uint _min = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L103

File: contracts/contracts/RewardsDistributor.sol   #39

105:          for (uint i = 0; i < 128; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L105

File: contracts/contracts/RewardsDistributor.sol   #40

119:          uint _min = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L119

File: contracts/contracts/RewardsDistributor.sol   #41

121:          for (uint i = 0; i < 128; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L121

File: contracts/contracts/RewardsDistributor.sol   #42

148:          for (uint i = 0; i < 20; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L148

File: contracts/contracts/RewardsDistributor.sol   #43

154:                  int128 dt = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L154

File: contracts/contracts/RewardsDistributor.sol   #44

170:          uint user_epoch = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L170

File: contracts/contracts/RewardsDistributor.sol   #45

171:          uint to_distribute = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L171

File: contracts/contracts/RewardsDistributor.sol   #46

195:          for (uint i = 0; i < 50; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L195

File: contracts/contracts/RewardsDistributor.sol   #47

227:          uint user_epoch = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L227

File: contracts/contracts/RewardsDistributor.sol   #48

228:          uint to_distribute = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L228

File: contracts/contracts/RewardsDistributor.sol   #49

252:          for (uint i = 0; i < 50; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L252

File: contracts/contracts/RewardsDistributor.sol   #50

299:          uint total = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L299

File: contracts/contracts/RewardsDistributor.sol   #51

301:          for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L301

File: contracts/contracts/Pair.sol   #52

257:          for (uint i = 0; i < _prices.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L257

File: contracts/contracts/Pair.sol   #53

273:          uint nextIndex = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L273

File: contracts/contracts/Pair.sol   #54

274:          uint index = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L274

File: contracts/contracts/Pair.sol   #55

389:          for (uint i = 0; i < 255; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L389

File: contracts/contracts/Voter.sol   #56

76:           for (uint i = 0; i < _tokens.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L76

File: contracts/contracts/Voter.sol   #57

101:          uint256 _totalWeight = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L101

File: contracts/contracts/Voter.sol   #58

103:          for (uint i = 0; i < _poolVoteCnt; i ++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L103

File: contracts/contracts/Voter.sol   #59

128:          for (uint i = 0; i < _poolCnt; i ++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L128

File: contracts/contracts/Voter.sol   #60

139:          uint256 _totalVoteWeight = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L139

File: contracts/contracts/Voter.sol   #61

140:          uint256 _totalWeight = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L140

File: contracts/contracts/Voter.sol   #62

141:          uint256 _usedWeight = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L141

File: contracts/contracts/Voter.sol   #63

143:          for (uint i = 0; i < _poolCnt; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L143

File: contracts/contracts/Voter.sol   #64

147:          for (uint i = 0; i < _poolCnt; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L147

File: contracts/contracts/Voter.sol   #65

266:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L266

File: contracts/contracts/Voter.sol   #66

304:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L304

File: contracts/contracts/Voter.sol   #67

310:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L310

File: contracts/contracts/Voter.sol   #68

346:          for (uint x = 0; x < _gauges.length; x++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L346

16. ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)

Saves 6 gas per loop

There are 37 instances of this issue:

File: contracts/contracts/Minter.sol   #1

57:           for (uint i = 0; i < claimants.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L57

File: contracts/contracts/Router.sol   #2

90:           for (uint i = 0; i < routes.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L90

File: contracts/contracts/Router.sol   #3

316:          for (uint i = 0; i < routes.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L316

File: contracts/contracts/Gauge.sol   #4

179:          for (uint i = 0; i < numRewards; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L179

File: contracts/contracts/Gauge.sol   #5

353:          for (uint i = 0; i < tokens.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L353

File: contracts/contracts/Gauge.sol   #6

405:          for (uint i = _startIndex; i < _endIndex; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L405

File: contracts/contracts/Gauge.sol   #7

426:          for (uint i; i < length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L426

File: contracts/contracts/Gauge.sol   #8

448:              for (uint i = _startIndex; i < _endIndex; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L448

File: contracts/contracts/Gauge.sol   #9

484:              for (uint i = _startIndex; i < _endIndex; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L484

File: contracts/contracts/VotingEscrow.sol   #10

1146:         for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1146

File: contracts/contracts/VotingEscrow.sol   #11

1193:         for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1193

File: contracts/contracts/VotingEscrow.sol   #12

1225:                 for (uint i = 0; i < srcRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1225

File: contracts/contracts/VotingEscrow.sol   #13

1249:                 for (uint i = 0; i < dstRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1249

File: contracts/contracts/VotingEscrow.sol   #14

1295:                 for (uint i = 0; i < srcRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1295

File: contracts/contracts/VotingEscrow.sol   #15

1320:                 for (uint i = 0; i < dstRepOld.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1320

File: contracts/contracts/VotingEscrow.sol   #16

1325:                 for (uint i = 0; i < ownerTokenCount; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1325

File: contracts/contracts/VelodromeLibrary.sol   #17

24:           for (uint i = 0; i < 255; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VelodromeLibrary.sol#L24

File: contracts/contracts/RewardsDistributor.sol   #18

75:           for (uint i = 0; i < 20; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L75

File: contracts/contracts/RewardsDistributor.sol   #19

105:          for (uint i = 0; i < 128; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L105

File: contracts/contracts/RewardsDistributor.sol   #20

121:          for (uint i = 0; i < 128; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L121

File: contracts/contracts/RewardsDistributor.sol   #21

148:          for (uint i = 0; i < 20; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L148

File: contracts/contracts/RewardsDistributor.sol   #22

195:          for (uint i = 0; i < 50; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L195

File: contracts/contracts/RewardsDistributor.sol   #23

252:          for (uint i = 0; i < 50; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L252

File: contracts/contracts/RewardsDistributor.sol   #24

301:          for (uint i = 0; i < _tokenIds.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L301

File: contracts/contracts/Pair.sol   #25

257:          for (uint i = 0; i < _prices.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L257

File: contracts/contracts/Pair.sol   #26

389:          for (uint i = 0; i < 255; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L389

File: contracts/contracts/Voter.sol   #27

76:           for (uint i = 0; i < _tokens.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L76

File: contracts/contracts/Voter.sol   #28

103:          for (uint i = 0; i < _poolVoteCnt; i ++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L103

File: contracts/contracts/Voter.sol   #29

128:          for (uint i = 0; i < _poolCnt; i ++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L128

File: contracts/contracts/Voter.sol   #30

143:          for (uint i = 0; i < _poolCnt; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L143

File: contracts/contracts/Voter.sol   #31

147:          for (uint i = 0; i < _poolCnt; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L147

File: contracts/contracts/Voter.sol   #32

266:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L266

File: contracts/contracts/Voter.sol   #33

272:          for (uint i = start; i < end; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L272

File: contracts/contracts/Voter.sol   #34

304:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L304

File: contracts/contracts/Voter.sol   #35

310:          for (uint i = 0; i < _gauges.length; i++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L310

File: contracts/contracts/Voter.sol   #36

340:          for (uint x = start; x < finish; x++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L340

File: contracts/contracts/Voter.sol   #37

346:          for (uint x = 0; x < _gauges.length; x++) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L346

17. Splitting require() statements that use && saves gas

See this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper

There are 17 instances of this issue:

File: contracts/contracts/redeem/RedemptionReceiver.sol   #1

79            require(
80                msg.sender == endpoint &&
81                    srcChainId == fantomChainId &&
82                    addressFromPackedBytes(srcAddress) == fantomSender,
83                "UNAUTHORIZED_CALLER"
84:           );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L79-L84

File: contracts/contracts/Router.sol   #2

59:           require(reserveA > 0 && reserveB > 0, 'Router: INSUFFICIENT_LIQUIDITY');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L59

File: contracts/contracts/Router.sol   #3

413:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L413

File: contracts/contracts/Router.sol   #4

420:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L420

File: contracts/contracts/Gauge.sol   #5

668:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L668

File: contracts/contracts/Gauge.sol   #6

675:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L675

File: contracts/contracts/Gauge.sol   #7

682:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L682

File: contracts/contracts/VotingEscrow.sol   #8

307:          require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L307

File: contracts/contracts/VotingEscrow.sol   #9

846:          require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L846

File: contracts/contracts/VotingEscrow.sol   #10

1085:         require(attachments[_from] == 0 && !voted[_from], "attached");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1085

File: contracts/contracts/PairFees.sol   #11

23:           require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/PairFees.sol#L23

File: contracts/contracts/Bribe.sol   #12

96:         require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L96

File: contracts/contracts/Bribe.sol   #13

103:        require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L103

File: contracts/contracts/Pair.sol   #14

485:          require(recoveredAddress != address(0) && recoveredAddress == owner, 'Pair: INVALID_SIGNATURE');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L485

File: contracts/contracts/Pair.sol   #15

525:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L525

File: contracts/contracts/Voter.sol   #16

194:              require(isWhitelisted[tokenA] && isWhitelisted[tokenB], "!whitelisted");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L194

File: contracts/contracts/Voter.sol   #17

355:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L355

18. Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed

There are 45 instances of this issue:

File: contracts/contracts/redeem/RedemptionSender.sol   #1

11:       uint16 public immutable optimismChainId; // 11 for OP, 10011 for OP Kovan

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L11

File: contracts/contracts/redeem/RedemptionSender.sol   #2

17:           uint16 _optimismChainId,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L17

File: contracts/contracts/redeem/RedemptionReceiver.sol   #3

14:       uint16 public immutable fantomChainId; // 12 for FTM, 10012 for FTM testnet

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L14

File: contracts/contracts/redeem/RedemptionReceiver.sol   #4

21:           uint16 _fantomChainId,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L21

File: contracts/contracts/redeem/RedemptionReceiver.sol   #5

73:           uint16 srcChainId,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L73

File: contracts/contracts/redeem/RedemptionReceiver.sol   #6

75:           uint64,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L75

File: contracts/contracts/Router.sol   #7

286:          bool approveMax, uint8 v, bytes32 r, bytes32 s

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L286

File: contracts/contracts/Router.sol   #8

305:          bool approveMax, uint8 v, bytes32 r, bytes32 s

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L305

File: contracts/contracts/VotingEscrow.sol   #9

27:           int128 amount;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L27

File: contracts/contracts/VotingEscrow.sol   #10

32:           int128 bias;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L32

File: contracts/contracts/VotingEscrow.sol   #11

33:           int128 slope; // # -dweight / dt

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L33

File: contracts/contracts/VotingEscrow.sol   #12

108:      uint8 internal constant _not_entered = 1;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L108

File: contracts/contracts/VotingEscrow.sol   #13

109:      uint8 internal constant _entered = 2;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L109

File: contracts/contracts/VotingEscrow.sol   #14

110:      uint8 internal _entered_state = 1;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L110

File: contracts/contracts/VotingEscrow.sol   #15

125:      uint8 constant public decimals = 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L125

File: contracts/contracts/VotingEscrow.sol   #16

543:      int128 internal constant iMAXTIME = 4 * 365 * 86400;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L543

File: contracts/contracts/VotingEscrow.sol   #17

553:      function get_last_user_slope(uint _tokenId) external view returns (int128) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L553

File: contracts/contracts/VotingEscrow.sol   #18

584:          int128 old_dslope = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L584

File: contracts/contracts/VotingEscrow.sol   #19

585:          int128 new_dslope = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L585

File: contracts/contracts/VotingEscrow.sol   #20

636:                  int128 d_slope = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L636

File: contracts/contracts/VotingEscrow.sol   #21

1019:             int128 d_slope = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1019

File: contracts/contracts/VotingEscrow.sol   #22

1140:         uint32 nCheckpoints = numCheckpoints[account];

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1140

File: contracts/contracts/VotingEscrow.sol   #23

1153:     function getPastVotesIndex(address account, uint timestamp) public view returns (uint32) {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1153

File: contracts/contracts/VotingEscrow.sol   #24

1154:         uint32 nCheckpoints = numCheckpoints[account];

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1154

File: contracts/contracts/VotingEscrow.sol   #25

1168:         uint32 lower = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1168

File: contracts/contracts/VotingEscrow.sol   #26

1169:         uint32 upper = nCheckpoints - 1;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1169

File: contracts/contracts/VotingEscrow.sol   #27

1171:             uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1171

File: contracts/contracts/VotingEscrow.sol   #28

1189:         uint32 _checkIndex = getPastVotesIndex(account, timestamp);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1189

File: contracts/contracts/VotingEscrow.sol   #29

1216:                 uint32 srcRepNum = numCheckpoints[srcRep];

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1216

File: contracts/contracts/VotingEscrow.sol   #30

1220:                 uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1220

File: contracts/contracts/VotingEscrow.sol   #31

1236:                 uint32 dstRepNum = numCheckpoints[dstRep];

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1236

File: contracts/contracts/VotingEscrow.sol   #32

1240:                 uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1240

File: contracts/contracts/VotingEscrow.sol   #33

1263:         returns (uint32)

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1263

File: contracts/contracts/VotingEscrow.sol   #34

1266:         uint32 _nCheckPoints = numCheckpoints[account];

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1266

File: contracts/contracts/VotingEscrow.sol   #35

1286:                 uint32 srcRepNum = numCheckpoints[srcRep];

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1286

File: contracts/contracts/VotingEscrow.sol   #36

1290:                 uint32 nextSrcRepNum = _findWhatCheckpointToWrite(srcRep);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1290

File: contracts/contracts/VotingEscrow.sol   #37

1306:                 uint32 dstRepNum = numCheckpoints[dstRep];

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1306

File: contracts/contracts/VotingEscrow.sol   #38

1310:                 uint32 nextDstRepNum = _findWhatCheckpointToWrite(dstRep);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1310

File: contracts/contracts/VotingEscrow.sol   #39

1358:         uint8 v,

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1358

File: contracts/contracts/RewardsDistributor.sol   #40

154:                  int128 dt = 0;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L154

File: contracts/contracts/RewardsDistributor.sol   #41

207:                  int128 dt = int128(int256(week_cursor - old_user_point.ts));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L207

File: contracts/contracts/RewardsDistributor.sol   #42

264:                  int128 dt = int128(int256(week_cursor - old_user_point.ts));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L264

File: contracts/contracts/Velo.sol   #43

8:        uint8 public constant decimals = 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L8

File: contracts/contracts/Pair.sol   #44

15:       uint8 public constant decimals = 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L15

File: contracts/contracts/Pair.sol   #45

466:      function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L466

19. abi.encode() is less efficient than abi.encodePacked()

There is 1 instance of this issue:

File: contracts/contracts/redeem/RedemptionSender.sol   #1

46:               abi.encode(msg.sender, amount),

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L46

20. Using private rather than public for constants, saves gas

If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table

There are 18 instances of this issue:

File: contracts/contracts/Minter.sol   #1

30:       uint public constant MAX_TEAM_RATE = 50; // 50 bps = 0.05%

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L30

File: contracts/contracts/redeem/RedemptionSender.sol   #2

11:       uint16 public immutable optimismChainId; // 11 for OP, 10011 for OP Kovan

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L11

File: contracts/contracts/redeem/RedemptionReceiver.sol   #3

14:       uint16 public immutable fantomChainId; // 12 for FTM, 10012 for FTM testnet

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L14

File: contracts/contracts/VotingEscrow.sol   #4

122:      string constant public name = "veNFT";

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L122

File: contracts/contracts/VotingEscrow.sol   #5

123:      string constant public symbol = "veNFT";

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L123

File: contracts/contracts/VotingEscrow.sol   #6

124:      string constant public version = "1.0.0";

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L124

File: contracts/contracts/VotingEscrow.sol   #7

125:      uint8 constant public decimals = 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L125

File: contracts/contracts/VotingEscrow.sol   #8

1106:     bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1106

File: contracts/contracts/VotingEscrow.sol   #9

1109:     bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1109

File: contracts/contracts/VotingEscrow.sol   #10

1113:     uint public constant MAX_DELEGATES = 1024; // avoid too much gas

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1113

File: contracts/contracts/VeloGovernor.sol   #11

19:       uint256 public constant MAX_PROPOSAL_NUMERATOR = 50; // max 5%

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L19

File: contracts/contracts/VeloGovernor.sol   #12

20:       uint256 public constant PROPOSAL_DENOMINATOR = 1000;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L20

File: contracts/contracts/Velo.sol   #13

6:        string public constant name = "Velodrome";

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L6

File: contracts/contracts/Velo.sol   #14

7:        string public constant symbol = "VELO";

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L7

File: contracts/contracts/Velo.sol   #15

8:        uint8 public constant decimals = 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L8

File: contracts/contracts/factories/PairFactory.sol   #16

14:       uint256 public constant MAX_FEE = 5; // 0.05%

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L14

File: contracts/contracts/Pair.sol   #17

15:       uint8 public constant decimals = 18;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L15

File: contracts/contracts/Pair.sol   #18

18:       bool public immutable stable;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L18

21. Duplicated require()/revert() checks should be refactored to a modifier or function

Saves deployment costs

There are 30 instances of this issue:

File: contracts/contracts/Minter.sol   #1

75:           require(msg.sender == team, "not team");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L75

File: contracts/contracts/Router.sol   #2

356:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L356

File: contracts/contracts/Router.sol   #3

417:          require(token.code.length > 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L417

File: contracts/contracts/Router.sol   #4

420:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L420

File: contracts/contracts/Gauge.sol   #5

189:          require(msg.sender == voter);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L189

File: contracts/contracts/Gauge.sol   #6

592:          require(amount > 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L592

File: contracts/contracts/Gauge.sol   #7

642:              require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L642

File: contracts/contracts/Gauge.sol   #8

654:              require(amount > _left);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L654

File: contracts/contracts/Gauge.sol   #9

635:          require(msg.sender == IGaugeFactory(factory).team(), 'only team');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L635

File: contracts/contracts/Gauge.sol   #10

672:          require(token.code.length > 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L672

File: contracts/contracts/Gauge.sol   #11

675:          require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L675

File: contracts/contracts/VotingEscrow.sol   #12

846:          require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L846

File: contracts/contracts/VotingEscrow.sol   #13

785:          require(_value > 0); // dev: need non-zero value

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L785

File: contracts/contracts/VotingEscrow.sol   #14

820:          require(_locked.amount > 0, 'No existing lock found');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L820

File: contracts/contracts/VotingEscrow.sol   #15

821:          require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L821

File: contracts/contracts/VotingEscrow.sol   #16

837:          require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L837

File: contracts/contracts/VotingEscrow.sol   #17

1065:         require(msg.sender == voter);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1065

File: contracts/contracts/VeloGovernor.sol   #18

45:           require(msg.sender == team, "not team");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L45

File: contracts/contracts/Velo.sol   #19

32:           require(msg.sender == minter);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Velo.sol#L32

File: contracts/contracts/Bribe.sol   #20

69:         require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L69

File: contracts/contracts/Bribe.sol   #21

76:       require(msg.sender == gauge);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L76

File: contracts/contracts/Bribe.sol   #22

100:        require(token.code.length > 0);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L100

File: contracts/contracts/Bribe.sol   #23

103:        require(success && (data.length == 0 || abi.decode(data, (bool))));

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L103

File: contracts/contracts/factories/PairFactory.sol   #24

51:           require(msg.sender == pauser);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L51

File: contracts/contracts/factories/PairFactory.sol   #25

66:           require(msg.sender == feeManager, 'not fee manager');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L66

File: contracts/contracts/Voter.sol   #26

179:          require(msg.sender == governor);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L179

File: contracts/contracts/Voter.sol   #27

218:          require(msg.sender == emergencyCouncil, "not emergency council");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L218

File: contracts/contracts/Voter.sol   #28

232:          require(isGauge[msg.sender]);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L232

File: contracts/contracts/Voter.sol   #29

233:          require(isAlive[msg.sender]);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L233

File: contracts/contracts/Voter.sol   #30

316:          require(isAlive[_gauge]); // killed gauges cannot distribute

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L316

22. Division by two should use bit shifting

<x> / 2 is the same as <x> >> 1. The DIV opcode costs 5 gas, whereas SHR only costs 3 gas

There are 9 instances of this issue:

File: contracts/contracts/Minter.sol   #1

105               (((((_minted * _veTotal) / _veloTotal) * _veTotal) / _veloTotal) *
106                   _veTotal) /
107               _veloTotal /
108:              2;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L105-L108

File: contracts/contracts/Gauge.sol   #2

225:              uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L225

File: contracts/contracts/Gauge.sol   #3

257:              uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L257

File: contracts/contracts/Gauge.sol   #4

289:              uint center = upper - (upper - lower) / 2; // ceil, avoiding overflow

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L289

File: contracts/contracts/VotingEscrow.sol   #5

891:              uint _mid = (_min + _max + 1) / 2;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L891

File: contracts/contracts/VotingEscrow.sol   #6

947:              uint _mid = (_min + _max + 1) / 2;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L947

File: contracts/contracts/VotingEscrow.sol   #7

1171:             uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1171

File: contracts/contracts/RewardsDistributor.sol   #8

107:              uint _mid = (_min + _max + 2) / 2;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L107

File: contracts/contracts/RewardsDistributor.sol   #9

123:              uint _mid = (_min + _max + 2) / 2;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/RewardsDistributor.sol#L123

23. Stack variable used as a cheaper cache for a state variable is only used once

If the variable is only accessed once, it's cheaper to use the state variable directly that one time

There is 1 instance of this issue:

File: contracts/contracts/VotingEscrow.sol   #1

1047:         uint _epoch = epoch;

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1047

24. require() or revert() statements that check input arguments should be at the top of the function

Checks that involve constants should come before checks that involve state variables

There are 13 instances of this issue:

File: contracts/contracts/Minter.sol   #1

76:           require(_teamRate <= MAX_TEAM_RATE, "rate too high");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L76

File: contracts/contracts/Router.sol   #2

341:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L341

File: contracts/contracts/Router.sol   #3

356:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L356

File: contracts/contracts/Router.sol   #4

371:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L371

File: contracts/contracts/Router.sol   #5

384:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L384

File: contracts/contracts/Gauge.sol   #6

628:          require(rewards[i] == oldToken);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L628

File: contracts/contracts/VotingEscrow.sol   #7

772:          require(_value > 0); // dev: need non-zero value

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L772

File: contracts/contracts/VotingEscrow.sol   #8

785:          require(_value > 0); // dev: need non-zero value

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L785

File: contracts/contracts/VotingEscrow.sol   #9

1086:         require(_from != _to);

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1086

File: contracts/contracts/VotingEscrow.sol   #10

1382          require(
1383              nonce == nonces[signatory]++,
1384              "VotingEscrow::delegateBySig: invalid nonce"
1385:         );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1382-L1385

File: contracts/contracts/VeloGovernor.sol   #11

46:           require(numerator <= MAX_PROPOSAL_NUMERATOR, "numerator too high");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L46

File: contracts/contracts/factories/PairFactory.sol   #12

67:           require(_fee <= MAX_FEE, 'fee too high');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L67

File: contracts/contracts/factories/PairFactory.sol   #13

68:           require(_fee != 0, 'fee must be nonzero');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L68

25. Use custom errors rather than revert()/require() strings to save deployment gas

Custom errors are available from solidity version 0.8.4. The instances below match or exceed that version

There are 86 instances of this issue:

File: contracts/contracts/Minter.sol   #1

65:           require(msg.sender == team, "not team");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L65

File: contracts/contracts/Minter.sol   #2

70:           require(msg.sender == pendingTeam, "not pending team");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L70

File: contracts/contracts/Minter.sol   #3

75:           require(msg.sender == team, "not team");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L75

File: contracts/contracts/Minter.sol   #4

76:           require(_teamRate <= MAX_TEAM_RATE, "rate too high");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Minter.sol#L76

File: contracts/contracts/redeem/RedemptionSender.sol   #5

21:           require(_optimismChainId == 11 || _optimismChainId == 10011, "CHAIN_ID_NOT_OP");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L21

File: contracts/contracts/redeem/RedemptionSender.sol   #6

33:           require(amount != 0, "AMOUNT_ZERO");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L33

File: contracts/contracts/redeem/RedemptionSender.sol   #7

34            require(
35                IERC20(weve).transferFrom(
36                    msg.sender,
37                    0x000000000000000000000000000000000000dEaD,
38                    amount
39                ),
40                "WEVE: TRANSFER_FAILED"
41:           );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionSender.sol#L34-L41

File: contracts/contracts/redeem/RedemptionReceiver.sol   #8

24:           require(_fantomChainId == 12 || _fantomChainId == 10012, "CHAIN_ID_NOT_FTM");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L24

File: contracts/contracts/redeem/RedemptionReceiver.sol   #9

43:           require(msg.sender == deployer, "ONLY_DEPLOYER");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L43

File: contracts/contracts/redeem/RedemptionReceiver.sol   #10

44:           require(fantomSender == address(0), "ALREADY_INITIALIZED");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L44

File: contracts/contracts/redeem/RedemptionReceiver.sol   #11

45            require(
46                USDC.transferFrom(msg.sender, address(this), _redeemableUSDC),
47                "USDC_TRANSFER_FAILED"
48:           );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L45-L48

File: contracts/contracts/redeem/RedemptionReceiver.sol   #12

49            require(
50                VELO.mintToRedemptionReceiver(_redeemableVELO),
51                "VELO_MINT_FAILED"
52:           );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L49-L52

File: contracts/contracts/redeem/RedemptionReceiver.sol   #13

78:           require(fantomSender != address(0), "NOT_INITIALIZED");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L78

File: contracts/contracts/redeem/RedemptionReceiver.sol   #14

79            require(
80                msg.sender == endpoint &&
81                    srcChainId == fantomChainId &&
82                    addressFromPackedBytes(srcAddress) == fantomSender,
83                "UNAUTHORIZED_CALLER"
84:           );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L79-L84

File: contracts/contracts/redeem/RedemptionReceiver.sol   #15

91            require(
92                (redeemedWEVE += amountWEVE) <= eligibleWEVE,
93                "cannot redeem more than eligible"
94:           );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L91-L94

File: contracts/contracts/redeem/RedemptionReceiver.sol   #16

97            require(
98                USDC.transfer(redemptionAddress, shareOfUSDC),
99                "USDC_TRANSFER_FAILED"
100:          );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L97-L100

File: contracts/contracts/redeem/RedemptionReceiver.sol   #17

101           require(
102               VELO.transfer(redemptionAddress, shareOfVELO),
103               "VELO_TRANSFER_FAILED"
104:          );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/redeem/RedemptionReceiver.sol#L101-L104

File: contracts/contracts/Router.sol   #18

25:           require(deadline >= block.timestamp, 'Router: EXPIRED');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L25

File: contracts/contracts/Router.sol   #19

40:           require(tokenA != tokenB, 'Router: IDENTICAL_ADDRESSES');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L40

File: contracts/contracts/Router.sol   #20

42:           require(token0 != address(0), 'Router: ZERO_ADDRESS');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L42

File: contracts/contracts/Router.sol   #21

58:           require(amountA > 0, 'Router: INSUFFICIENT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L58

File: contracts/contracts/Router.sol   #22

59:           require(reserveA > 0 && reserveB > 0, 'Router: INSUFFICIENT_LIQUIDITY');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L59

File: contracts/contracts/Router.sol   #23

87:           require(routes.length >= 1, 'Router: INVALID_PATH');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L87

File: contracts/contracts/Router.sol   #24

177:                  require(amountBOptimal >= amountBMin, 'Router: INSUFFICIENT_B_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L177

File: contracts/contracts/Router.sol   #25

182:                  require(amountAOptimal >= amountAMin, 'Router: INSUFFICIENT_A_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L182

File: contracts/contracts/Router.sol   #26

249:          require(amountA >= amountAMin, 'Router: INSUFFICIENT_A_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L249

File: contracts/contracts/Router.sol   #27

250:          require(amountB >= amountBMin, 'Router: INSUFFICIENT_B_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L250

File: contracts/contracts/Router.sol   #28

341:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L341

File: contracts/contracts/Router.sol   #29

356:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L356

File: contracts/contracts/Router.sol   #30

369:          require(routes[0].from == address(weth), 'Router: INVALID_PATH');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L369

File: contracts/contracts/Router.sol   #31

371:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L371

File: contracts/contracts/Router.sol   #32

382:          require(routes[routes.length - 1].to == address(weth), 'Router: INVALID_PATH');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L382

File: contracts/contracts/Router.sol   #33

384:          require(amounts[amounts.length - 1] >= amountOutMin, 'Router: INSUFFICIENT_OUTPUT_AMOUNT');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L384

File: contracts/contracts/Router.sol   #34

406:          require(success, 'TransferHelper: ETH_TRANSFER_FAILED');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Router.sol#L406

File: contracts/contracts/Gauge.sol   #35

594:              require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L594

File: contracts/contracts/Gauge.sol   #36

615:          require(rewardRate[token] <= balance / DURATION, "Provided reward too high");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L615

File: contracts/contracts/Gauge.sol   #37

627:          require(msg.sender == IGaugeFactory(factory).team(), 'only team');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L627

File: contracts/contracts/Gauge.sol   #38

635:          require(msg.sender == IGaugeFactory(factory).team(), 'only team');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L635

File: contracts/contracts/Gauge.sol   #39

642:              require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Gauge.sol#L642

File: contracts/contracts/VotingEscrow.sol   #40

163:          require(idToOwner[_tokenId] != address(0), "Query for nonexistent token");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L163

File: contracts/contracts/VotingEscrow.sol   #41

307:          require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L307

File: contracts/contracts/VotingEscrow.sol   #42

518:          require(_isApprovedOrOwner(msg.sender, _tokenId), "caller is not owner nor approved");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L518

File: contracts/contracts/VotingEscrow.sol   #43

773:          require(_locked.amount > 0, 'No existing lock found');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L773

File: contracts/contracts/VotingEscrow.sol   #44

774:          require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L774

File: contracts/contracts/VotingEscrow.sol   #45

786:          require(unlock_time > block.timestamp, 'Can only lock until time in the future');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L786

File: contracts/contracts/VotingEscrow.sol   #46

787:          require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L787

File: contracts/contracts/VotingEscrow.sol   #47

820:          require(_locked.amount > 0, 'No existing lock found');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L820

File: contracts/contracts/VotingEscrow.sol   #48

821:          require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L821

File: contracts/contracts/VotingEscrow.sol   #49

834:          require(_locked.end > block.timestamp, 'Lock expired');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L834

File: contracts/contracts/VotingEscrow.sol   #50

835:          require(_locked.amount > 0, 'Nothing is locked');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L835

File: contracts/contracts/VotingEscrow.sol   #51

836:          require(unlock_time > _locked.end, 'Can only increase lock duration');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L836

File: contracts/contracts/VotingEscrow.sol   #52

837:          require(unlock_time <= block.timestamp + MAXTIME, 'Voting lock can be 4 years max');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L837

File: contracts/contracts/VotingEscrow.sol   #53

846:          require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L846

File: contracts/contracts/VotingEscrow.sol   #54

849:          require(block.timestamp >= _locked.end, "The lock didn't expire");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L849

File: contracts/contracts/VotingEscrow.sol   #55

1085:         require(attachments[_from] == 0 && !voted[_from], "attached");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1085

File: contracts/contracts/VotingEscrow.sol   #56

1245                  require(
1246                      dstRepOld.length + 1 <= MAX_DELEGATES,
1247                      "dstRep would have too many tokenIds"
1248:                 );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1245-L1248

File: contracts/contracts/VotingEscrow.sol   #57

1315                  require(
1316                      dstRepOld.length + ownerTokenCount <= MAX_DELEGATES,
1317                      "dstRep would have too many tokenIds"
1318:                 );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1315-L1318

File: contracts/contracts/VotingEscrow.sol   #58

1378          require(
1379              signatory != address(0),
1380              "VotingEscrow::delegateBySig: invalid signature"
1381:         );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1378-L1381

File: contracts/contracts/VotingEscrow.sol   #59

1382          require(
1383              nonce == nonces[signatory]++,
1384              "VotingEscrow::delegateBySig: invalid nonce"
1385:         );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1382-L1385

File: contracts/contracts/VotingEscrow.sol   #60

1386          require(
1387              block.timestamp <= expiry,
1388              "VotingEscrow::delegateBySig: signature expired"
1389:         );

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VotingEscrow.sol#L1386-L1389

File: contracts/contracts/VeloGovernor.sol   #61

40:           require(msg.sender == team, "not team");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L40

File: contracts/contracts/VeloGovernor.sol   #62

45:           require(msg.sender == team, "not team");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L45

File: contracts/contracts/VeloGovernor.sol   #63

46:           require(numerator <= MAX_PROPOSAL_NUMERATOR, "numerator too high");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/VeloGovernor.sol#L46

File: contracts/contracts/Bribe.sol   #64

31:       require(gauge == address(0), "gauge already set");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L31

File: contracts/contracts/Bribe.sol   #65

44:           require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L44

File: contracts/contracts/Bribe.sol   #66

69:         require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Bribe.sol#L69

File: contracts/contracts/factories/PairFactory.sol   #67

56:           require(msg.sender == feeManager, 'not fee manager');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L56

File: contracts/contracts/factories/PairFactory.sol   #68

61:           require(msg.sender == pendingFeeManager, 'not pending fee manager');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L61

File: contracts/contracts/factories/PairFactory.sol   #69

66:           require(msg.sender == feeManager, 'not fee manager');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L66

File: contracts/contracts/factories/PairFactory.sol   #70

67:           require(_fee <= MAX_FEE, 'fee too high');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L67

File: contracts/contracts/factories/PairFactory.sol   #71

68:           require(_fee != 0, 'fee must be nonzero');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/factories/PairFactory.sol#L68

File: contracts/contracts/Pair.sol   #72

303:          require(liquidity > 0, 'ILM'); // Pair: INSUFFICIENT_LIQUIDITY_MINTED

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L303

File: contracts/contracts/Pair.sol   #73

322:          require(amount0 > 0 && amount1 > 0, 'ILB'); // Pair: INSUFFICIENT_LIQUIDITY_BURNED

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L322

File: contracts/contracts/Pair.sol   #74

338:          require(amount0Out < _reserve0 && amount1Out < _reserve1, 'IL'); // Pair: INSUFFICIENT_LIQUIDITY

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L338

File: contracts/contracts/Pair.sol   #75

344:          require(to != _token0 && to != _token1, 'IT'); // Pair: INVALID_TO

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L344

File: contracts/contracts/Pair.sol   #76

361:          require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), 'K'); // Pair: K

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L361

File: contracts/contracts/Pair.sol   #77

467:          require(deadline >= block.timestamp, 'Pair: EXPIRED');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L467

File: contracts/contracts/Pair.sol   #78

485:          require(recoveredAddress != address(0) && recoveredAddress == owner, 'Pair: INVALID_SIGNATURE');

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Pair.sol#L485

File: contracts/contracts/Voter.sol   #79

190:          require(gauges[_pool] == address(0x0), "exists");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L190

File: contracts/contracts/Voter.sol   #80

192:              require(IPairFactory(factory).isPair(_pool), "!_pool");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L192

File: contracts/contracts/Voter.sol   #81

194:              require(isWhitelisted[tokenA] && isWhitelisted[tokenB], "!whitelisted");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L194

File: contracts/contracts/Voter.sol   #82

211:          require(msg.sender == emergencyCouncil, "not emergency council");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L211

File: contracts/contracts/Voter.sol   #83

212:          require(isAlive[_gauge], "gauge already dead");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L212

File: contracts/contracts/Voter.sol   #84

218:          require(msg.sender == emergencyCouncil, "not emergency council");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L218

File: contracts/contracts/Voter.sol   #85

219:          require(!isAlive[_gauge], "gauge already alive");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L219

File: contracts/contracts/Voter.sol   #86

318:          require((dayCalc < BRIBE_LAG) || (dayCalc > (DURATION + BRIBE_LAG)), "cannot claim during votes period");

https://github.com/code-423n4/2022-05-velodrome/blob/7fda97c570b758bbfa7dd6724a336c43d4041740/contracts/contracts/Voter.sol#L318

#0 - liveactionllama

2022-05-31T17:18:50Z

Warden created this issue as a placeholder, because their submission was too large for the contest form. They then emailed their md file to our team on 05/29/2022 at 11:37 UTC. I've updated this issue with their md file content.

#1 - GalloDaSballo

2022-06-30T00:17:29Z

The warden could have sent the 5 immutable variables findings and could have probably won.

Just the 5 immutable variables would save 2100 * 5 = 10500 gas for end users by saving at least on SLOAD

Will rate the rest of the savings after reviewing other reports

#2 - GalloDaSballo

2022-07-04T00:18:11Z

1. Multiple address mappings can be combined into a single mapping of an address to a struct, where appropriate

Interesting find that would benefit from further details, especially if some of the Structs could be packed for massive gas savings.

I believe some of these are valuable, especially the more obvious address => uint mappings for which we can save 42 gas from computing storage

17 entries - 4 (that would still be there) 13 * 42 = 546

2. State variables only set in the constructor should be declared immutable

Massive savings, in lack of further details, will give 1 SLOAD per variable 2100 * 5 = 10500

3. State variables can be packed into fewer storage slots

Finding is valid and may save more gas for multi-sig operations, however the lock is not used in conjunction with that slot so it won't save that much gas beside the first write

4. Using calldata instead of memory for read-only arguments in external functions saves gas

Finding is valid and estimate looks right 15 * 60 = 900

5. State variables should be cached in stack variables rather than re-reading them from storage

First read will save 94 gas, second onward will save 97 Giving 94 each for simplicity 39 * 94 = 3666

6. Multiple accesses of a mapping should use a local variable cache

These seem to be memory variables for which the finding will not apply

7. <x> += <y> costs more gas than <x> = <x> + <y> for state variables

Does work when not using unchecked, saves 13 gas per instance 21 * 13 = 273

8. internal functions only called once can be inlined to save gas

Valid, I'd value at 16 gas (2 JUMP) in lack of POC 25 * 16 = 400

9. <array>.length should not be looked up in every loop of a for-loop

Saves 3 gas per instance 17 * 3 = 51

10. ++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops

Valuing at 25 per instance 41 * 25 = 1025

11. require()/revert() strings longer than 32 bytes cost extra gas

6 gas per instance 14 * 6 = 84

12. keccak256() should only need to be called on a specific string literal once

Saves 30 gas

13. Using bools for storage incurs overhead

Pretty good idea for some of the lines, although some are meant to be set only once, hard to quantify in lack of POC as these would mostly be savings for one call

14. Using > 0 costs more gas than != 0 when used on a uint in a require() statement

Not anymore (after 0.8.12)

15. It costs more gas to initialize variables to zero than to let the default of zero be applied

Saves 3 gas per instance 68 * 3 = 204

++i

Already counted above

17. Splitting require() statements that use && saves gas

Valid, saves 3 gas 17 * 3 = 51

18. Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

Cannot quantity in lack of a Coded Alternative

19. abi.encode() is less efficient than abi.encodePacked()

Disagree per the documentation of LZ

22. Division by two should use bit shifting

Saves 2 gas + 20 for unchecked 9 * 22 = 198

23. Stack variable used as a cheaper cache for a state variable is only used once

3 gas

Have ommited 20, 21 because they save gas at deploy time and 24 because I can't quantify in lack of a coded poc

Very thorough report, that I think missed 1 immutable variable, overall great, however some findings would benefit by not pasting the same "mistake" 30+ times

Total Gas Saved 17931

#3 - GalloDaSballo

2022-07-05T00:22:59Z

Winning Report, well played!

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Š 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter