Platform: Code4rena
Start Date: 26/01/2023
Pot Size: $60,500 USDC
Total HM: 7
Participants: 31
Period: 6 days
Judge: berndartmueller
Total Solo HM: 3
Id: 207
League: ETH
Rank: 19/31
Findings: 2
Award: $187.91
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: CodingNameKiki
Also found by: 0xAgro, 0xSmartContract, IllIllI, Rolezn, SleepingBugs, btk, chrisdior4, matrix_0wl
142.4841 USDC - $142.48
Issue | Contexts | |
---|---|---|
LOW‑1 | Possible rounding issue | 1 |
LOW‑2 | Use _safeMint instead of _mint | 1 |
LOW‑3 | Missing parameter validation in constructor | 9 |
Total: 27 contexts over 6 issues
Issue | Contexts | |
---|---|---|
NC‑1 | Compliance with Solidity Style rules in Constant expressions | 3 |
NC‑2 | Duplicated require() /revert() Checks Should Be Refactored To A Modifier Or Function | 2 |
NC‑3 | Function writing that does not comply with the Solidity Style Guide | All in-scope contracts |
NC‑4 | NatSpec return parameters should be included in contracts | All in-scope contracts |
NC‑5 | NatSpec comments should be increased in contracts | All in-scope contracts |
NC‑6 | Use a more recent version of Solidity | 15 |
NC‑7 | Use bytes.concat() | 2 |
NC‑8 | Use of Block.Timestamp | 2 |
Total: 138 contexts over 18 issues
There might be a rounding issue. For example, if totalLiquidity
is 20*1e18
and borrowedLiquidity
is 19
, would lead to return 0
value
42: return (borrowedLiquidity * 1e18) / totalLiquidity;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/JumpRate.sol#L42
_safeMint
instead of _mint
According to openzepplin's ERC721, the use of _mint
is discouraged, use _safeMint whenever possible.
https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#ERC721-_mint-address-uint256-
93: _mint(to, shares);
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L93
Use _safeMint
whenever possible instead of _mint
constructor
Some parameters of constructors are not checked for invalid values.
50: address _factory 51: address _uniswapV2Factory 52: address _uniswapV3Factory 53: address _weth
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L50-L53
75: address _factory 75: address _weth
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L75-L75
17: address _weth
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L17
29: address _uniswapV2Factory 29: address _uniswapV3Factory
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/SwapHelper.sol#L29-L29
Validate the parameters.
7: uint256 public constant override kink = 0.8 ether;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/JumpRate.sol#L7
9: uint256 public constant override multiplier = 1.375 ether;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/JumpRate.sol#L9
11: uint256 public constant override jumpMultiplier = 44.5 ether;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/JumpRate.sol#L11
Variables are declared as constant utilize the UPPER_CASE_WITH_UNDERSCORES format.
require()
/revert()
Checks Should Be Refactored To A Modifier Or FunctionSaves deployment costs
61: require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); 79: require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
Order of Functions; ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. But there are contracts in the project that do not comply with this.
https://docs.soliditylang.org/en/v0.8.17/style-guide.html
Functions should be grouped according to their visibility and ordered:
All in-scope contracts
It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI). It is clearly stated in the Solidity official documentation. In complex projects such as Defi, the interpretation of all functions and their arguments and returns is important for code readability and auditability.
https://docs.soliditylang.org/en/v0.8.15/natspec-format.html
All in-scope contracts
Include return parameters in NatSpec comments
Recommendation Code Style: (from Uniswap3)
/// @notice Adds liquidity for the given recipient/tickLower/tickUpper position /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends /// on tickLower, tickUpper, the amount of liquidity, and the current price. /// @param recipient The address for which the liquidity will be created /// @param tickLower The lower tick of the position in which to add liquidity /// @param tickUpper The upper tick of the position in which to add liquidity /// @param amount The amount of liquidity to mint /// @param data Any data that should be passed through to the callback /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback function mint( address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data ) external returns (uint256 amount ### <a href="#Summary">[NC‑5]</a><a name="NC‑5"> NatSpec comments should be increased in contracts It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI). It is clearly stated in the Solidity official documentation. In complex projects such as Defi, the interpretation of all functions and their arguments and returns is important for code readability and auditability. https://docs.soliditylang.org/en/v0.8.15/natspec-format.html #### <ins>Proof Of Concept</ins> All in-scope contracts #### <ins>Recommended Mitigation Steps</ins> NatSpec comments should be increased in contracts ### <a href="#Summary">[NC‑6]</a><a name="NC‑6"> Use a more recent version of Solidity <a href="https://blog.soliditylang.org/2021/04/21/solidity-0.8.4-release-announcement/">0.8.4</a>: bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>) <a href="https://blog.soliditylang.org/2022/02/16/solidity-0.8.12-release-announcement/">0.8.12</a>: string.concat() instead of abi.encodePacked(<str>,<str>) <a href="https://blog.soliditylang.org/2022/03/16/solidity-0.8.13-release-announcement/">0.8.13</a>: Ability to use using for with a list of free functions <a href="https://blog.soliditylang.org/2022/05/18/solidity-0.8.14-release-announcement/">0.8.14</a>: ABI Encoder: When ABI-encoding values from calldata that contain nested arrays, correctly validate the nested array length against calldatasize() in all cases. Override Checker: Allow changing data location for parameters only when overriding external functions. <a href="https://blog.soliditylang.org/2022/06/15/solidity-0.8.15-release-announcement/">0.8.15</a>: Code Generation: Avoid writing dirty bytes to storage when copying bytes arrays. Yul Optimizer: Keep all memory side-effects of inline assembly blocks. <a href="https://blog.soliditylang.org/2022/08/08/solidity-0.8.16-release-announcement/">0.8.16</a>: Code Generation: Fix data corruption that affected ABI-encoding of calldata values represented by tuples: structs at any nesting level; argument lists of external functions, events and errors; return value lists of external functions. The 32 leading bytes of the first dynamically-encoded value in the tuple would get zeroed when the last component contained a statically-encoded array. <a href="https://blog.soliditylang.org/2022/09/08/solidity-0.8.17-release-announcement/">0.8.17</a>: Yul Optimizer: Prevent the incorrect removal of storage writes before calls to Yul functions that conditionally terminate the external EVM call. #### <ins>Proof Of Concept</ins> ```solidity pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Factory.sol#L2
pragma solidity ^0.8.0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/ImmutableState.sol#L2
pragma solidity ^0.8.0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/JumpRate.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/libraries/Position.sol#L2
pragma solidity >=0.5.0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/libraries/PositionMath.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/libraries/Balance.sol#L2
pragma solidity ^0.8.0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/libraries/SafeCast.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/SwapHelper.sol#L2
pragma solidity >=0.5.0;
pragma solidity >=0.8.0;
Consider updating to a more recent solidity version.
bytes.concat()
Solidity version 0.8.4 introduces bytes.concat()
(vs abi.encodePacked(<bytes>,<bytes>)
)
25: abi.encodePacked( hex"ff", factory, keccak256(abi.encode(token0, token1, token0Exp, token1Exp, upperBound)
23: abi.encodePacked( hex"ff", factory, keccak256(abi.encodePacked(token0, token1)
Use bytes.concat()
and upgrade to at least Solidity version 0.8.4 if required.
Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts. References: SWC ID: 116
66: if (deadline < block.timestamp) revert LivelinessError();
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L66
84: if (deadline < block.timestamp) revert LivelinessError();
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L84
Block timestamps should not be used for entropy or generating random numbers—i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.
Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.
#0 - c4-judge
2023-02-16T11:40:58Z
berndartmueller marked the issue as grade-b
🌟 Selected for report: NoamYakov
Also found by: 0xSmartContract, 0xackermann, Aymen0909, Deivitto, Diana, IllIllI, RaymondFam, ReyAdmirado, Rolezn, antonttc, arialblack14, c3phas, cryptostellar5, matrix_0wl, nadin, oyc_109
45.4256 USDC - $45.43
Issue | Contexts | Estimated Gas Saved | |
---|---|---|---|
GAS‑1 | abi.encode() is less efficient than abi.encodepacked() | 6 | 600 |
GAS‑2 | Setting the constructor to payable | 5 | 65 |
GAS‑3 | Duplicated require() /revert() Checks Should Be Refactored To A Modifier Or Function | 2 | 56 |
GAS‑4 | Use assembly to write address storage values | 15 | - |
GAS‑5 | Use hardcoded address instead address(this) | 16 | - |
GAS‑6 | Optimize names to save gas | 9 | 198 |
GAS‑7 | <x> += <y> Costs More Gas Than <x> = <x> + <y> For State Variables | 10 | - |
GAS‑8 | Public Functions To External | 8 | - |
GAS‑9 | Splitting require() Statements That Use && Saves Gas | 2 | 18 |
GAS‑10 | Usage of uints /ints smaller than 32 bytes (256 bits) incurs overhead | 25 | - |
GAS‑11 | Using unchecked blocks to save gas | 3 | 408 |
GAS‑12 | Use solidity version 0.8.17 to gain some gas boost | 15 | 1320 |
GAS‑13 | Using storage instead of memory saves gas | 4 | 1400 |
Total: 159 contexts over 22 issues
abi.encode()
is less efficient than abi.encodepacked()
See for more information: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison
82: lendgine = address(new Lendgine{ salt: keccak256(abi.encode(token0, token1, token0Exp, token1Exp, upperBound)) }());
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Factory.sol#L82
150: abi.encode(
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L150
268: abi.encode(
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L268
160: abi.encode(
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L160
108: abi.encode(params.tokenIn)
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/SwapHelper.sol#L108
28: keccak256(abi.encode(token0, token1, token0Exp, token1Exp, upperBound)),
constructor
to payable
Saves ~13 gas per instance
27: constructor()
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/ImmutableState.sol#L27
49: constructor( address _factory, address _uniswapV2Factory, address _uniswapV3Factory, address _weth ) SwapHelper(_uniswapV2Factory, _uniswapV3Factory) Payment(_weth)
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L49
75: constructor(address _factory, address _weth) Payment(_weth)
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L75
17: constructor(address _weth)
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L17
29: constructor(address _uniswapV2Factory, address _uniswapV3Factory)
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/SwapHelper.sol#L29
require()
/revert()
Checks Should Be Refactored To A Modifier Or FunctionSaves deployment costs
61: require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); 79: require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
assembly
to write address storage values135: _totalPositionSize = totalPositionSize; // SLOAD
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L135
163: _totalPositionSize = totalPositionSize; // SLOAD
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L163
164: _totalLiquidity = totalLiquidity; // SLOAD
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L164
214: _totalLiquidityBorrowed = totalLiquidityBorrowed; // SLOAD
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L214
247: _totalLiquidityBorrowed = totalLiquidityBorrowed; // SLOAD
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L247
267: _rewardPerPositionStored = rewardPerPositionStored; // SLOAD
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L267
73: _totalLiquidity = totalLiquidity; // SLOAD
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L73
96: _totalLiquidity = totalLiquidity; // SLOAD
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L96
47: _positionInfo = positionInfo;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/libraries/Position.sol#L47
address(this)
Instead of using address(this)
, it is more gas-efficient to pre-calculate and use the hardcoded address
. Foundry's script.sol and solmate's LibRlp.sol
contracts can help achieve this.
References: https://book.getfoundry.sh/reference/forge-std/compute-create-address
https://twitter.com/transmissions11/status/1518507047943245824
108: uint256 shares = balanceOf[address(this)]; 115: _burn(address(this), shares);
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L108
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L115
14: token.staticcall(abi.encodeWithSelector(bytes4(keccak256(bytes("balanceOf(address)"))), address(this)));
https://github.com/code-423n4/2023-01-numoen/tree/main/src/libraries/Balance.sol#L14
148: address(this),
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L148
235: if (decoded.recipient != address(this)) {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L235
262: address recipient = params.recipient == address(0) ? address(this) : params.recipient; 267: address(this),
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L262
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L267
158: address(this), 177: (, uint256 rewardPerPositionPaid,) = ILendgine(lendgine).positions(address(this));
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L158
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L177
206: address recipient = params.recipient == address(0) ? address(this) : params.recipient; 213: (, uint256 rewardPerPositionPaid,) = ILendgine(lendgine).positions(address(this));
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L206
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L213
233: address recipient = params.recipient == address(0) ? address(this) : params.recipient; 237: (, uint256 rewardPerPositionPaid,) = ILendgine(params.lendgine).positions(address(this));
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L233
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L237
45: if (address(this).balance > 0) SafeTransferLib.safeTransferETH(msg.sender, address(this).balance);
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L45
53: if (token == weth && address(this).balance >= value) { 57: } else if (payer == address(this)) {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L53
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L57
Use hardcoded address
Contracts most called functions could simply save gas by function ordering via Method ID. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.
See more <a href="https://medium.com/joyso/solidity-how-does-function-name-affect-gas-consumption-in-smart-contract-47d270d8ac92">here</a>
All in-scope contracts
Find a lower method ID name for the most called functions for example Call() vs. Call1() is cheaper by 22 gas For example, the function IDs in the Gauge.sol contract will be the most used; A lower method ID may be given.
<x> += <y>
Costs More Gas Than <x> = <x> + <y>
For State Variables91: totalLiquidityBorrowed += liquidity;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L91
114: totalLiquidityBorrowed -= liquidity;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L114
176: totalPositionSize -= size;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L176
257: rewardPerPositionStored += FullMath.mulDiv(dilutionSpeculative, 1e18, totalPositionSize);
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L257
178: position.tokensOwed += FullMath.mulDiv(position.size, rewardPerPositionPaid - position.rewardPerPositionPaid, 1e18); 180: position.size += size;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L178
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L180
214: position.tokensOwed += FullMath.mulDiv(position.size, rewardPerPositionPaid - position.rewardPerPositionPaid, 1e18); 216: position.size -= params.size;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L214
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L216
238: position.tokensOwed += FullMath.mulDiv(position.size, rewardPerPositionPaid - position.rewardPerPositionPaid, 1e18); 242: position.tokensOwed -= amount;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L238
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L242
The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.
function getBorrowRate(uint256 borrowedLiquidity, uint256 totalLiquidity) public pure override returns (uint256 rate) {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/JumpRate.sol#L13
function convertLiquidityToShare(uint256 liquidity) public view override returns (uint256) {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L213
function convertShareToLiquidity(uint256 shares) public view override returns (uint256) {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L219
function convertCollateralToLiquidity(uint256 collateral) public view override returns (uint256) {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L224
function convertLiquidityToCollateral(uint256 liquidity) public view override returns (uint256) {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L229
function invariant(uint256 amount0, uint256 amount1, uint256 liquidity) public view override returns (bool) {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L53
function unwrapWETH(uint256 amountMinimum, address recipient) public payable {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L25
function sweepToken(address token, uint256 amountMinimum, address recipient) public payable {
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L35
require()
statements that use &&
saves gasInstead of using operator &&
on a single require
. Using a two require
can save more gas.
i.e.
for require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
use:
require(reserveIn > 0); require(reserveOut > 0);
61: require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
79: require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
uints
/ints
smaller than 32 bytes (256 bits) incurs overheadWhen 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
Each operation involving a uint8
costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8
) as compared to ones involving uint256
, due to the compiler having to clear the higher bits of the memory word before operating on the uint8
, as well as the associated stack operations of doing so. Use a larger size then downcast where needed
50: uint128 token0Exp;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Factory.sol#L50
51: uint128 token1Exp;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Factory.sol#L51
30: uint128 _token0Exp;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/ImmutableState.sol#L30
31: uint128 _token1Exp;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/ImmutableState.sol#L31
40: uint120 public override reserve0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L40
43: uint120 public override reserve1;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L43
119: uint120 _reserve0 = reserve0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L119
120: uint120 _reserve1 = reserve1;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L120
62: uint24 fee;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/SwapHelper.sol#L62
unchecked
blocks to save gasSolidity 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
14: require((z = x - uint256(-y)) < x, "LS");
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/libraries/PositionMath.sol#L14
107: zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/SwapHelper.sol#L107
81: uint256 denominator = (reserveOut - amountOut) * 997;
Upgrade to the latest solidity version 0.8.17 to get additional gas savings.
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Factory.sol#L2
pragma solidity ^0.8.0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/ImmutableState.sol#L2
pragma solidity ^0.8.0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/JumpRate.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Pair.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/libraries/Position.sol#L2
pragma solidity >=0.5.0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/libraries/PositionMath.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/libraries/Balance.sol#L2
pragma solidity ^0.8.0;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/libraries/SafeCast.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LendgineRouter.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/Payment.sol#L2
pragma solidity ^0.8.4;
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/SwapHelper.sol#L2
pragma solidity >=0.5.0;
pragma solidity >=0.8.0;
storage
instead of memory
saves gasWhen fetching data from a storage
location, assigning the data to a memory
variable causes all fields of the struct/array to be read from storage
, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory
variable, they incur an additional MLOAD rather than a cheap stack read. Instead of declearing the variable with the memory keyword, declaring the variable with the storage
keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory
variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory, or if the array/struct is being read from another memory
array/struct
167: Position.Info memory positionInfo = positions[msg.sender];
https://github.com/code-423n4/2023-01-numoen/tree/main/src/core/Lendgine.sol#L167
175: Position memory position = positions[params.recipient][lendgine];
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L175
211: Position memory position = positions[msg.sender][lendgine];
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L211
235: Position memory position = positions[msg.sender][params.lendgine];
https://github.com/code-423n4/2023-01-numoen/tree/main/src/periphery/LiquidityManager.sol#L235
#0 - c4-judge
2023-02-16T11:11:46Z
berndartmueller marked the issue as grade-b