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
Rank: 33/103
Findings: 1
Award: $179.23
š Selected for report: 0
š Solo Findings: 0
š Selected for report: Rolezn
Also found by: 0x1f8b, 0xAgro, 0xSmartContract, 0xab00, 0xhacksmithh, Aymen0909, Bnke0x0, Breeje, Diana, HardlyCodeMan, IllIllI, JC, JrNet, Madalad, NoamYakov, RaymondFam, ReyAdmirado, SleepingBugs, UdarTeam, c3phas, carlitox477, cryptonue, gz627, lukris02, millersplanet, oyc_109, pavankv, ret2basic, saneryee, tnevler
179.2341 USDC - $179.23
Issue | Instances | Total Gas Saved | |
---|---|---|---|
[Gā01] | internal functions only called once can be inlined to save gas | 1 | 20 |
[Gā02] | Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if -statement | 1 | 85 |
[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 | 8 | 480 |
[Gā04] | Functions guaranteed to revert when called by normal users can be marked payable | 4 | 84 |
[Gā05] | State variables should be cached in stack variables rather than re-reading them from storage | 1 | 97 |
[Gā06] | Splitting require() statements that use && saves gas | 1 | 3 |
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.
internal
functions only called once can be inlined to save gasNot 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
unchecked {}
for subtractions where the operands cannot underflow because of a previous require()
or if
-statementrequire(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
++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
-loopsThe 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
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
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
require()
statements that use &&
saves gasSee 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!