Caviar contest - Aymen0909'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: 72/103

Findings: 1

Award: $14.83

Gas:
grade-b

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Awards

14.833 USDC - $14.83

Labels

bug
G (Gas Optimization)
grade-b
edited-by-warden
G-28

External Links

Gas Optimizations

Summary

IssueInstances
1Use unchecked blocks to save gas1
2internal function _baseTokenReserves() should be called to get the base token reserve instead of baseTokenReserves()4
3x += y/x -= y costs more gas than x = x + y/x = x - y for state variables2
4Splitting require() statements that uses && saves gas1
5public functions not called by the contract should be declared external instead10
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 loops7

Findings

1- Use 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.

2- internal function _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;

3- 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;

4- Splitting 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");

5- 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)

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 :

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

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