Platform: Code4rena
Start Date: 20/01/2023
Pot Size: $90,500 USDC
Total HM: 10
Participants: 59
Period: 7 days
Judge: Picodes
Total Solo HM: 4
Id: 206
League: ETH
Rank: 18/59
Findings: 3
Award: $326.64
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: RaymondFam
Also found by: Rolezn, SaeedAlipoor01988, kaden, mert_eren, nadin, pavankv, rbserver
212.7503 USDC - $212.75
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-option/src/TimeswapV2Option.sol#L220 https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-option/src/TimeswapV2Option.sol#L259 https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-option/src/TimeswapV2Option.sol#L262
Some tokens take a transfer fee (e.g. STA, PAXG), some do not currently charge a fee but may do so in the future (e.g. USDT, USDC).
Should a fee-on-transfer token be used, it could be abused to mint more shares. In the current implementation, TimeswapV2Option.sol#swap()
and TimeswapV2Option.sol#collect()
assume that the received amount is the same as the transfer amount, and uses it to calculate funds. As a result, users will be unable to properly use the functionality of swap
and collect
functions due to how fee-on-transfer tokens work.
220: IERC20(param.isLong0ToLong1 ? token0 : token1).safeTransfer(param.tokenTo, param.isLong0ToLong1 ? token0AndLong0Amount : token1AndLong1Amount);
259: if (token0Amount != 0) IERC20(token0).safeTransfer(param.token0To, token0Amount); 262: if (token1Amount != 0) IERC20(token1).safeTransfer(param.token1To, token1Amount);
#0 - c4-judge
2023-02-02T21:22:48Z
Picodes marked the issue as primary issue
#1 - c4-sponsor
2023-02-08T13:00:55Z
vhawk19 marked the issue as sponsor disputed
#2 - c4-sponsor
2023-02-08T13:36:33Z
vhawk19 marked the issue as sponsor acknowledged
#3 - vhawk19
2023-02-08T13:36:43Z
This is currently not supported by design
#4 - c4-judge
2023-02-12T22:21:28Z
Picodes marked issue #247 as primary and marked this issue as a duplicate of 247
#5 - c4-judge
2023-02-12T22:37:39Z
Picodes marked the issue as satisfactory
🌟 Selected for report: rbserver
Also found by: 0x1f8b, 0xAgro, 0xGusMcCrae, 0xSmartContract, Awesome, Breeje, DadeKuma, Diana, IllIllI, Josiah, Moksha, RaymondFam, Rolezn, SaeedAlipoor01988, Udsen, Viktor_Cortess, brgltd, btk, chaduke, cryptonue, ddimitrov22, delfin454000, descharre, fatherOfBlocks, georgits, hansfriese, lukris02, luxartvinsec, martin, matrix_0wl, mookimgo, oberon, popular00, shark, tnevler
65.3481 USDC - $65.35
Issue | Contexts | |
---|---|---|
LOW‑1 | Low Level Calls With Solidity Version 0.8.14 Can Result In Optimiser Bug | 1 |
LOW‑2 | Use _safeMint instead of _mint | 4 |
LOW‑3 | Missing parameter validation in constructor | 5 |
LOW‑4 | Contracts are not using their OZ Upgradeable counterparts | 9 |
LOW‑5 | Remove unused code | 3 |
Total: 22 contexts over 5 issues
Issue | Contexts | |
---|---|---|
NC‑1 | Add a timelock to critical functions | 1 |
NC‑2 | Avoid Floating Pragmas: The Version Should Be Locked | 13 |
NC‑3 | Critical Changes Should Use Two-step Procedure | 1 |
NC‑4 | Function writing that does not comply with the Solidity Style Guide | 88 |
NC‑5 | Use delete to Clear Variables | 11 |
NC‑6 | NatSpec return parameters should be included in contracts | All in-scope contracts |
NC‑7 | Lines are too long | 10 |
NC‑8 | Implementation contract may not be initialized | 8 |
NC‑9 | NatSpec comments should be increased in contracts | All in-scope contracts |
NC‑10 | Use a more recent version of Solidity | 88 |
Total: 396 contexts over 10 issues
The project contracts in scope are using low level calls with solidity version before 0.8.14 which can result in optimizer bug. https://medium.com/certora/overly-optimistic-optimizer-certora-bug-disclosure-2101e3f7994d
Simliar findings in Code4rena contests for reference: https://code4rena.com/reports/2022-06-illuminate/#5-low-level-calls-with-solidity-version-0814-can-result-in-optimiser-bug
POC can be found in the above medium reference url.
Functions that execute low level calls in contracts with solidity version under 0.8.14
18: assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } }
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/BytesLib.sol#L18
Consider upgrading to at least solidity v0.8.15.
_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-
128: _mint(param.to, id, param.liquidityAmount, bytes(""));
110: _mint(param.long0To, id, (param.long0Amount), bytes(""));
139: _mint(param.long1To, id, (param.long1Amount), bytes(""));
168: _mint(param.shortTo, id, (param.shortAmount), bytes(""));
Use _safeMint
whenever possible instead of _mint
constructor
Some parameters of constructors are not checked for invalid values.
37: address chosenOwner
18: address chosenOwner
36: address chosenOptionFactory 36: address chosenPoolFactory
41: address chosenOptionFactory
Validate the parameters.
The non-upgradeable standard version of OpenZeppelin’s library are inherited / used by the contracts. It would be safer to use the upgradeable versions of the library contracts to avoid unexpected behaviour.
4: import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5: import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
4: import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5: import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
4: import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
4: import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
6: import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
6: import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
4: import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
Where applicable, use the contracts from @openzeppelin/contracts-upgradeable
instead of @openzeppelin/contracts
.
This code is not used in the main project contract files, remove it or add event-emit Code that is not in use, suggests that they should not be present and could potentially contain insecure functionalities.
function invalidTransaction
function addFees
function _afterTokenTransfer
It is a good practice to give time for users to react and adjust to critical changes. A timelock provides more guarantees and reduces the level of trust required, thus decreasing risk for users. It also indicates that the project is legitimate (less risk of a malicious owner making a sandwich attack on a user). Consider adding a timelock to the following functions:
23: function setPendingOwner(address chosenPendingOwner) external override {
Avoid floating pragmas for non-library contracts.
While floating pragmas make sense for libraries to allow them to be included with multiple different versions of applications, it may be a security risk for application implementations.
A known vulnerable compiler version may accidentally be selected or security tools might fall-back to an older compiler version ending up checking a different EVM compilation that is ultimately deployed on the blockchain.
It is recommended to pin to a concrete compiler version.
Found usage of floating pragmas ^0.8.8 of Solidity in [TimeswapV2LiquidityToken.sol]
Found usage of floating pragmas ^0.8.8 of Solidity in [TimeswapV2Token.sol]
Found usage of floating pragmas ^0.8.0 of Solidity in [ERC1155Enumerable.sol]
Found usage of floating pragmas ^0.8.0 of Solidity in [IERC1155Enumerable.sol]
Found usage of floating pragmas ^0.8.8 of Solidity in [CallbackParam.sol]
Found usage of floating pragmas ^0.8.8 of Solidity in [CallbackParam.sol]
Found usage of floating pragmas ^0.8.8 of Solidity in [CallbackParam.sol]
Found usage of floating pragmas ^0.8.8 of Solidity in [FeesPosition.sol]
Found usage of floating pragmas ^0.8.8 of Solidity in [Param.sol]
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L2
Found usage of floating pragmas ^0.8.8 of Solidity in [Param.sol]
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L2
Found usage of floating pragmas ^0.8.8 of Solidity in [Param.sol]
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L2
Found usage of floating pragmas ^0.8.8 of Solidity in [Position.sol]
Found usage of floating pragmas ^0.8.8 of Solidity in [Position.sol]
The critical procedures should be two step process.
See similar findings in previous Code4rena contests for reference: https://code4rena.com/reports/2022-06-illuminate/#2-critical-changes-should-use-two-step-procedure
23: function setPendingOwner(address chosenPendingOwner) external override {
Lack of two-step procedure for critical operations leaves them error-prone. Consider adding two step procedure on the critical functions.
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
delete
to Clear Variablesdelete a
assigns the initial value for the type to a
. i.e. for integers it is equivalent to a = 0
, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset. Similarly, it can also be used to set an address to zero address. It has no effect on whole mappings though (as the keys of mappings may be arbitrary and are generally unknown). However, individual keys and what they map to can be deleted: If a
is a mapping, then delete a[x]
will delete the value stored at x
.
The delete
key better conveys the intention and is also more idiomatic. Consider replacing assignments of zero with delete
statements.
166: quotient0 = 0;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/FullMath.sol#L166
93: liquidityPosition.long0Fees = 0; 101: liquidityPosition.long1Fees = 0; 109: liquidityPosition.shortFees = 0;
228: pool.long0ProtocolFees = 0; 236: pool.long1ProtocolFees = 0; 244: pool.shortProtocolFees = 0;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L228
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L236
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L244
633: pool.long0Balance = 0; 646: pool.long1Balance = 0;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L633
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L646
685: pool.long1Balance = 0; 706: pool.long0Balance = 0;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L685
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L706
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 amount0, uint256 amount1);
Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. Since the files will most likely reside in GitHub, and GitHub starts using a scroll bar in all cases when the length is over 164 characters, the lines below should be split when they reach that length Reference: https://docs.soliditylang.org/en/v0.8.10/style-guide.html#maximum-line-length
62: /// @param amount If isLong0ToLong1 and transaction is GivenToken0AndLong0, this is the amount of token0 withdrawn, and the amount of long0 position burnt.
63: /// If isLong1ToLong0 and transaction is GivenToken0AndLong0, this is the amount of token0 to be deposited, and the amount of long0 position minted.
64: /// If isLong0ToLong1 and transaction is GivenToken1AndLong1, this is the amount of token1 to be deposited, and the amount of long1 position minted.
62: /// @param amount If isLong0ToLong1 and transaction is GivenToken0AndLong0, this is the amount of token0 withdrawn, and the amount of long0 position burnt.
63: /// If isLong1ToLong0 and transaction is GivenToken0AndLong0, this is the amount of token0 to be deposited, and the amount of long0 position minted.
64: /// If isLong0ToLong1 and transaction is GivenToken1AndLong1, this is the amount of token1 to be deposited, and the amount of long1 position minted.
62: /// @param amount If isLong0ToLong1 and transaction is GivenToken0AndLong0, this is the amount of token0 withdrawn, and the amount of long0 position burnt.
63: /// If isLong1ToLong0 and transaction is GivenToken0AndLong0, this is the amount of token0 to be deposited, and the amount of long0 position minted.
64: /// If isLong0ToLong1 and transaction is GivenToken1AndLong1, this is the amount of token1 to be deposited, and the amount of long1 position minted.
87: /// @dev Calculate the amount of liquidity positions and amount of long positions in base denomination or short position whichever is larger or smaller.
OpenZeppelin recommends that the initializer modifier be applied to constructors. Per OZs Post implementation contract should be initialized to avoid potential griefs or exploits. https://forum.openzeppelin.com/t/uupsupgradeable-vulnerability-post-mortem/15680/5
19: constructor()
65: constructor() NoDelegateCall()
77: constructor() NoDelegateCall()
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/TimeswapV2Pool.sol#L77
37: constructor(address chosenOwner, uint256 chosenTransactionFee, uint256 chosenProtocolFee) OwnableTwoSteps(chosenOwner)
18: constructor(address chosenOwner)
36: constructor(address chosenOptionFactory, address chosenPoolFactory) ERC1155("Timeswap V2 uint160 address")
41: constructor(address chosenOptionFactory) ERC1155("Timeswap V2 address")
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
NatSpec comments should be increased in contracts
<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.
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/BytesLib.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/CatchError.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/Error.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/FullMath.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/Math.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/Ownership.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/SafeCast.sol#L2
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-option/src/structs/Param.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-option/src/structs/Param.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-option/src/structs/Param.sol#L2
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/NoDelegateCall.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/NoDelegateCall.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/TimeswapV2Pool.sol#L2
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/libraries/Fee.sol#L2
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Param.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Param.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Param.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L2
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
pragma solidity ^0.8.0;
pragma solidity ^0.8.0;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L2
pragma solidity ^0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L2
pragma solidity ^0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L2
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
Consider updating to a more recent solidity version.
#0 - c4-judge
2023-02-02T11:48:23Z
Picodes marked the issue as grade-b
🌟 Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0xackermann, Aymen0909, Beepidibop, IllIllI, Iurii3, Rageur, RaymondFam, ReyAdmirado, Rolezn, SaeedAlipoor01988, Udsen, Viktor_Cortess, W0RR1O, W_Max, atharvasama, c3phas, chaduke, descharre, fatherOfBlocks, kaden, matrix_0wl, shark
48.5424 USDC - $48.54
Issue | Contexts | Estimated Gas Saved | |
---|---|---|---|
GAS‑1 | abi.encode() is less efficient than abi.encodepacked() | 9 | 900 |
GAS‑2 | Setting the constructor to payable | 10 | 130 |
GAS‑3 | Using delete statement can save gas | 11 | - |
GAS‑4 | Use hardcoded address instead address(this) | 35 | - |
GAS‑5 | Multiple Address Mappings Can Be Combined Into A Single Mapping Of An Address To A Struct, Where Appropriate | 3 | - |
GAS‑6 | Optimize names to save gas | 14 | 308 |
GAS‑7 | <x> += <y> Costs More Gas Than <x> = <x> + <y> For State Variables | 73 | - |
GAS‑8 | Public Functions To External | 1 | - |
GAS‑9 | Usage of uints /ints smaller than 32 bytes (256 bits) incurs overhead | 85 | - |
GAS‑10 | Use solidity version 0.8.17 to gain some gas boost | 68 | 5984 |
GAS‑11 | Using storage instead of memory saves gas | 5 | 1750 |
Total: 314 contexts over 11 issues
abi.encode()
is less efficient than abi.encodepacked()
See for more information: https://github.com/ConnorBlockchain/Solidity-Encode-Gas-Comparison
35: optionPair = address(new TimeswapV2Option{salt: keccak256(abi.encode(token0, token1))}());
28: poolPair = address(new TimeswapV2Pool{salt: keccak256(abi.encode(optionPair))}());
46: bytes32 key = keccak256(abi.encode(token0, token1, strike, maturity));
53: bytes32 key = keccak256(abi.encode(token0, token1, strike, maturity));
61: bytes32 key = keccak256(abi.encode(token0, token1, strike, maturity));
35: return keccak256(abi.encode(timeswapV2TokenPosition));
40: return keccak256(abi.encode(timeswapV2LiquidityTokenPosition));
35: return keccak256(abi.encode(timeswapV2TokenPosition));
40: return keccak256(abi.encode(timeswapV2LiquidityTokenPosition));
constructor
to payable
Saves ~13 gas per instance
19: constructor()
19: constructor()
65: constructor() NoDelegateCall()
19: constructor()
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/NoDelegateCall.sol#L19
19: constructor()
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/NoDelegateCall.sol#L19
77: constructor() NoDelegateCall()
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/TimeswapV2Pool.sol#L77
37: constructor(address chosenOwner, uint256 chosenTransactionFee, uint256 chosenProtocolFee) OwnableTwoSteps(chosenOwner)
18: constructor(address chosenOwner)
36: constructor(address chosenOptionFactory, address chosenPoolFactory) ERC1155("Timeswap V2 uint160 address")
41: constructor(address chosenOptionFactory) ERC1155("Timeswap V2 address")
delete
statement can save gas166: quotient0 = 0;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/FullMath.sol#L166
93: liquidityPosition.long0Fees = 0; 101: liquidityPosition.long1Fees = 0; 109: liquidityPosition.shortFees = 0;
228: pool.long0ProtocolFees = 0; 236: pool.long1ProtocolFees = 0; 244: pool.shortProtocolFees = 0;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L228
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L236
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L244
633: pool.long0Balance = 0; 646: pool.long1Balance = 0;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L633
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L646
685: pool.long1Balance = 0; 706: pool.long0Balance = 0;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L685
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L706
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
30: if (address(this) != original) revert CannotBeDelegateCalled();
30: if (address(this) != original) revert CannotBeDelegateCalled();
128: IERC20(token0).balanceOf(address(this)) + token0AndLong0Amount, 129: IERC20(token1).balanceOf(address(this)) + token1AndLong1Amount 145: if (token0AndLong0Amount != 0) Error.checkEnough(IERC20(token0).balanceOf(address(this)), currentProcess.balance0Target); 148: if (token1AndLong1Amount != 0) Error.checkEnough(IERC20(token1).balanceOf(address(this)), currentProcess.balance1Target);
215: param.isLong0ToLong1 ? IERC20(token0).balanceOf(address(this)) - token0AndLong0Amount : IERC20(token0).balanceOf(address(this)) + token0AndLong0Amount, 216: param.isLong0ToLong1 ? IERC20(token1).balanceOf(address(this)) + token1AndLong1Amount : IERC20(token1).balanceOf(address(this)) - token1AndLong1Amount 235: Error.checkEnough(IERC20(param.isLong0ToLong1 ? token1 : token0).balanceOf(address(this)), param.isLong0ToLong1 ? currentProcess.balance1Target : currentProcess.balance0Target);
51: optionPair = deploy(address(this), token0, token1);
30: if (address(this) != original) revert CannotBeDelegateCalled();
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/NoDelegateCall.sol#L30
30: if (address(this) != original) revert CannotBeDelegateCalled();
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/NoDelegateCall.sol#L30
267: if (long0Amount != 0) long0BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0) + long0Amount; 271: if (long1Amount != 0) long1BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1) + long1Amount; 274: uint256 shortBalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short) + shortAmount; 293: if (long0Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0), long0BalanceTarget); 295: if (long1Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1), long1BalanceTarget); 297: Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short), shortBalanceTarget);
393: if (long0Amount != 0) long0BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0) + long0Amount; 397: if (long1Amount != 0) long1BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1) + long1Amount; 411: if (long0Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0), long0BalanceTarget); 413: if (long1Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1), long1BalanceTarget);
444: uint256 balanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short) + shortAmount; 461: Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short), balanceTarget);
482: address(this), 511: ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), param.isLong0ToLong1 ? TimeswapV2OptionPosition.Long0 : TimeswapV2OptionPosition.Long1),
65: pair = deploy(address(this), optionPair, transactionFee, protocolFee);
125: uint160 liquidityBalanceTarget = ITimeswapV2Pool(poolPair).liquidityOf(param.strike, param.maturity, address(this)) + param.liquidityAmount; 143: Error.checkEnough(ITimeswapV2Pool(poolPair).liquidityOf(param.strike, param.maturity, address(this)), liquidityBalanceTarget);
87: long0BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0) + param.long0Amount; 117: long1BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1) + param.long1Amount; 146: shortBalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short) + param.shortAmount; 186: if (param.long0Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0), long0BalanceTarget); 189: if (param.long1Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1), long1BalanceTarget); 192: if (param.shortAmount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short), shortBalanceTarget);
Use hardcoded address
Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot.
23: mapping(address => uint256) long0;
24: mapping(address => uint256) long1;
25: mapping(address => uint256) short;
For the above, can create a struct for example:
struct structShortLong{ uint256 long0; uint256 long1; uint256 short; } ### <a href="#Summary">[GAS‑6]</a><a name="GAS‑6"> Optimize names to save gas 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> #### <ins>Proof Of Concept</ins> All in-scope contracts #### <ins>Recommended Mitigation Steps</ins> 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. ### <a href="#Summary">[GAS‑7]</a><a name="GAS‑7"> `<x> += <y>` Costs More Gas Than `<x> = <x> + <y>` For State Variables #### <ins>Proof Of Concept</ins> ```solidity 163: productA1 += (quotient1 * divisor);
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/FullMath.sol#L163
190: option.long0[msg.sender] -= token0AndLong0Amount; 191: option.long1[msg.sender] -= token1AndLong1Amount; 192: option.short[msg.sender] -= shortAmount;
237: if (param.isLong0ToLong1) option.long0[msg.sender] -= token0AndLong0Amount; 238: else option.long1[msg.sender] -= token1AndLong1Amount;
277: option.short[msg.sender] -= shortAmount;
62: option.long0[msg.sender] -= amount; 63: option.long0[to] += amount; 65: option.long1[to] += amount; 66: option.long1[msg.sender] -= amount; 68: option.short[msg.sender] -= amount; 69: option.short[to] += amount;
103: option.totalLong0 += token0AndLong0Amount; 104: option.long0[long0To] += token0AndLong0Amount; 108: option.totalLong1 += token1AndLong1Amount; 109: option.long1[long1To] += token1AndLong1Amount; 112: option.short[shortTo] += shortAmount;
141: option.totalLong0 -= token0AndLong0Amount; 142: option.totalLong1 -= token1AndLong1Amount;
173: option.totalLong0 -= token0AndLong0Amount; 174: option.totalLong1 += token1AndLong1Amount; 175: option.long1[longTo] += token1AndLong1Amount; 177: option.totalLong1 -= token1AndLong1Amount; 178: option.totalLong0 += token0AndLong0Amount; 179: option.long0[longTo] += token0AndLong0Amount;
208: option.totalLong0 -= token0Amount; 209: option.totalLong1 -= token1Amount;
149: if (isAdd) longAmount -= fees; 150: else shortAmount -= fees;
180: shortAmount -= fees;
212: longAmount -= fees;
244: amount -= fees;
295: denominator2 += longAmount;
55: liquidityPosition.long0Fees += FeeCalculation.getFees(liquidity, liquidityPosition.long0FeeGrowth, long0FeeGrowth); 56: liquidityPosition.long1Fees += FeeCalculation.getFees(liquidity, liquidityPosition.long1FeeGrowth, long1FeeGrowth); 57: liquidityPosition.shortFees += FeeCalculation.getFees(liquidity, liquidityPosition.shortFeeGrowth, shortFeeGrowth);
66: liquidityPosition.liquidity += liquidityAmount;
70: liquidityPosition.long0Fees += long0Fees; 71: liquidityPosition.long1Fees += long1Fees; 72: liquidityPosition.shortFees += shortFees;
76: liquidityPosition.liquidity -= liquidityAmount;
80: liquidityPosition.long0Fees -= long0Fees; 81: liquidityPosition.long1Fees -= long1Fees; 82: liquidityPosition.shortFees -= shortFees;
361: if (long0Amount != 0) pool.long0Balance += long0Amount; 362: if (long1Amount != 0) pool.long1Balance += long1Amount; 365: pool.liquidity += liquidityAmount;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L361
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L362
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L365
452: if (long0Amount != 0) pool.long0Balance -= long0Amount; 453: if (long1Amount != 0) pool.long1Balance -= long1Amount; 455: pool.liquidity -= liquidityAmount;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L452
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L453
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L455
537: if (long0Amount != 0) pool.long0Balance += long0Amount; 538: if (long1Amount != 0) pool.long1Balance += long1Amount;
636: pool.long0Balance -= (long0Amount + long0Fees); 650: pool.long1Balance -= (long1Amount + long1Fees);
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L636
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L650
677: pool.long1Balance -= (long1Amount + longFees); 677: pool.long1Balance -= (long1Amount + longFees); 695: pool.long0Balance += long0Amount; 710: pool.long0Balance -= (long0Amount + longFees); 710: pool.long0Balance -= (long0Amount + longFees); 722: pool.long1Balance += long1Amount;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L677
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L677
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L695
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L710
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L710
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L722
61: _idTotalSupply[id] += amount;
95: _idTotalSupply[id] -= amount;
117: _currentIndex[to] += 1;
31: feesPosition.long0Fees += FeeCalculation.getFees(liquidity, feesPosition.long0FeeGrowth, long0FeeGrowth); 32: feesPosition.long1Fees += FeeCalculation.getFees(liquidity, feesPosition.long1FeeGrowth, long1FeeGrowth); 33: feesPosition.shortFees += FeeCalculation.getFees(liquidity, feesPosition.shortFeeGrowth, shortFeeGrowth);
53: feesPosition.long0Fees += long0Fees; 54: feesPosition.long1Fees += long1Fees; 55: feesPosition.shortFees += shortFees;
59: feesPosition.long0Fees -= long0Fees; 60: feesPosition.long1Fees -= long1Fees; 61: feesPosition.shortFees -= shortFees;
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 positionOf(address owner, TimeswapV2TokenPosition calldata timeswapV2TokenPosition) public view returns (uint256 amount) {
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
205: uint160 deltaRate;
12: uint96 internal constant NOT_INTERACTED = 0;
15: uint96 internal constant NOT_ENTERED = 1;
18: uint96 internal constant ENTERED = 2;
72: uint160 liquidityAmount;
72: uint160 liquidityAmount;
72: uint160 liquidityAmount;
16: uint160 liquidity;
52: uint160 liquidity = liquidityPosition.liquidity;
42: uint160 liquidity;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L42
43: uint96 lastTimestamp;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L43
44: uint160 sqrtInterestRate;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L44
125: uint160 liquidityBalanceTarget = ITimeswapV2Pool(poolPair).liquidityOf(param.strike, param.maturity, address(this)) + param.liquidityAmount;
70: uint160 liquidityAmount;
70: uint160 liquidityAmount;
70: uint160 liquidityAmount;
90: uint160 liquidityAmount;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L90
90: uint160 liquidityAmount;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L90
90: uint160 liquidityAmount;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L90
Upgrade to the latest solidity version 0.8.17 to get additional gas savings.
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/BytesLib.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/CatchError.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/Error.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/FullMath.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/Math.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/Ownership.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-library/src/SafeCast.sol#L2
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-option/src/structs/Param.sol#L2
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/NoDelegateCall.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/TimeswapV2Pool.sol#L2
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/libraries/Fee.sol#L2
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Param.sol#L2
pragma solidity =0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-pool/src/structs/Pool.sol#L2
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
pragma solidity ^0.8.0;
pragma solidity ^0.8.0;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity =0.8.8;
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
pragma solidity ^0.8.8;
https://github.com/code-423n4/2023-01-timeswap/tree/main/packages/v2-token/src/structs/Param.sol#L2
pragma solidity ^0.8.8;
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
238: TimeswapV2LiquidityTokenPosition memory timeswapV2LiquidityTokenPosition = _timeswapV2LiquidityTokenPositions[id]; 264: TimeswapV2LiquidityTokenPosition memory timeswapV2LiquidityTokenPosition = _timeswapV2LiquidityTokenPositions[id];
238: TimeswapV2LiquidityTokenPosition memory timeswapV2LiquidityTokenPosition = _timeswapV2LiquidityTokenPositions[id]; 264: TimeswapV2LiquidityTokenPosition memory timeswapV2LiquidityTokenPosition = _timeswapV2LiquidityTokenPositions[id]; 275: FeesPosition memory feesPosition = _feesPositions[id][owner];
#0 - c4-judge
2023-02-02T12:41:17Z
Picodes marked the issue as grade-c
#1 - c4-judge
2023-02-02T12:41:30Z
Picodes marked the issue as grade-b