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: 72/103
Findings: 1
Award: $14.83
π 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
14.833 USDC - $14.83
Issue | Instances | |
---|---|---|
1 | Use unchecked blocks to save gas | 1 |
2 | internal function _baseTokenReserves() should be called to get the base token reserve instead of baseTokenReserves() | 4 |
3 | x += y/x -= y costs more gas than x = x + y/x = x - y for state variables | 2 |
4 | Splitting require() statements that uses && saves gas | 1 |
5 | public functions not called by the contract should be declared external instead | 10 |
6 | ++i/i++ should be unchecked{++i} /unchecked{i++} when it is not possible for them to overflow, as in the case when used in for & while loops | 7 |
unchecked
blocks to save gas :Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isnβt possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block.
There is 1 instance of this issue:
File: src/Pair.sol Line 157
uint256 refundAmount = maxInputAmount - inputAmount;
The above operation cannot underflow because of the check :
File: src/Pair.sol Line 168
require(inputAmount <= maxInputAmount, "Slippage: amount in");
So the operation should be marked unchecked.
_baseTokenReserves()
should be called to get the base token reserve instead of baseTokenReserves()
:When a function inside the Pair contract need to get the amount of base token reserve it should call the internal function _baseTokenReserves()
instead of the public one baseTokenReserves()
, this will save around 30-40 gas (on average) for each function call.
Additionaly the baseTokenReserves()
function could be marked external
to save further gas.
There are 4 instances of this issue :
File: src/Pair.sol Line 399
return (outputAmount * 1000 * baseTokenReserves()) / ((fractionalTokenReserves() - outputAmount) * 997);
File: src/Pair.sol Line 408
return (inputAmountWithFee * baseTokenReserves()) / ((fractionalTokenReserves() * 1000) + inputAmountWithFee);
File: src/Pair.sol Line 421
uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves();
File: src/Pair.sol Line 437
uint256 baseTokenOutputAmount = (baseTokenReserves() * lpTokenAmount) / lpTokenSupply;
x += y/x -= y
costs more gas than x = x + y/x = x - y
for state variables :Using the addition operator instead of plus-equals saves 113 gas as explained here
There are 2 instances of this issue:
File: src/Pair.sol Line 448
balanceOf[from] -= amount;
File: src/Pair.sol Line 453
balanceOf[to] += amount;
require()
statements that uses && saves gas :There is 1 instance of this issue :
File: src/Pair.sol Line 71
require(baseTokenAmount > 0 && fractionalTokenAmount > 0, "Input token amount is zero");
public
functions not called by the contract should be declared external
instead :The public
functions that are not called inside the contract should be declared external
instead to save gas.
There are 10 instances of this issue:
File: src/Caviar.sol Line 28
function create(address nft, address baseToken, bytes32 merkleRoot) public returns (Pair pair)
File: src/Caviar.sol Line 49
function destroy(address nft, address baseToken, bytes32 merkleRoot) public
File: src/LpToken.sol Line 19
function mint(address to, uint256 amount) public
File: src/LpToken.sol Line 26
function mint(address to, uint256 amount) public
File: src/Pair.sol Line 275
function nftAdd( uint256 baseTokenAmount, uint256[] calldata tokenIds, uint256 minLpTokenAmount, bytes32[][] calldata proofs ) public payable returns (uint256 lpTokenAmount)
File: src/Pair.sol Line 294
function nftRemove(uint256 lpTokenAmount, uint256 minBaseTokenOutputAmount, uint256[] calldata tokenIds) public
File: src/Pair.sol Line 310
function nftBuy(uint256[] calldata tokenIds, uint256 maxInputAmount) public payable returns (uint256 inputAmount) public
File: src/Pair.sol Line 323
function nftSell(uint256[] calldata tokenIds, uint256 minOutputAmount, bytes32[][] calldata proofs) public
File: src/Pair.sol Line 341
function close() public
File: src/Pair.sol Line 359
function withdraw(uint256 tokenId) public
File: src/Pair.sol Line 390
function price() public view returns (uint256)
++i/i++
should be unchecked{++i}/unchecked{i++}
when it is not possible for them to overflow, as in the case when used in for & while loops :There are 7 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++) File: contracts/Lock.sol 12 for (uint256 j = 0; j < 32; j++) 22 for (uint256 j = 0; j < charCount; j++) 33 for (uint256 i = 32; i < 64; i++) 39 for (uint256 i = 0; i < charCount; i++)
#0 - c4-judge
2023-01-14T17:13:07Z
berndartmueller marked the issue as grade-b