Caviar contest - NoamYakov's results

A fully on-chain NFT AMM that allows you to trade every NFT in a collection.

General Information

Platform: Code4rena

Start Date: 12/12/2022

Pot Size: $36,500 USDC

Total HM: 8

Participants: 103

Period: 7 days

Judge: berndartmueller

Id: 193

League: ETH

Caviar

Findings Distribution

Researcher Performance

Rank: 33/103

Findings: 1

Award: $179.23

Gas:
grade-a

🌟 Selected for report: 0

šŸš€ Solo Findings: 0

Awards

179.2341 USDC - $179.23

Labels

bug
G (Gas Optimization)
grade-a
G-14

External Links

Summary

Gas Optimizations

IssueInstancesTotal Gas Saved
[G‑01]internal functions only called once can be inlined to save gas120
[G‑02]Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement185
[G‑03]++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-loops8480
[G‑04]Functions guaranteed to revert when called by normal users can be marked payable484
[G‑05]State variables should be cached in stack variables rather than re-reading them from storage197
[G‑06]Splitting require() statements that use && saves gas13

Total: 16 instances over 6 issues with 769 gas saved

Gas totals use lower bounds of ranges and count two iterations of each for-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.

Gas Optimizations

[G‑01] 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 is 1 instance of this issue:

File: src/Pair.sol

463:      function _validateTokenIds(uint256[] calldata tokenIds, bytes32[][] calldata proofs) internal view {

https://github.com/code-423n4/2022-12-caviar/blob/main/src/Pair.sol#463

[G‑02] Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement

require(a <= b); x = b - a => require(a <= b); unchecked { x = b - a }

There is 1 instance of this issue:

File: src/Pair.sol

/// @audit require() on line 157
168:              uint256 refundAmount = maxInputAmount - inputAmount;

https://github.com/code-423n4/2022-12-caviar/blob/main/src/Pair.sol#168

[G‑03] ++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 8 instances of this issue:

File: src/Pair.sol

238:          for (uint256 i = 0; i < tokenIds.length; i++) {

258:          for (uint256 i = 0; i < tokenIds.length; i++) {

468:          for (uint256 i = 0; i < tokenIds.length; i++) {

https://github.com/code-423n4/2022-12-caviar/blob/main/src/Pair.sol#238

File: src/lib/SafeERC20Namer.sol

13:           for (uint256 j = 0; j < 32; j++) {

/// @audit for-loop on line 13
17:                   charCount++;

22:           for (uint256 j = 0; j < charCount; j++) {

33:           for (uint256 i = 32; i < 64; i++) {

39:           for (uint256 i = 0; i < charCount; i++) {

https://github.com/code-423n4/2022-12-caviar/blob/main/src/lib/SafeERC20Namer.sol#238

[G‑04] Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost

There are 4 instances of this issue:

File: src/LpToken.sol

19:       function mint(address to, uint256 amount) public onlyOwner {

26:       function burn(address from, uint256 amount) public onlyOwner {

https://github.com/code-423n4/2022-12-caviar/blob/main/src/LpToken.sol#19

File: src/Pair.sol

/// @audit require() on line 343
341:      function close() public {

/// @audit require() on line 361
359:      function withdraw(uint256 tokenId) public {

https://github.com/code-423n4/2022-12-caviar/blob/main/src/Pair.sol#341

[G‑05] 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 replaces 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 is 1 instance of this issue:

File: src/Pair.sol

/// @audit closeTimestamp on line 364
367:          require(block.timestamp >= closeTimestamp, "Not withdrawable yet");

https://github.com/code-423n4/2022-12-caviar/blob/main/src/Pair.sol#367

[G‑06] 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 by 3 gas

There is 1 instance of this issue:

File: src/Pair.sol

71:           require(baseTokenAmount > 0 && fractionalTokenAmount > 0, "Input token amount is zero");

https://github.com/code-423n4/2022-12-caviar/blob/main/src/Pair.sol#71

#0 - c4-judge

2023-01-14T17:17:21Z

berndartmueller marked the issue as grade-b

#1 - noamyakov

2023-01-18T09:02:00Z

My gas report got a grade-b label, while other gas reports which got grade-a labels save less gas than mine in total. I think my report should get a higher score.

#440 is an example of a report that saves less gas but got a higher score than mine.

Moreover, I think my report saves even more gas than #62, the one with the selected-for-report label (the one which got the highest score).

#2 - berndartmueller

2023-01-18T10:20:35Z

@noamyakov I agree! I may have graded your gas report too hasty with the abundance of reports that were submitted. I apologize for not thoroughly reviewing your report and will immediately grade your report as A.

Additionally, I will reconsider my decision for the currently selected gas report.

#3 - c4-judge

2023-01-19T08:19:30Z

berndartmueller marked the issue as grade-a

#4 - berndartmueller

2023-01-19T08:35:28Z

I stand by my decision to select gas report #62 for the report. Your work was exemplary and it was a tough call, however, the report #62 demonstrated a slightly larger amount of gas savings (I assumed almost equal gas savings on GAS-2 and your G‑03 - GAS-2 is just missing 1 instance). Though this was not the selection, your efforts have not gone unnoticed.

Keep up the great work!

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