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: 14/59
Findings: 2
Award: $886.34
š Selected for report: 0
š Solo Findings: 0
š 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
837.804 USDC - $837.80
Issue | Instances | |
---|---|---|
[Lā01] | Vulnerable versions of packages are being used | 1 |
Total: 1 instances over 1 issues
Issue | Instances | |
---|---|---|
[Nā01] | Import declarations should import specific identifiers, rather than the whole file | 2 |
[Nā02] | Unused file | 3 |
[Nā03] | Remove import for forge-std | 1 |
[Nā04] | public functions not called by the contract should be declared external instead | 1 |
[Nā05] | constant s should be defined rather than using magic numbers | 51 |
[Nā06] | Events that mark critical parameter changes should contain both the old and the new value | 1 |
[Nā07] | Use a more recent version of solidity | 18 |
[Nā08] | Constant redefined elsewhere | 2 |
[Nā09] | Inconsistent spacing in comments | 5 |
[Nā10] | Lines are too long | 103 |
[Nā11] | Inconsistent method of specifying a floating pragma | 6 |
[Nā12] | Non-library/interface files should use fixed compiler versions, not floating ones | 2 |
[Nā13] | Typos | 77 |
[Nā14] | File is missing NatSpec | 3 |
[Nā15] | NatSpec is incomplete | 63 |
[Nā16] | Not using the named return variables anywhere in the function is confusing | 55 |
[Nā17] | Consider using delete rather than assigning zero to clear values | 10 |
[Nā18] | Contracts should have full test coverage | 1 |
[Nā19] | Large or complicated code bases should implement fuzzing tests | 1 |
[Nā20] | Function ordering does not follow the Solidity style guide | 18 |
[Nā21] | Contract does not follow the Solidity style guide's suggested layout ordering | 3 |
Total: 426 instances over 21 issues
This project's specific package versions are vulnerable to the specific CVEs listed below. Consider switching to more recent versions of these packages that don't have these vulnerabilities
There is 1 instance of this issue:
File: Various Files /// @audit Vulnerabilities: ///
ECDSA.recover
and ECDSA.tryRecover
are vulnerable to a kind of signature malleability due to accepting EIP-2098 compact signatures in addition to the traditional 65 byte signature format. This is only an issue for the functions that take a single bytes
argument, and not the functions that take r, v, s
or r, vs
as separate arguments. The potentially affected contracts are those that implement signature reuse or replay protection by marking the signature itself as used rather than the signed message or a nonce included in it. A user may take a signature that has already been submitted, submit it again in a different form, and bypass this protection. The issue has been patched in 4.7.3. (@openzeppelin/contracts >=4.1.0 <4.7.3)Using import declarations of the form import {<identifier_name>} from "some/file.sol"
avoids polluting the symbol namespace making flattened files smaller, and speeds up compilation
There are 2 instances of this issue:
File: packages/v2-token/src/interfaces/IERC1155Enumerable.sol 6: import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
File: packages/v2-token/src/TimeswapV2Token.sol 5: import "forge-std/console.sol";
The file is never imported by any other source file. If the file is needed for tests, it should be moved to a test directory
There are 3 instances of this issue:
File: packages/v2-library/src/CatchError.sol 1: //SPDX-License-Identifier: Unlicense
File: packages/v2-pool/src/libraries/Fee.sol 1: //SPDX-License-Identifier: Unlicense
File: packages/v2-pool/src/libraries/PoolPair.sol 1: //SPDX-License-Identifier: Unlicense
import
for forge-stdTest code should not be mixed in with production code. The production version should be extended and have its functions overridden for testing purposes
There is 1 instance of this issue:
File: packages/v2-token/src/TimeswapV2Token.sol 5: import "forge-std/console.sol";
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents' functions and change the visibility from external
to public
.
There is 1 instance of this issue:
File: packages/v2-token/src/TimeswapV2Token.sol 66: function positionOf(address owner, TimeswapV2TokenPosition calldata timeswapV2TokenPosition) public view returns (uint256 amount) {
constant
s should be defined rather than using magic numbersEven assembly can benefit from using readable constants instead of hex/numeric literals
There are 51 instances of this issue:
File: packages/v2-library/src/BytesLib.sol /// @audit 31 13: require(_length + 31 >= _length, "slice_overflow"); /// @audit 0x40 23: tempBytes := mload(0x40) /// @audit 31 33: let lengthmod := and(_length, 31) /// @audit 0x20 39: let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) /// @audit 0x20 45: let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) /// @audit 0x20 47: mc := add(mc, 0x20) /// @audit 0x20 48: cc := add(cc, 0x20) /// @audit 0 20: case 0 { /// @audit 0x40 61: tempBytes := mload(0x40) /// @audit 0x40 /// @audit 0x20 66: mstore(0x40, add(tempBytes, 0x20))
File: packages/v2-library/src/CatchError.sol /// @audit 4 /// @audit 32 /// @audit 4 /// @audit 4 15: if ((length - 4) % 32 == 0 && bytes4(reason) == selector) return BytesLib.slice(reason, 4, length - 4);
File: packages/v2-library/src/FullMath.sol /// @audit 0 79: let mm := mulmod(multiplicand, multiplier, not(0)) /// @audit 1 93: quotient := add(div(sub(0, divisor), divisor), 1) /// @audit 1 226: twos := add(div(sub(0, twos), twos), 1) /// @audit 3 236: inv = (3 * divisor) ^ 2;
File: packages/v2-library/src/StrikeConversion.sol /// @audit 128 /// @audit 128 17: return zeroToOne ? FullMath.mulDiv(amount, strike, uint256(1) << 128, roundUp) : FullMath.mulDiv(amount, uint256(1) << 128, strike, roundUp);
File: packages/v2-option/src/enums/Position.sol /// @audit 3 23: if (uint256(position) >= 3) revert InvalidPosition();
File: packages/v2-option/src/enums/Transaction.sol /// @audit 3 55: if (uint256(transaction) >= 3) revert InvalidTransaction();
File: packages/v2-pool/src/enums/Transaction.sol /// @audit 4 53: if (uint256(transaction) >= 4) revert InvalidTransaction(); /// @audit 4 58: if (uint256(transaction) >= 4) revert InvalidTransaction(); /// @audit 4 63: if (uint256(transaction) >= 4) revert InvalidTransaction(); /// @audit 4 68: if (uint256(transaction) >= 4) revert InvalidTransaction();
File: packages/v2-pool/src/libraries/ConstantProduct.sol /// @audit 96 30: return (uint256(liquidity) << 96).div(rate, roundUp); /// @audit 192 39: return FullMath.mulDiv(uint256(liquidity).unsafeMul(duration), uint256(rate), uint256(1) << 192, roundUp); /// @audit 96 260: return FullMath.mulDiv(uint256(rate), longAmount, uint256(1) << 96, roundUp).toUint160(); /// @audit 192 269: return FullMath.mulDiv(shortAmount, uint256(1) << 192, uint256(rate).unsafeMul(duration), roundUp).toUint160(); /// @audit 96 280: numerator = uint256(liquidity) << 96; /// @audit 192 316: deltaRate = FullMath.mulDiv(shortAmount, uint256(1) << 192, denominator, !isAdd).toUint160(); /// @audit 96 332: numerator = uint256(liquidity) << 96; /// @audit 192 344: return FullMath.mulDiv(uint256(numerator), uint256(deltaRate), uint256(1) << 192, roundUp); /// @audit 16 374: uint256 adjustment = (uint256(1) << 16).unsafeSub(transactionFee); /// @audit 112 378: ? FullMath.mulDiv(liquidity, uint256(1) << 112, uint256(rate).unsafeMul(adjustment), false) /// @audit 176 379: : FullMath.mulDiv(uint256(liquidity).unsafeMul(duration), rate, (uint256(1) << 176).unsafeMul(adjustment), false); /// @audit 16 380: uint256 negativeB2 = FullMath.mulDiv(sumAmount, uint256(1) << 16, adjustment, false); /// @audit 174 /// @audit 16 /// @audit 16 404: uint256 denominator = isShort ? (uint256(1) << 174).unsafeMul((uint256(1) << 16).unsafeSub(transactionFee)) : uint256(rate).unsafeMul((uint256(1) << 16).unsafeSub(transactionFee)); /// @audit 114 406: (uint256 a0, uint256 a1) = isShort ? FullMath.mul512(uint256(liquidity).unsafeMul(duration), rate) : FullMath.mul512(liquidity, uint256(1) << 114);
File: packages/v2-pool/src/libraries/FeeCalculation.sol /// @audit 128 41: return globalFeeGrowth != lastFeeGrowth ? FullMath.mulDiv(liquidity, uint256(1) << 128, globalFeeGrowth.unsafeSub(lastFeeGrowth), false) : 0; /// @audit 16 /// @audit 16 48: return FullMath.mulDiv(amount, (uint256(1) << 16), (uint256(1) << 16).unsafeSub(fee), true); /// @audit 16 /// @audit 16 55: return FullMath.mulDiv(amount, (uint256(1) << 16).unsafeSub(fee), uint256(1) << 16, false); /// @audit 16 62: return FullMath.mulDiv(amount, fee, uint256(1) << 16, true); /// @audit 16 69: return FullMath.mulDiv(amount, fee, (uint256(1) << 16).unsafeSub(fee), true); /// @audit 128 76: return FullMath.mulDiv(feeAmount, uint256(1) << 128, liquidity, false);
This should especially be done if the new value is not required to be different from the old value
There is 1 instance of this issue:
File: packages/v2-pool/src/base/OwnableTwoSteps.sol /// @audit setPendingOwner() 31: emit SetOwner(pendingOwner);
Use a solidity version of at least 0.8.13 to get the ability to use using for
with a list of free functions
There are 18 instances of this issue:
File: packages/v2-library/src/FullMath.sol 2: pragma solidity =0.8.8;
File: packages/v2-option/src/libraries/OptionFactory.sol 2: pragma solidity =0.8.8;
File: packages/v2-option/src/structs/Option.sol 2: pragma solidity =0.8.8;
File: packages/v2-option/src/TimeswapV2OptionFactory.sol 2: pragma solidity =0.8.8;
File: packages/v2-option/src/TimeswapV2Option.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/base/OwnableTwoSteps.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/libraries/ConstantProduct.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/libraries/ConstantSum.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/libraries/DurationCalculation.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/libraries/DurationWeight.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/libraries/FeeCalculation.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/libraries/PoolFactory.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/structs/LiquidityPosition.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/structs/Pool.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/TimeswapV2PoolFactory.sol 2: pragma solidity =0.8.8;
File: packages/v2-pool/src/TimeswapV2Pool.sol 2: pragma solidity =0.8.8;
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol 2: pragma solidity ^0.8.8;
File: packages/v2-token/src/TimeswapV2Token.sol 2: pragma solidity ^0.8.8;
Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant
in a library
. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.
There are 2 instances of this issue:
File: packages/v2-pool/src/TimeswapV2Pool.sol /// @audit seen in packages/v2-pool/src/TimeswapV2PoolFactory.sol 48: uint256 public immutable override transactionFee; /// @audit seen in packages/v2-pool/src/TimeswapV2PoolFactory.sol 50: uint256 public immutable override protocolFee;
Some lines use // x
and some use //x
. The instances below point out the usages that don't follow the majority, within each file
There are 5 instances of this issue:
File: packages/v2-library/src/BytesLib.sol 55: //update free-memory pointer 56: //allocating the array padded to 32 bytes like the compiler does now 59: //if we want a zero-length slice let's just return a zero-length array 62: //zero out the 32 bytes slice we are about to return 63: //we need to do it because Solidity does not garbage collect
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
There are 103 instances of this issue:
File: packages/v2-option/src/interfaces/ITimeswapV2Option.sol 159: function mint(TimeswapV2OptionMintParam calldata param) external returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount, bytes memory data); 169: function burn(TimeswapV2OptionBurnParam calldata param) external returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount, bytes memory data); 190: function collect(TimeswapV2OptionCollectParam calldata param) external returns (uint256 token0Amount, uint256 token1Amount, uint256 shortAmount, bytes memory data);
File: packages/v2-option/src/structs/Option.sol 95: if (transaction == TimeswapV2OptionMint.GivenTokensAndLongs) shortAmount = StrikeConversion.combine(token0AndLong0Amount = amount0, token1AndLong1Amount = amount1, strike, false); 134: if (transaction == TimeswapV2OptionBurn.GivenTokensAndLongs) shortAmount = StrikeConversion.combine(token0AndLong0Amount = amount0, token1AndLong1Amount = amount1, strike, true); 191: function collect(Option storage option, uint256 strike, TimeswapV2OptionCollect transaction, uint256 amount) internal returns (uint256 token0Amount, uint256 token1Amount, uint256 shortAmount) {
File: packages/v2-option/src/TimeswapV2Option.sol 27: import {TimeswapV2OptionMintCallbackParam, TimeswapV2OptionBurnCallbackParam, TimeswapV2OptionSwapCallbackParam, TimeswapV2OptionCollectCallbackParam} from "./structs/CallbackParam.sol"; 118: (token0AndLong0Amount, token1AndLong1Amount, shortAmount) = option.mint(param.strike, param.long0To, param.long1To, param.shortTo, param.transaction, param.amount0, param.amount1); 198: function swap(TimeswapV2OptionSwapParam calldata param) external override noDelegateCall returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, bytes memory data) { 235: Error.checkEnough(IERC20(param.isLong0ToLong1 ? token1 : token0).balanceOf(address(this)), param.isLong0ToLong1 ? currentProcess.balance1Target : currentProcess.balance0Target); 246: function collect(TimeswapV2OptionCollectParam calldata param) external override noDelegateCall returns (uint256 token0Amount, uint256 token1Amount, uint256 shortAmount, bytes memory data) {
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolBurnCallback.sol 13: function timeswapV2PoolBurnChoiceCallback(TimeswapV2PoolBurnChoiceCallbackParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolDeleverageCallback.sol 14: function timeswapV2PoolDeleverageChoiceCallback(TimeswapV2PoolDeleverageChoiceCallbackParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolLeverageCallback.sol 14: function timeswapV2PoolLeverageChoiceCallback(TimeswapV2PoolLeverageChoiceCallbackParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolMintCallback.sol 14: function timeswapV2PoolMintChoiceCallback(TimeswapV2PoolMintChoiceCallbackParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);
File: packages/v2-pool/src/interfaces/ITimeswapV2Pool.sol 6: import {TimeswapV2PoolCollectParam, TimeswapV2PoolMintParam, TimeswapV2PoolBurnParam, TimeswapV2PoolDeleverageParam, TimeswapV2PoolLeverageParam, TimeswapV2PoolRebalanceParam} from "../structs/Param.sol"; 81: event Mint(uint256 indexed strike, uint256 indexed maturity, address indexed caller, address to, uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount); 115: event Deleverage(uint256 indexed strike, uint256 indexed maturity, address indexed caller, address to, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount); 126: event Leverage(uint256 indexed strike, uint256 indexed maturity, address indexed caller, address long0To, address long1To, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount); 138: event Rebalance(uint256 indexed strike, uint256 indexed maturity, address indexed caller, address to, bool isLong0ToLong1, uint256 long0Amount, uint256 long1Amount); 204: function protocolFeesEarned(uint256 strike, uint256 maturity) external view returns (uint256 long0ProtocolFees, uint256 long1ProtocolFees, uint256 shortProtocolFees); 280: function mint(TimeswapV2PoolMintParam calldata param) external returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data); 307: function burn(TimeswapV2PoolBurnParam calldata param) external returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data); 333: function deleverage(TimeswapV2PoolDeleverageParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data); 344: function deleverage(TimeswapV2PoolDeleverageParam calldata param, uint96 durationForward) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data); 353: function leverage(TimeswapV2PoolLeverageParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data); 364: function leverage(TimeswapV2PoolLeverageParam calldata param, uint96 durationForward) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);
File: packages/v2-pool/src/libraries/ConstantProduct.sol 51: function calculateGivenLiquidityDelta(uint160 rate, uint160 deltaLiquidity, uint96 duration, bool isAdd) internal pure returns (uint256 longAmount, uint256 shortAmount) { 66: function calculateGivenLiquidityLong(uint160 rate, uint256 longAmount, uint96 duration, bool isAdd) internal pure returns (uint160 liquidityAmount, uint256 shortAmount) { 81: function calculateGivenLiquidityShort(uint160 rate, uint256 shortAmount, uint96 duration, bool isAdd) internal pure returns (uint160 liquidityAmount, uint256 longAmount) { 313: function getNewSqrtInterestRateGivenShort(uint160 liquidity, uint160 rate, uint256 shortAmount, uint96 duration, bool isAdd) private pure returns (uint160 newRate, uint160 deltaRate) { 334: return roundUp ? FullMath.mulDiv(numerator, deltaRate, uint256(rate), true).div(newRate, true) : FullMath.mulDiv(numerator, deltaRate, uint256(newRate), false).div(rate, false); 356: function getShortOrLongFromGivenSum(uint160 liquidity, uint160 rate, uint256 sumAmount, uint96 duration, uint256 transactionFee, bool isShort) private pure returns (uint256 amount) { 373: function calculateNegativeB(uint160 liquidity, uint160 rate, uint256 sumAmount, uint96 duration, uint256 transactionFee, bool isShort) private pure returns (uint256 negativeB) { 404: uint256 denominator = isShort ? (uint256(1) << 174).unsafeMul((uint256(1) << 16).unsafeSub(transactionFee)) : uint256(rate).unsafeMul((uint256(1) << 16).unsafeSub(transactionFee));
File: packages/v2-pool/src/libraries/ConstantSum.sol 19: function calculateGivenLongIn(uint256 strike, uint256 longAmountIn, uint256 transactionFee, bool isLong0ToLong1) internal pure returns (uint256 longAmountOut, uint256 longFees) { 30: function calculateGivenLongOut(uint256 strike, uint256 longAmountOut, uint256 transactionFee, bool isLong0ToLong1) internal pure returns (uint256 longAmountIn, uint256 longFees) {
File: packages/v2-pool/src/libraries/FeeCalculation.sol 27: function update(uint160 liquidity, uint256 feeGrowth, uint256 protocolFees, uint256 fees, uint256 protocolFee) internal pure returns (uint256 newFeeGrowth, uint256 newProtocolFees) {
File: packages/v2-pool/src/structs/Param.sol 6: import {TimeswapV2PoolMint, TimeswapV2PoolBurn, TimeswapV2PoolDeleverage, TimeswapV2PoolLeverage, TimeswapV2PoolRebalance, TransactionLibrary} from "../enums/Transaction.sol";
File: packages/v2-pool/src/structs/Pool.sol 25: import {TimeswapV2PoolMint, TimeswapV2PoolBurn, TimeswapV2PoolDeleverage, TimeswapV2PoolLeverage, TimeswapV2PoolRebalance, TransactionLibrary} from "../enums/Transaction.sol"; 27: import {TimeswapV2PoolCollectParam, TimeswapV2PoolMintParam, TimeswapV2PoolBurnParam, TimeswapV2PoolDeleverageParam, TimeswapV2PoolLeverageParam, TimeswapV2PoolRebalanceParam} from "./Param.sol"; 28: import {TimeswapV2PoolMintChoiceCallbackParam, TimeswapV2PoolBurnChoiceCallbackParam, TimeswapV2PoolDeleverageChoiceCallbackParam, TimeswapV2PoolLeverageChoiceCallbackParam} from "./CallbackParam.sol"; 103: shortAmount = ConstantProduct.getShort(pool.liquidity, pool.sqrtInterestRate, DurationCalculation.unsafeDurationFromNowToMaturity(maturity, blockTimestamp), false); 183: function transferFees(Pool storage pool, uint256 maturity, address to, uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint96 blockTimestamp) external { 533: TimeswapV2PoolDeleverageChoiceCallbackParam({strike: param.strike, maturity: param.maturity, longAmount: longAmount, shortAmount: shortAmount, data: param.data}) 639: (pool.long0FeeGrowth, pool.long0ProtocolFees) = FeeCalculation.update(pool.liquidity, pool.long0FeeGrowth, pool.long0ProtocolFees, long0Fees, protocolFee); 653: (pool.long1FeeGrowth, pool.long1ProtocolFees) = FeeCalculation.update(pool.liquidity, pool.long1FeeGrowth, pool.long1ProtocolFees, long1Fees, protocolFee); 665: function rebalance(Pool storage pool, TimeswapV2PoolRebalanceParam memory param, uint256 transactionFee, uint256 protocolFee) external returns (uint256 long0Amount, uint256 long1Amount) { 697: (pool.long1FeeGrowth, pool.long1ProtocolFees) = FeeCalculation.update(pool.liquidity, pool.long1FeeGrowth, pool.long1ProtocolFees, longFees, protocolFee); 724: (pool.long0FeeGrowth, pool.long0ProtocolFees) = FeeCalculation.update(pool.liquidity, pool.long0FeeGrowth, pool.long0ProtocolFees, longFees, protocolFee);
File: packages/v2-pool/src/TimeswapV2Pool.sol 31: import {TimeswapV2PoolCollectParam, TimeswapV2PoolMintParam, TimeswapV2PoolBurnParam, TimeswapV2PoolDeleverageParam, TimeswapV2PoolLeverageParam, TimeswapV2PoolRebalanceParam, ParamLibrary} from "./structs/Param.sol"; 32: import {TimeswapV2PoolMintChoiceCallbackParam, TimeswapV2PoolMintCallbackParam, TimeswapV2PoolBurnChoiceCallbackParam, TimeswapV2PoolBurnCallbackParam, TimeswapV2PoolDeleverageChoiceCallbackParam, TimeswapV2PoolDeleverageCallbackParam, TimeswapV2PoolLeverageCallbackParam, TimeswapV2PoolLeverageChoiceCallbackParam, TimeswapV2PoolRebalanceCallbackParam} from "./structs/CallbackParam.sol"; 34: import {TimeswapV2PoolMint, TimeswapV2PoolBurn, TimeswapV2PoolDeleverage, TimeswapV2PoolLeverage, TimeswapV2PoolRebalance, TransactionLibrary} from "./enums/Transaction.sol"; 123: function feesEarnedOf(uint256 strike, uint256 maturity, address owner) external view override returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees) { 128: function protocolFeesEarned(uint256 strike, uint256 maturity) external view override returns (uint256 long0ProtocolFees, uint256 long1ProtocolFees, uint256 shortProtocolFees) { 184: function collectProtocolFees(TimeswapV2PoolCollectParam calldata param) external override noDelegateCall returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount) { 192: (long0Amount, long1Amount, shortAmount) = pools[param.strike][param.maturity].collectProtocolFees(param.long0Requested, param.long1Requested, param.shortRequested); 202: function collectTransactionFees(TimeswapV2PoolCollectParam calldata param) external override noDelegateCall returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount) { 231: function collect(uint256 strike, uint256 maturity, address long0To, address long1To, address shortTo, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount) private { 240: function mint(TimeswapV2PoolMintParam calldata param) external override returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { 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); 305: function burn(TimeswapV2PoolBurnParam calldata param) external override returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { 335: if (long0Amount != 0) ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.long0To, TimeswapV2OptionPosition.Long0, long0Amount); 338: if (long1Amount != 0) ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.long1To, TimeswapV2OptionPosition.Long1, long1Amount); 365: function deleverage(TimeswapV2PoolDeleverageParam calldata param) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { 387: (long0Amount, long1Amount, shortAmount, data) = pools[param.strike][param.maturity].deleverage(param, transactionFee, protocolFee, blockTimestamp(durationForward)); 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; 404: TimeswapV2PoolDeleverageCallbackParam({strike: param.strike, maturity: param.maturity, long0Amount: long0Amount, long1Amount: long1Amount, shortAmount: shortAmount, data: data}) 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); 421: function leverage(TimeswapV2PoolLeverageParam calldata param) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { 426: function leverage(TimeswapV2PoolLeverageParam calldata param, uint96 durationForward) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { 440: (long0Amount, long1Amount, shortAmount, data) = pools[param.strike][param.maturity].leverage(param, transactionFee, protocolFee, blockTimestamp(durationForward)); 448: if (long0Amount != 0) ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.long0To, TimeswapV2OptionPosition.Long0, long0Amount); 450: if (long1Amount != 0) ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.long1To, TimeswapV2OptionPosition.Long1, long1Amount); 454: TimeswapV2PoolLeverageCallbackParam({strike: param.strike, maturity: param.maturity, long0Amount: long0Amount, long1Amount: long1Amount, shortAmount: shortAmount, data: data}) 469: function rebalance(TimeswapV2PoolRebalanceParam calldata param) external override noDelegateCall returns (uint256 long0Amount, uint256 long1Amount, bytes memory data) { 511: ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), param.isLong0ToLong1 ? TimeswapV2OptionPosition.Long0 : TimeswapV2OptionPosition.Long1),
File: packages/v2-token/src/interfaces/ITimeswapV2LiquidityToken.sol 42: function transferFeesFrom(address from, address to, TimeswapV2LiquidityTokenPosition calldata position, uint256 long0Fees, uint256 long1Fees, uint256 shortFees) external; 59: function collect(TimeswapV2LiquidityTokenCollectParam calldata param) external returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, bytes memory data);
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol 22: import {TimeswapV2LiquidityTokenMintCallbackParam, TimeswapV2LiquidityTokenBurnCallbackParam, TimeswapV2LiquidityTokenCollectCallbackParam} from "./structs/CallbackParam.sol"; 70: function transferTokenPositionFrom(address from, address to, TimeswapV2LiquidityTokenPosition calldata timeswapV2LiquidityTokenPosition, uint160 liquidityAmount) external { 75: function transferFeesFrom(address from, address to, TimeswapV2LiquidityTokenPosition calldata position, uint256 long0Fees, uint256 long1Fees, uint256 shortFees) external override { 182: function collect(TimeswapV2LiquidityTokenCollectParam calldata param) external returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, bytes memory data) { 244: (, address poolPair) = PoolFactoryLibrary.getWithCheck(optionFactory, poolFactory, timeswapV2LiquidityTokenPosition.token0, timeswapV2LiquidityTokenPosition.token1); 246: (long0FeeGrowth, long1FeeGrowth, shortFeeGrowth) = ITimeswapV2Pool(poolPair).feeGrowth(timeswapV2LiquidityTokenPosition.strike, timeswapV2LiquidityTokenPosition.maturity); 270: (, address poolPair) = PoolFactoryLibrary.getWithCheck(optionFactory, poolFactory, timeswapV2LiquidityTokenPosition.token0, timeswapV2LiquidityTokenPosition.token1); 272: (long0FeeGrowth, long1FeeGrowth, shortFeeGrowth) = ITimeswapV2Pool(poolPair).feeGrowth(timeswapV2LiquidityTokenPosition.strike, timeswapV2LiquidityTokenPosition.maturity); 277: (uint256 long0Fees, uint256 long1Fees, uint256 shortFees) = feesPosition.feesEarnedOf(uint160(balanceOf(owner, id)), long0FeeGrowth, long1FeeGrowth, shortFeeGrowth);
File: packages/v2-token/src/TimeswapV2Token.sol 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); 205: if (param.long0Amount != 0) ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.long0To, TimeswapV2OptionPosition.Long0, param.long0Amount); 213: if (param.shortAmount != 0) ITimeswapV2Option(optionPair).transferPosition(param.strike, param.maturity, param.shortTo, TimeswapV2OptionPosition.Short, param.shortAmount);
Some files use >=
, some use ^
. The instances below are examples of the method that has the fewest instances for a specific version. Note that using >=
without also specifying <=
will lead to failures to compile, or external project incompatability, when the major version changes and there are breaking-changes, so ^
should be preferred regardless of the instance counts
There are 6 instances of this issue:
File: packages/v2-token/src/structs/CallbackParam.sol 2: pragma solidity ^0.8.8;
File: packages/v2-token/src/structs/FeesPosition.sol 2: pragma solidity ^0.8.8;
File: packages/v2-token/src/structs/Param.sol 2: pragma solidity ^0.8.8;
File: packages/v2-token/src/structs/Position.sol 2: pragma solidity ^0.8.8;
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol 2: pragma solidity ^0.8.8;
File: packages/v2-token/src/TimeswapV2Token.sol 2: pragma solidity ^0.8.8;
There are 2 instances of this issue:
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol 2: pragma solidity ^0.8.8;
File: packages/v2-token/src/TimeswapV2Token.sol 2: pragma solidity ^0.8.8;
There are 77 instances of this issue:
File: packages/v2-library/src/FullMath.sol /// @audit signficant 40: /// @param addendA0 The least signficant part of addendA. /// @audit precoditions 250: // correct result modulo 2**256. Since the precoditions guarantee
File: packages/v2-library/src/StrikeConversion.sol /// @audit oneToZero 22: /// @param amount The amount ot be converted. Token0 amount when zeroToOne. Token1 amount when oneToZero. /// @audit toekn1 40: /// @dev When oneToZero, given a larger base amount, and toekn1 amount, get the difference token0 amount.
File: packages/v2-option/src/interfaces/callbacks/ITimeswapV2OptionBurnCallback.sol /// @audit receipients 12: /// @dev The token0 and token1 will already transferred to the receipients.
File: packages/v2-option/src/interfaces/callbacks/ITimeswapV2OptionCollectCallback.sol /// @audit receipients 12: /// @dev The token0 and token1 will already transferred to the receipients.
File: packages/v2-option/src/interfaces/callbacks/ITimeswapV2OptionMintCallback.sol /// @audit receipients 12: /// @dev The long0 positions, long1 positions, and/or short positions will already minted to the receipients.
File: packages/v2-option/src/interfaces/callbacks/ITimeswapV2OptionSwapCallback.sol /// @audit receipients 12: /// @dev The long0 positions or long1 positions will already minted to the receipients.
File: packages/v2-option/src/interfaces/ITimeswapV2Option.sol /// @audit receipient 18: /// @param to The address of the receipient of the position. /// @audit receipient 27: /// @param long0To The address of the receipient of long token0 position. /// @audit receipient 28: /// @param long1To The address of the receipient of long token1 position. /// @audit receipient 29: /// @param shortTo The address of the receipient of short position. /// @audit receipient 49: /// @param token0To The address of the receipient of token0. /// @audit receipient 50: /// @param token1To The address of the receipient of token1. /// @audit receipient 69: /// @param tokenTo The address of the receipient of token0 or token1. /// @audit receipient 70: /// @param longTo The address of the receipient of long token0 or long token1. /// @audit receipient 91: /// @param token0To The address of the receipient of token0. /// @audit receipient 92: /// @param token1To The address of the receipient of token1. /// @audit receipient 145: /// @param to The address of the receipient of the position.
File: packages/v2-option/src/TimeswapV2Option.sol /// @audit maturies 47: /// @dev mapping of all option state for all strikes and maturies. /// @audit overidden 69: // Can be overidden for testing purposes.
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolDeleverageCallback.sol /// @audit receipient 10: /// @dev The short positions will already be minted to the receipient.
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolLeverageCallback.sol /// @audit receipients 10: /// @dev The long0 positions and long1 positions will already be minted to the receipients.
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolMintCallback.sol /// @audit receipient 10: /// @dev The liquidity positionss will already be minted to the receipient.
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolRebalanceCallback.sol /// @audit receipient 10: /// @dev The long0 positions or long1 positions will already be minted to the receipient.
File: packages/v2-pool/src/interfaces/ITimeswapV2Pool.sol /// @audit receipeint 14: /// @param to The receipeint of liquidity position. /// @audit receipeint 22: /// @param to The receipeint of fees position. /// @audit receipient 32: /// @param long0To The receipient of long0 position fees. /// @audit receipient 33: /// @param long1To The receipient of long1 position fees. /// @audit receipient 34: /// @param shortTo The receipient of short position fees. /// @audit receipient 54: /// @param long0To The receipient of long0 position fees. /// @audit receipient 55: /// @param long1To The receipient of long1 position fees. /// @audit receipient 56: /// @param shortTo The receipient of short position fees. /// @audit receipient 76: /// @param to The receipient of liquidity positions. /// @audit receipient 87: /// @param long0To The receipient of long0 positions. /// @audit receipient 88: /// @param long1To The receipient of long1 positions. /// @audit receipient 89: /// @param shortTo The receipient of short positions. /// @audit receipient 111: /// @param to The receipient of short positions. /// @audit receipient 121: /// @param long0To The receipient of long0 positions. /// @audit receipient 122: /// @param long1To The receipient of long1 positions. /// @audit receipient /// @audit ekse 132: /// @param to If isLong0ToLong1 then receipient of long0 positions, ekse recipient of long1 positions. /// @audit receipient 234: /// @param to The receipient of the liquidity positions. /// @audit receipient 242: /// @param to The receipient of the transaction fees. /// @audit transferrred 243: /// @param long0Fees The amount of long0 position fees transferrred. /// @audit transferrred 244: /// @param long1Fees The amount of long1 position fees transferrred. /// @audit transferrred 245: /// @param shortFees The amount of short position fees transferrred.
File: packages/v2-pool/src/libraries/ConstantProduct.sol /// @audit liqudity 19: /// @dev Reverts when there is not enough time value liqudity to receive when lending. /// @audit disriminant 394: /// @return sqrtDiscriminant The square root disriminant calculated.
File: packages/v2-pool/src/libraries/PoolFactory.sol /// @audit retreived 27: /// @return optionPair The retreived option pair address. Zero address if not deployed. /// @audit retreived 28: /// @return poolPair The retreived pool pair address. Zero address if not deployed. /// @audit retreived 41: /// @return optionPair The retreived option pair address. /// @audit retreived 42: /// @return poolPair The retreived pool pair address.
File: packages/v2-pool/src/libraries/PoolPair.sol /// @audit doesn 19: /// @dev Checks if the pool doesn not exist.
File: packages/v2-pool/src/structs/Pool.sol /// @audit receipient 155: /// @param to The receipient of the liquidity positions. /// @audit receipient 168: // Update the fee growth and fees of receipient. /// @audit receipient 178: /// @param to The receipient of the transaction fees. /// @audit transferrred 179: /// @param long0Fees The amount of long0 position fees transferrred. /// @audit transferrred 180: /// @param long1Fees The amount of long1 position fees transferrred. /// @audit transferrred 181: /// @param shortFees The amount of short position fees transferrred.
File: packages/v2-pool/src/TimeswapV2Pool.sol /// @audit overidden 81: // Can be overidden for testing purposes. /// @audit receipients 222: /// @dev Transfer long0 positions, long1 positions, and/or short positions to the receipients. /// @audit receipient 225: /// @param long0To The receipient of long0 positions. /// @audit receipient 226: /// @param long1To The receipient of long1 positions. /// @audit receipient 227: /// @param shortTo The receipient of short positions. /// @audit receipients 332: // Transfer the positions to the receipients. /// @audit receipient 399: // Transfer short positions to the receipient. /// @audit receipients 446: // Transfer the positions to the receipients. /// @audit receipients 486: // Transfer the positions to the receipients.
File: packages/v2-token/src/base/ERC1155Enumerable.sol /// @audit overidden 69: /// @dev Any additional condition to add token enumeration when overidden. /// @audit overidden 74: /// @dev Any additional condition to add token enumeration when overidden. /// @audit overidden 103: /// @dev Any additional condition to remove token enumeration when overidden. /// @audit overidden 108: /// @dev Any additional condition to remove token enumeration when overidden.
File: packages/v2-token/src/interfaces/ITimeswapV2Token.sol /// @audit postion 27: /// @dev mints TimeswapV2Token as per postion and amount /// @audit postion 32: /// @dev burns TimeswapV2Token as per postion and amount
File: packages/v2-token/src/structs/Position.sol /// @audit keccak 33: /// @dev return keccak for key management for Token. /// @audit keccak 38: /// @dev return keccak for key management for Liquidity Token.
There are 3 instances of this issue:
File: packages/v2-option/src/TimeswapV2OptionFactory.sol
File: packages/v2-pool/src/TimeswapV2PoolDeployer.sol
File: packages/v2-token/src/structs/FeesPosition.sol
There are 63 instances of this issue:
File: packages/v2-library/src/CatchError.sol /// @audit Missing: '@return' 10 /// @param reason The data being inquired upon. 11 /// @param selector The given conditional selector. 12: function catchError(bytes memory reason, bytes4 selector) internal pure returns (bytes memory) {
File: packages/v2-library/src/Error.sol /// @audit Missing: '@param maturity' 123 /// @dev Reverts when an option of given strike and maturity is still inactive. 124 /// @param strike The chosen strike. 125: function inactiveOptionChoice(uint256 strike, uint256 maturity) internal pure {
File: packages/v2-library/src/FullMath.sol /// @audit Missing: '@return' 113 /// @param quotient0 The least significant part of quotient. 114 /// @param quotient1 The most significant part of quotient. 115: function div512(uint256 dividend0, uint256 dividend1, uint256 divisor) private pure returns (uint256 quotient0, uint256 quotient1) { /// @audit Missing: '@return' 136 /// @param roundUp Round up the result when true. Round down if false. 137 /// @param quotient The quotient. 138: function div512To256(uint256 dividend0, uint256 dividend1, uint256 divisor, bool roundUp) internal pure returns (uint256 quotient) { /// @audit Missing: '@return' 156 /// @param quotient0 The least significant part of quotient. 157 /// @param quotient1 The most significant part of quotient. 158: function div512(uint256 dividend0, uint256 dividend1, uint256 divisor, bool roundUp) internal pure returns (uint256 quotient0, uint256 quotient1) { /// @audit Missing: '@return' 285 /// @param currentEstimate The current estimate of the iteration. 286 /// @param estimate The new estimate of the iteration. 287: function sqrt512Estimate(uint256 value0, uint256 value1, uint256 currentEstimate) private pure returns (uint256 estimate) {
File: packages/v2-library/src/SafeCast.sol /// @audit Missing: '@return' 16 /// @param value The uint256 number to be safecasted. 17 /// @param result The uint16 result. 18: function toUint16(uint256 value) internal pure returns (uint16 result) { /// @audit Missing: '@return' 25 /// @param value The uint256 number to be safecasted. 26 /// @param result The uint96 result. 27: function toUint96(uint256 value) internal pure returns (uint96 result) { /// @audit Missing: '@return' 34 /// @param value The uint256 number to be safecasted. 35 /// @param result The uint160 result. 36: function toUint160(uint256 value) internal pure returns (uint160 result) {
File: packages/v2-library/src/StrikeConversion.sol /// @audit Missing: '@return' 14 /// @param zeroToOne ZeroToOne if it is true. OneToZero if it is false. 15 /// @param roundUp Round up the result when true. Round down if false. 16: function convert(uint256 amount, uint256 strike, bool zeroToOne, bool roundUp) internal pure returns (uint256) { /// @audit Missing: '@return' 24 /// @param toOne ToOne if it is true, ToZero if it is false. 25 /// @param roundUp Round up the result when true. Round down if false. 26: function turn(uint256 amount, uint256 strike, bool toOne, bool roundUp) internal pure returns (uint256) { /// @audit Missing: '@return' 33 /// @param strike The strike multiple conversion. 34 /// @param roundUp Round up the result when true. Round down if false. 35: function combine(uint256 amount0, uint256 amount1, uint256 strike, bool roundUp) internal pure returns (uint256) { /// @audit Missing: '@return' 44 /// @param zeroToOne ZeroToOne if it is true. OneToZero if it is false. 45 /// @param roundUp Round up the result when true. Round down if false. 46: function dif(uint256 base, uint256 amount, uint256 strike, bool zeroToOne, bool roundUp) internal pure returns (uint256) {
File: packages/v2-option/src/interfaces/ITimeswapV2OptionFactory.sol /// @audit Missing: '@return' 26 /// @dev Get the address of the option pair in the option pair enumeration list. 27 /// @param id The chosen index. 28: function getByIndex(uint256 id) external view returns (address optionPair); /// @audit Missing: '@return' 39 /// @param token1 The second ERC20 token address of the pair. 40 /// @param optionPair The address of the Timeswap V2 Option contract created. 41: function create(address token0, address token1) external returns (address optionPair);
File: packages/v2-option/src/interfaces/ITimeswapV2Option.sol /// @audit Missing: '@return' 118 /// @dev Get the strike and maturity of the option in the option enumeration list. 119 /// @param id The chosen index. 120: function getByIndex(uint256 id) external view returns (StrikeAndMaturity memory);
File: packages/v2-option/src/libraries/Proportion.sol /// @audit Missing: '@param roundUp' /// @audit Missing: '@return' 7 /// @dev Get the balance proportion calculation. 8 /// @notice Round down the result. 9 /// @param multiplicand The multiplicand balance. 10 /// @param multiplier The multiplier balance. 11 /// @param divisor The divisor balance. 12: function proportion(uint256 multiplicand, uint256 multiplier, uint256 divisor, bool roundUp) internal pure returns (uint256) {
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolBurnCallback.sol /// @audit Missing: '@param param' 8 /// @dev Returns the amount of long0 position and long1 positions chosen to be withdrawn. 9 /// @notice The StrikeConversion.combine of long0 position and long1 position must be less than or equal to long amount. 10 /// @return long0Amount Amount of long0 position to be withdrawn. 11 /// @return long1Amount Amount of long1 position to be withdrawn. 12 /// @return data The bytes of data to be sent to msg.sender. 13: function timeswapV2PoolBurnChoiceCallback(TimeswapV2PoolBurnChoiceCallbackParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data); /// @audit Missing: '@param param' 18 /// @dev Require enough liquidity position by the msg.sender. 19 /// @return data The bytes of data to be sent to msg.sender. 20: function timeswapV2PoolBurnCallback(TimeswapV2PoolBurnCallbackParam calldata param) external returns (bytes memory data);
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolDeleverageCallback.sol /// @audit Missing: '@param param' 8 /// @dev Returns the amount of long0 position and long1 positions chosen to be deposited to the pool. 9 /// @notice The StrikeConversion.combine of long0 position and long1 position must be greater than or equal to long amount. 10 /// @dev The short positions will already be minted to the receipient. 11 /// @return long0Amount Amount of long0 position to be deposited. 12 /// @return long1Amount Amount of long1 position to be deposited. 13 /// @param data The bytes of data to be sent to msg.sender. 14: function timeswapV2PoolDeleverageChoiceCallback(TimeswapV2PoolDeleverageChoiceCallbackParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data); /// @audit Missing: '@param param' /// @audit Missing: '@return' 16 /// @dev Require the transfer of long0 position and long1 position into the pool. 17 /// @param data The bytes of data to be sent to msg.sender. 18: function timeswapV2PoolDeleverageCallback(TimeswapV2PoolDeleverageCallbackParam calldata param) external returns (bytes memory data);
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolLeverageCallback.sol /// @audit Missing: '@param param' 8 /// @dev Returns the amount of long0 position and long1 positions chosen to be withdrawn. 9 /// @notice The StrikeConversion.combine of long0 position and long1 position must be less than or equal to long amount. 10 /// @dev The long0 positions and long1 positions will already be minted to the receipients. 11 /// @return long0Amount Amount of long0 position to be withdrawn. 12 /// @return long1Amount Amount of long1 position to be withdrawn. 13 /// @param data The bytes of data to be sent to msg.sender. 14: function timeswapV2PoolLeverageChoiceCallback(TimeswapV2PoolLeverageChoiceCallbackParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data); /// @audit Missing: '@param param' /// @audit Missing: '@return' 16 /// @dev Require the transfer of short position into the pool. 17 /// @param data The bytes of data to be sent to msg.sender. 18: function timeswapV2PoolLeverageCallback(TimeswapV2PoolLeverageCallbackParam calldata param) external returns (bytes memory data);
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolMintCallback.sol /// @audit Missing: '@param param' 8 /// @dev Returns the amount of long0 position and long1 positions chosen to be deposited to the pool. 9 /// @notice The StrikeConversion.combine of long0 position and long1 position must be greater than or equal to long amount. 10 /// @dev The liquidity positionss will already be minted to the receipient. 11 /// @return long0Amount Amount of long0 position to be deposited. 12 /// @return long1Amount Amount of long1 position to be deposited. 13 /// @param data The bytes of data to be sent to msg.sender. 14: function timeswapV2PoolMintChoiceCallback(TimeswapV2PoolMintChoiceCallbackParam calldata param) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data); /// @audit Missing: '@param param' /// @audit Missing: '@return' 16 /// @dev Require the transfer of long0 position, long1 position, and short position into the pool. 17 /// @param data The bytes of data to be sent to msg.sender. 18: function timeswapV2PoolMintCallback(TimeswapV2PoolMintCallbackParam calldata param) external returns (bytes memory data);
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolRebalanceCallback.sol /// @audit Missing: '@param param' /// @audit Missing: '@return' 8 /// @dev When Long0ToLong1, require the transfer of long0 position into the pool. 9 /// @dev When Long1ToLong0, require the transfer of long1 position into the pool. 10 /// @dev The long0 positions or long1 positions will already be minted to the receipient. 11 /// @param data The bytes of data to be sent to msg.sender. 12: function timeswapV2PoolRebalanceCallback(TimeswapV2PoolRebalanceCallbackParam calldata param) external returns (bytes memory data);
File: packages/v2-pool/src/interfaces/ITimeswapV2PoolFactory.sol /// @audit Missing: '@return' 39 /// @param option The address of the option contract used by the pool. 40 /// @param poolPair The address of the Timeswap V2 Pool contract created. 41: function create(address option) external returns (address poolPair);
File: packages/v2-pool/src/interfaces/ITimeswapV2Pool.sol /// @audit Missing: '@return' 156 /// @dev Get the strike and maturity of the pool in the pool enumeration list. 157 /// @param id The chosen index. 158: function getByIndex(uint256 id) external view returns (StrikeAndMaturity memory);
File: packages/v2-pool/src/libraries/ConstantProduct.sol /// @audit Missing: '@return' 27 /// @param rate The pool's squared root Interest Rate. 28 /// @param roundUp Rounds up the result when true. Rounds down the result when false. 29: function getLong(uint160 liquidity, uint160 rate, bool roundUp) internal pure returns (uint256) { /// @audit Missing: '@return' 36 /// @param duration The time duration in seconds. 37 /// @param roundUp Rounds up the result when true. Rounds down the result when false. 38: function getShort(uint160 liquidity, uint160 rate, uint96 duration, bool roundUp) internal pure returns (uint256) { /// @audit Missing: '@param isAdd' 153 /// @dev Update the new square root interest rate given change in long positions in base denomination. 154 /// @param liquidity The amount of liquidity of the pool. 155 /// @param rate The pool's squared root Interest Rate. 156 /// @param longAmount The amount of long positions. 157 /// @param duration The time duration in seconds. 158 /// @param transactionFee The fee that will be adjusted in the transaction. 159 /// @return newRate The new squared root Interest Rate. 160 /// @return shortAmount The amount of short positions to withdraw when depositing long positions in base denomination. 161 /// The amount of short positions to deposit when withdrawing long positions in base denomination. 162 /// @return fees The amount of short positions fee when depositing long positions in base denomination. 163 /// The amount of long positions fee in base denominations fee when withdrawing long positions in base denomination. 164 function updateGivenLong( 165 uint160 liquidity, 166 uint160 rate, 167 uint256 longAmount, 168 uint96 duration, 169 uint256 transactionFee, 170 bool isAdd 171: ) internal pure returns (uint160 newRate, uint256 shortAmount, uint256 fees) { /// @audit Missing: '@param isAdd' 184 /// @dev Update the new square root interest rate given change in short positions. 185 /// @param liquidity The amount of liquidity of the pool. 186 /// @param rate The pool's squared root Interest Rate. 187 /// @param shortAmount The amount of short positions. 188 /// @param duration The time duration in seconds. 189 /// @param transactionFee The fee that will be adjusted in the transaction. 190 /// @return newRate The new squared root Interest Rate. 191 /// @return longAmount The amount of long positions in base denomination to withdraw when depositing short positions. 192 /// The amount of long positions in base denomination to deposit when withdrawing short positions. 193 /// @return fees The amount of long positions fee in base denominations when depositing short positions. 194 /// The amount of short positions fee when withdrawing short positions. 195 function updateGivenShort( 196 uint160 liquidity, 197 uint160 rate, 198 uint256 shortAmount, 199 uint96 duration, 200 uint256 transactionFee, 201 bool isAdd 202: ) internal pure returns (uint160 newRate, uint256 longAmount, uint256 fees) { /// @audit Missing: '@return' 257 /// @param longAmount The amount of long in base denomination change.. 258 /// @param roundUp Round up the result when true. Round down the result when false. 259: function getLiquidityGivenLong(uint160 rate, uint256 longAmount, bool roundUp) private pure returns (uint160) { /// @audit Missing: '@return' 266 /// @param duration The time duration in seconds. 267 /// @param roundUp Round up the result when true. Round down the result when false. 268: function getLiquidityGivenShort(uint160 rate, uint256 shortAmount, uint96 duration, bool roundUp) private pure returns (uint160) { /// @audit Missing: '@return' 275 /// @param longAmount The amount long positions in base denomination change. 276 /// @param isAdd Long positions increase when true. Long positions decrease when false. 277: function getNewSqrtInterestRateGivenLong(uint160 liquidity, uint160 rate, uint256 longAmount, bool isAdd) private pure returns (uint160) { /// @audit Missing: '@return' 327 /// @param newRate The new interest rate of the pool 328 /// @param roundUp Increases in square root interest rate when true. Decrease in square root interest rate when false. 329: function getLongFromSqrtInterestRate(uint160 liquidity, uint160 rate, uint160 deltaRate, uint160 newRate, bool roundUp) private pure returns (uint256) { /// @audit Missing: '@return' 340 /// @param duration The time duration in seconds. 341 /// @param roundUp Increases in square root interest rate when true. Decrease in square root interest rate when false. 342: function getShortFromSqrtInterestRate(uint160 liquidity, uint160 deltaRate, uint96 duration, bool roundUp) private pure returns (uint256) {
File: packages/v2-pool/src/libraries/ConstantSum.sol /// @audit Missing: '@return' 17 /// @param transactionFee The fee that will be adjusted in the transaction. 18 /// @param isLong0ToLong1 Deposit long0 positions when true. Deposit long1 positions when false. 19: function calculateGivenLongIn(uint256 strike, uint256 longAmountIn, uint256 transactionFee, bool isLong0ToLong1) internal pure returns (uint256 longAmountOut, uint256 longFees) { /// @audit Missing: '@return' 28 /// @param transactionFee The fee that will be adjusted in the transaction. 29 /// @param isLong0ToLong1 Deposit long0 positions when true. Deposit long1 positions when false. 30: function calculateGivenLongOut(uint256 strike, uint256 longAmountOut, uint256 transactionFee, bool isLong0ToLong1) internal pure returns (uint256 longAmountIn, uint256 longFees) { /// @audit Missing: '@return' 37 /// @param longAmountOut The long amount to be withdrawn. 38 /// @param isLong0ToLong1 Deposit long0 positions when true. Deposit long1 positions when false. 39: function calculateGivenLongOutAlreadyAdjustFees(uint256 strike, uint256 longAmountOut, bool isLong0ToLong1) internal pure returns (uint256 longAmountIn) {
File: packages/v2-pool/src/libraries/Duration.sol /// @audit Missing: '@return' 9 /// @dev Reverts when the duration is too large. 10 /// @param duration The duration in seconds which is needed to be converted to the Duration type. 11: function init(uint256 duration) internal pure returns (uint96) {
File: packages/v2-pool/src/libraries/DurationWeight.sol /// @audit Missing: '@return' 13 /// @param shortAmount The amount of short withdrawn. 14 /// @param newShortFeeGrowth The newly updated short fee growth. 15: function update(uint160 liquidity, uint256 shortFeeGrowth, uint256 shortAmount) internal pure returns (uint256 newShortFeeGrowth) {
File: packages/v2-pool/src/libraries/FeeCalculation.sol /// @audit Missing: '@return' 38 /// @param lastFeeGrowth The previous global fee growth when owner enters. 39 /// @param globalFeeGrowth The current global fee growth. 40: function getFees(uint160 liquidity, uint256 lastFeeGrowth, uint256 globalFeeGrowth) internal pure returns (uint256) { /// @audit Missing: '@return' 45 /// @param amount The original amount. 46 /// @param fee The transaction fee rate. 47: function addFees(uint256 amount, uint256 fee) internal pure returns (uint256) { /// @audit Missing: '@return' 52 /// @param amount The original amount. 53 /// @param fee The transaction fee rate. 54: function removeFees(uint256 amount, uint256 fee) internal pure returns (uint256) { /// @audit Missing: '@return' 59 /// @param amount The amount with fees. 60 /// @param fee The transaction fee rate. 61: function getFeesRemoval(uint256 amount, uint256 fee) internal pure returns (uint256) { /// @audit Missing: '@return' 66 /// @param amount The amount with fees. 67 /// @param fee The transaction fee rate. 68: function getFeesAdditional(uint256 amount, uint256 fee) internal pure returns (uint256) { /// @audit Missing: '@return' 73 /// @param feeAmount The fee amount. 74 /// @param liquidity The current liquidity in the pool. 75: function getFeeGrowth(uint256 feeAmount, uint160 liquidity) internal pure returns (uint256) {
File: packages/v2-pool/src/structs/LiquidityPosition.sol /// @audit Missing: '@return' 31 /// @param long1FeeGrowth The current global long1 position fee growth to be compared. 32 /// @param shortFeeGrowth The current global short position fee growth to be compared. 33 function feesEarnedOf( 34 LiquidityPosition memory liquidityPosition, 35 uint256 long0FeeGrowth, 36 uint256 long1FeeGrowth, 37 uint256 shortFeeGrowth 38: ) internal pure returns (uint256 long0Fee, uint256 long1Fee, uint256 shortFee) {
File: packages/v2-pool/src/structs/Pool.sol /// @audit Missing: '@param transactionFee' 87 /// @dev Returns the amount of long0 and long1 adjusted for the protocol and transaction fee. 88 /// @param pool The state of the pool. 89 /// @return long0Amount The amount of long0 in the pool, adjusted for the protocol and transaction fee. 90 /// @return long1Amount The amount of long1 in the pool, adjusted for the protocol and transaction fee. 91: function totalLongBalanceAdjustFees(Pool storage pool, uint256 transactionFee) external view returns (uint256 long0Amount, uint256 long1Amount) { /// @audit Missing: '@param maturity' /// @audit Missing: '@param blockTimestamp' 96 /// @dev Returns the amount of sum of long0 and long1 converted to base denomination in the pool. 97 /// @dev Returns the amount of short positions in the pool. 98 /// @param pool The state of the pool. 99 /// @return longAmount The amount of sum of long0 and long1 converted to base denomination in the pool. 100 /// @return shortAmount The amount of short in the pool. 101: function totalPositions(Pool storage pool, uint256 maturity, uint96 blockTimestamp) external view returns (uint256 longAmount, uint256 shortAmount) { /// @audit Missing: '@param maturity' 139 /// @dev Move short positions to short fee growth due to duration of the pool decreasing as time moves forward when pool is after maturity. 140 /// @param pool The state of the pool. 141 /// @param blockTimestamp The block timestamp. 142: function updateDurationWeightAfterMaturity(Pool storage pool, uint256 maturity, uint96 blockTimestamp) private { /// @audit Missing: '@param maturity' 175 /// @dev Transfer fees earned of the sender to another address. 176 /// @notice Does not transfer the liquidity positions of the sender. 177 /// @param pool The state of the pool. 178 /// @param to The receipient of the transaction fees. 179 /// @param long0Fees The amount of long0 position fees transferrred. 180 /// @param long1Fees The amount of long1 position fees transferrred. 181 /// @param shortFees The amount of short position fees transferrred. 182 /// @param blockTimestamp The current block timestamp. 183: function transferFees(Pool storage pool, uint256 maturity, address to, uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint96 blockTimestamp) external { /// @audit Missing: '@param maturity' 251 /// @dev Collects the transaction fees of the pool. 252 /// @dev only liquidity provider can call this function. 253 /// @dev if the owner enters an amount which is greater than the fee amount they have earned, withdraw only the amount they have. 254 /// @param pool The state of the pool. 255 /// @param blockTimestamp The current block timestamp. 256 /// @param long0Requested The maximum amount of long0 positions wanted. 257 /// @param long1Requested The maximum amount of long1 positions wanted. 258 /// @param shortRequested The maximum amount of short positions wanted. 259 /// @return long0Amount The amount of long0 collected. 260 /// @return long1Amount The amount of long1 collected. 261 /// @return shortAmount The amount of short collected. 262 function collectTransactionFees( 263 Pool storage pool, 264 uint256 maturity, 265 uint256 long0Requested, 266 uint256 long1Requested, 267 uint256 shortRequested, 268 uint96 blockTimestamp 269: ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount) {
File: packages/v2-token/src/interfaces/ITimeswapV2LiquidityToken.sol /// @audit Missing: '@return' 24 /// @param owner The owner of the token 25 /// @param position The liquidity position 26: function positionOf(address owner, TimeswapV2LiquidityTokenPosition calldata position) external view returns (uint256 amount);
File: packages/v2-token/src/interfaces/ITimeswapV2Token.sol /// @audit Missing: '@return' 16 /// @param owner The owner of the token 17 /// @param position type of option position (long0, long1, short) 18: function positionOf(address owner, TimeswapV2TokenPosition calldata position) external view returns (uint256 amount); /// @audit Missing: '@return' 32 /// @dev burns TimeswapV2Token as per postion and amount 33 /// @param param The TimeswapV2TokenBurnParam 34: function burn(TimeswapV2TokenBurnParam calldata param) external returns (bytes memory data);
Consider changing the variable to be an unnamed one
There are 55 instances of this issue:
File: packages/v2-library/src/Math.sol /// @audit result 88: function min(uint256 value1, uint256 value2) internal pure returns (uint256 result) {
File: packages/v2-pool/src/structs/Pool.sol /// @audit long0FeeGrowth /// @audit long1FeeGrowth /// @audit shortFeeGrowth 66: function feeGrowth(Pool storage pool) external view returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth) { /// @audit long0Fees /// @audit long1Fees /// @audit shortFees 75: function feesEarnedOf(Pool storage pool, address owner) external view returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees) { /// @audit long0ProtocolFees /// @audit long1ProtocolFees /// @audit shortProtocolFees 83: function protocolFeesEarned(Pool storage pool) external view returns (uint256 long0ProtocolFees, uint256 long1ProtocolFees, uint256 shortProtocolFees) {
File: packages/v2-pool/src/TimeswapV2Pool.sol /// @audit long0FeeGrowth /// @audit long1FeeGrowth /// @audit shortFeeGrowth 118: function feeGrowth(uint256 strike, uint256 maturity) external view override returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth) { /// @audit long0Fees /// @audit long1Fees /// @audit shortFees 123: function feesEarnedOf(uint256 strike, uint256 maturity, address owner) external view override returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees) { /// @audit long0ProtocolFees /// @audit long1ProtocolFees /// @audit shortProtocolFees 128: function protocolFeesEarned(uint256 strike, uint256 maturity) external view override returns (uint256 long0ProtocolFees, uint256 long1ProtocolFees, uint256 shortProtocolFees) { /// @audit liquidityAmount /// @audit long0Amount /// @audit long1Amount /// @audit shortAmount /// @audit data 240: function mint(TimeswapV2PoolMintParam calldata param) external override returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit liquidityAmount /// @audit long0Amount /// @audit long1Amount /// @audit shortAmount /// @audit data 245 function mint( 246 TimeswapV2PoolMintParam calldata param, 247 uint96 durationForward 248: ) external override returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit liquidityAmount /// @audit long0Amount /// @audit long1Amount /// @audit shortAmount /// @audit data 305: function burn(TimeswapV2PoolBurnParam calldata param) external override returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit liquidityAmount /// @audit long0Amount /// @audit long1Amount /// @audit shortAmount /// @audit data 310 function burn( 311 TimeswapV2PoolBurnParam calldata param, 312 uint96 durationForward 313: ) external override returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit long0Amount /// @audit long1Amount /// @audit shortAmount /// @audit data 365: function deleverage(TimeswapV2PoolDeleverageParam calldata param) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit long0Amount /// @audit long1Amount /// @audit shortAmount /// @audit data 370 function deleverage( 371 TimeswapV2PoolDeleverageParam calldata param, 372 uint96 durationForward 373: ) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit long0Amount /// @audit long1Amount /// @audit shortAmount /// @audit data 421: function leverage(TimeswapV2PoolLeverageParam calldata param) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit long0Amount /// @audit long1Amount /// @audit shortAmount /// @audit data 426: function leverage(TimeswapV2PoolLeverageParam calldata param, uint96 durationForward) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
delete
rather than assigning zero to clear valuesThe delete
keyword more closely matches the semantics of what is being done, and draws more attention to the changing of state, which may lead to a more thorough audit of its associated logic
There are 10 instances of this issue:
File: packages/v2-pool/src/structs/LiquidityPosition.sol 93: liquidityPosition.long0Fees = 0; 101: liquidityPosition.long1Fees = 0; 109: liquidityPosition.shortFees = 0;
File: packages/v2-pool/src/structs/Pool.sol 228: pool.long0ProtocolFees = 0; 236: pool.long1ProtocolFees = 0; 244: pool.shortProtocolFees = 0; 633: pool.long0Balance = 0; 646: pool.long1Balance = 0; 685: pool.long1Balance = 0; 706: pool.long0Balance = 0;
While 100% code coverage does not guarantee that there are no bugs, it often will catch easy-to-find bugs, and will ensure that there are fewer regressions when the code invariably has to be modified. Furthermore, in order to get full coverage, code authors will often have to re-organize their code so that it is more modular, so that each component can be tested separately, which reduces interdependencies between modules and layers, and makes for code that is easier to reason about and audit.
There is 1 instance of this issue:
File: Various Files
Large code bases, or code with lots of inline-assembly, complicated math, or complicated interactions between multiple contracts, should implement fuzzing tests. Fuzzers such as Echidna require the test writer to come up with invariants which should not be violated under any circumstances, and the fuzzer tests various inputs and function calls to ensure that the invariants always hold. Even code with 100% code coverage can still have bugs due to the order of the operations a user performs, and fuzzers, with properly and extensively-written invariants, can close this testing gap significantly.
There is 1 instance of this issue:
File: Various Files
According to the Solidity style guide, functions should be laid out in the following order :constructor()
, receive()
, fallback()
, external
, public
, internal
, private
, but the cases below do not follow this pattern
There are 18 instances of this issue:
File: packages/v2-library/src/FullMath.sol /// @audit div512() came earlier 138: function div512To256(uint256 dividend0, uint256 dividend1, uint256 divisor, bool roundUp) internal pure returns (uint256 quotient) {
File: packages/v2-option/src/TimeswapV2Option.sol /// @audit addOptionEnumerationIfNecessary() came earlier 65: constructor() NoDelegateCall() { /// @audit blockTimestamp() came earlier 76: function getByIndex(uint256 id) external view override returns (StrikeAndMaturity memory) {
File: packages/v2-pool/src/libraries/FeeCalculation.sol /// @audit feeOverflow() came earlier 27: function update(uint160 liquidity, uint256 feeGrowth, uint256 protocolFees, uint256 fees, uint256 protocolFee) internal pure returns (uint256 newFeeGrowth, uint256 newProtocolFees) {
File: packages/v2-pool/src/structs/Pool.sol /// @audit updateDurationWeightAfterMaturity() came earlier 158: function transferLiquidity(Pool storage pool, address to, uint160 liquidityAmount, uint96 blockTimestamp) external {
File: packages/v2-pool/src/TimeswapV2Pool.sol /// @audit lowerGuard() came earlier 77: constructor() NoDelegateCall() { /// @audit blockTimestamp() came earlier 89: function getByIndex(uint256 id) external view override returns (StrikeAndMaturity memory) { /// @audit hasLiquidity() came earlier 103: function totalLiquidity(uint256 strike, uint256 maturity) external view override returns (uint160) { /// @audit collect() came earlier 240: function mint(TimeswapV2PoolMintParam calldata param) external override returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit mint() came earlier 305: function burn(TimeswapV2PoolBurnParam calldata param) external override returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit burn() came earlier 365: function deleverage(TimeswapV2PoolDeleverageParam calldata param) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit deleverage() came earlier 421: function leverage(TimeswapV2PoolLeverageParam calldata param) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit leverage() came earlier 469: function rebalance(TimeswapV2PoolRebalanceParam calldata param) external override noDelegateCall returns (uint256 long0Amount, uint256 long1Amount, bytes memory data) {
File: packages/v2-token/src/base/ERC1155Enumerable.sol /// @audit totalSupply() came earlier 41: function tokenByIndex(uint256 index) external view override returns (uint256) {
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol /// @audit lowerGuard() came earlier 65: function positionOf(address owner, TimeswapV2LiquidityTokenPosition calldata timeswapV2LiquidityTokenPosition) external view returns (uint256 amount) { /// @audit _updateFeesPositions() came earlier 255: function _additionalConditionAddTokenToOwnerEnumeration(address to, uint256 id) internal view override returns (bool) {
File: packages/v2-token/src/TimeswapV2Token.sol /// @audit lowerGuard() came earlier 66: function positionOf(address owner, TimeswapV2TokenPosition calldata timeswapV2TokenPosition) public view returns (uint256 amount) { /// @audit positionOf() came earlier 71: function transferTokenPositionFrom(address from, address to, TimeswapV2TokenPosition calldata timeswapV2TokenPosition, uint256 amount) external override {
The style guide says that, within a contract, the ordering should be 1) Type declarations, 2) State variables, 3) Events, 4) Modifiers, and 5) Functions, but the contract(s) below do not follow this ordering
There are 3 instances of this issue:
File: packages/v2-option/src/NoDelegateCall.sol /// @audit function checkNotDelegateCall came earlier 34 modifier noDelegateCall() { 35 checkNotDelegateCall(); 36 _; 37: }
File: packages/v2-pool/src/NoDelegateCall.sol /// @audit function checkNotDelegateCall came earlier 34 modifier noDelegateCall() { 35 checkNotDelegateCall(); 36 _; 37: }
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol /// @audit function constructor came earlier 41: mapping(bytes32 => uint96) private reentrancyGuards;
These findings are excluded from awards calculations because there are publicly-available automated tools that find them. The valid ones appear here for completeness
Issue | Instances | |
---|---|---|
[Lā01] | Missing checks for address(0x0) when assigning values to address state variables | 4 |
Total: 4 instances over 1 issues
Issue | Instances | |
---|---|---|
[Nā01] | constant s should be defined rather than using magic numbers | 3 |
[Nā02] | Event is missing indexed fields | 3 |
Total: 6 instances over 2 issues
address(0x0)
when assigning values to address
state variablesThere are 4 instances of this issue:
File: packages/v2-pool/src/base/OwnableTwoSteps.sol /// @audit (valid but excluded finding) 19: owner = chosenOwner;
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol /// @audit (valid but excluded finding) 37: optionFactory = chosenOptionFactory; /// @audit (valid but excluded finding) 38: poolFactory = chosenPoolFactory;
File: packages/v2-token/src/TimeswapV2Token.sol /// @audit (valid but excluded finding) 42: optionFactory = chosenOptionFactory;
constant
s should be defined rather than using magic numbersEven assembly can benefit from using readable constants instead of hex/numeric literals
There are 3 instances of this issue:
File: packages/v2-library/src/BytesLib.sol /// @audit 0x40 - (valid but excluded finding) /// @audit 31 - (valid but excluded finding) /// @audit 31 - (valid but excluded finding) 57: mstore(0x40, and(add(mc, 31), not(31)))
indexed
fieldsIndex event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event
should use three indexed
fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.
There are 3 instances of this issue:
File: packages/v2-pool/src/interfaces/IOwnableTwoSteps.sol /// @audit (valid but excluded finding) 7: event SetOwner(address pendingOwner); /// @audit (valid but excluded finding) 11: event AcceptOwner(address owner);
File: packages/v2-token/src/interfaces/ITimeswapV2LiquidityToken.sol /// @audit (valid but excluded finding) 13: event TransferFees(address from, address to, TimeswapV2LiquidityTokenPosition position, uint256 long0Fees, uint256 long1Fees, uint256 shortFees);
#0 - c4-judge
2023-02-01T23:36:27Z
Picodes marked the issue as grade-a
š 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 | Instances | Total Gas Saved | |
---|---|---|---|
[Gā01] | Multiple address /ID mappings can be combined into a single mapping of an address /ID to a struct , where appropriate | 1 | - |
[Gā02] | Structs can be packed into fewer storage slots | 1 | - |
[Gā03] | Using calldata instead of memory for read-only arguments in external functions saves gas | 5 | 600 |
[Gā04] | Avoid contract existence checks by using low level calls | 62 | 6200 |
[Gā05] | State variables should be cached in stack variables rather than re-reading them from storage | 2 | 194 |
[Gā06] | Optimize names to save gas | 12 | 264 |
[Gā07] | Use a more recent version of solidity | 2 | - |
[Gā08] | Usage of uints /ints smaller than 32 bytes (256 bits) incurs overhead | 19 | - |
Total: 104 instances over 8 issues with 7258 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.
address
/ID mappings can be combined into a single mapping
of an address
/ID to a struct
, where appropriateSaves 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. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
There is 1 instance of this issue:
File: packages/v2-pool/src/TimeswapV2Pool.sol 52 mapping(uint256 => mapping(uint256 => uint96)) private reentrancyGuards; 53: mapping(uint256 => mapping(uint256 => Pool)) private pools;
Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings
There is 1 instance of this issue:
File: packages/v2-token/src/structs/Position.sol /// @audit Variable ordering with 4 slots instead of the current 5: /// uint256(32):strike, uint256(32):maturity, address(20):token0, uint8(1):position, address(20):token1 12 struct TimeswapV2TokenPosition { 13 address token0; 14 address token1; 15 uint256 strike; 16 uint256 maturity; 17 TimeswapV2OptionPosition position; 18: }
calldata
instead of memory
for read-only arguments in external
functions saves gasWhen a function with a memory
array is called externally, the abi.decode()
step has to use a for-loop to copy each index of the calldata
to the memory
index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length
). Using calldata
directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as having memory
arguments, it's still valid for implementation contracs to use calldata
arguments instead.
If the array is passed to an internal
function which passes the array to another internal function where the array is modified and therefore memory
is used in the external
call, it's still more gass-efficient to use calldata
when the external
function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one
Note that I've also flagged instances where the function is public
but can be marked as external
since it's not called by the contract, and cases where a constructor is involved
There are 5 instances of this issue:
File: packages/v2-pool/src/structs/Pool.sol /// @audit param 294 function mint( 295 Pool storage pool, 296 TimeswapV2PoolMintParam memory param, 297 uint96 blockTimestamp 298: ) external returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit param 380 function burn( 381 Pool storage pool, 382 TimeswapV2PoolBurnParam memory param, 383 uint96 blockTimestamp 384: ) external returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit param 469 function deleverage( 470 Pool storage pool, 471 TimeswapV2PoolDeleverageParam memory param, 472 uint256 transactionFee, 473 uint256 protocolFee, 474 uint96 blockTimestamp 475: ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit param 552 function leverage( 553 Pool storage pool, 554 TimeswapV2PoolLeverageParam memory param, 555 uint256 transactionFee, 556 uint256 protocolFee, 557 uint96 blockTimestamp 558: ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) { /// @audit param 665: function rebalance(Pool storage pool, TimeswapV2PoolRebalanceParam memory param, uint256 transactionFee, uint256 protocolFee) external returns (uint256 long0Amount, uint256 long1Amount) {
Prior to 0.8.10 the compiler inserted extra code, including EXTCODESIZE
(100 gas), to check for contract existence for external function calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return value. Similar behavior can be achieved in earlier versions by using low-level calls, since low level calls never check for contract existence
There are 62 instances of this issue:
File: packages/v2-option/src/libraries/OptionFactory.sol /// @audit get() 28: optionPair = ITimeswapV2OptionFactory(optionFactory).get(token0, token1);
File: packages/v2-option/src/TimeswapV2Option.sol /// @audit parameter() 66: (optionFactory, token0, token1) = ITimeswapV2OptionDeployer(msg.sender).parameter(); /// @audit balanceOf() 128: IERC20(token0).balanceOf(address(this)) + token0AndLong0Amount, /// @audit balanceOf() 129: IERC20(token1).balanceOf(address(this)) + token1AndLong1Amount /// @audit timeswapV2OptionMintCallback() 133: data = ITimeswapV2OptionMintCallback(msg.sender).timeswapV2OptionMintCallback( /// @audit balanceOf() 145: if (token0AndLong0Amount != 0) Error.checkEnough(IERC20(token0).balanceOf(address(this)), currentProcess.balance0Target); /// @audit balanceOf() 148: if (token1AndLong1Amount != 0) Error.checkEnough(IERC20(token1).balanceOf(address(this)), currentProcess.balance1Target); /// @audit safeTransfer() 172: if (token0AndLong0Amount != 0) IERC20(token0).safeTransfer(param.token0To, token0AndLong0Amount); /// @audit safeTransfer() 175: if (token1AndLong1Amount != 0) IERC20(token1).safeTransfer(param.token1To, token1AndLong1Amount); /// @audit timeswapV2OptionBurnCallback() 179: data = ITimeswapV2OptionBurnCallback(msg.sender).timeswapV2OptionBurnCallback( /// @audit balanceOf() /// @audit balanceOf() 215: param.isLong0ToLong1 ? IERC20(token0).balanceOf(address(this)) - token0AndLong0Amount : IERC20(token0).balanceOf(address(this)) + token0AndLong0Amount, /// @audit balanceOf() /// @audit balanceOf() 216: param.isLong0ToLong1 ? IERC20(token1).balanceOf(address(this)) + token1AndLong1Amount : IERC20(token1).balanceOf(address(this)) - token1AndLong1Amount /// @audit safeTransfer() 220: IERC20(param.isLong0ToLong1 ? token0 : token1).safeTransfer(param.tokenTo, param.isLong0ToLong1 ? token0AndLong0Amount : token1AndLong1Amount); /// @audit timeswapV2OptionSwapCallback() 223: data = ITimeswapV2OptionSwapCallback(msg.sender).timeswapV2OptionSwapCallback( /// @audit balanceOf() 235: Error.checkEnough(IERC20(param.isLong0ToLong1 ? token1 : token0).balanceOf(address(this)), param.isLong0ToLong1 ? currentProcess.balance1Target : currentProcess.balance0Target); /// @audit safeTransfer() 259: if (token0Amount != 0) IERC20(token0).safeTransfer(param.token0To, token0Amount); /// @audit safeTransfer() 262: if (token1Amount != 0) IERC20(token1).safeTransfer(param.token1To, token1Amount); /// @audit timeswapV2OptionCollectCallback() 266: data = ITimeswapV2OptionCollectCallback(msg.sender).timeswapV2OptionCollectCallback(
File: packages/v2-pool/src/libraries/PoolFactory.sol /// @audit get() 32: poolPair = ITimeswapV2PoolFactory(poolFactory).get(optionPair); /// @audit get() 46: poolPair = ITimeswapV2PoolFactory(poolFactory).get(optionPair);
File: packages/v2-pool/src/structs/Pool.sol /// @audit timeswapV2PoolMintChoiceCallback() 349: (long0Amount, long1Amount, data) = ITimeswapV2PoolMintCallback(msg.sender).timeswapV2PoolMintChoiceCallback( /// @audit timeswapV2PoolBurnChoiceCallback() 438: (long0Amount, long1Amount, data) = ITimeswapV2PoolBurnCallback(msg.sender).timeswapV2PoolBurnChoiceCallback( /// @audit timeswapV2PoolDeleverageChoiceCallback() 532: (long0Amount, long1Amount, data) = ITimeswapV2PoolDeleverageCallback(msg.sender).timeswapV2PoolDeleverageChoiceCallback( /// @audit timeswapV2PoolLeverageChoiceCallback() 615: (long0Amount, long1Amount, data) = ITimeswapV2PoolLeverageCallback(msg.sender).timeswapV2PoolLeverageChoiceCallback(
File: packages/v2-pool/src/TimeswapV2Pool.sol /// @audit parameter() 78: (poolFactory, optionPair, transactionFee, protocolFee) = ITimeswapV2PoolDeployer(msg.sender).parameter(); /// @audit owner() 189: ITimeswapV2PoolFactory(poolFactory).owner().checkIfOwner(); /// @audit positionOf() 267: if (long0Amount != 0) long0BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0) + long0Amount; /// @audit positionOf() 271: if (long1Amount != 0) long1BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1) + long1Amount; /// @audit positionOf() 274: uint256 shortBalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short) + shortAmount; /// @audit timeswapV2PoolMintCallback() 277: data = ITimeswapV2PoolMintCallback(msg.sender).timeswapV2PoolMintCallback( /// @audit positionOf() 293: if (long0Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0), long0BalanceTarget); /// @audit positionOf() 295: if (long1Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1), long1BalanceTarget); /// @audit positionOf() 297: Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short), shortBalanceTarget); /// @audit timeswapV2PoolBurnCallback() 343: data = ITimeswapV2PoolBurn2Callback(msg.sender).timeswapV2PoolBurnCallback( /// @audit positionOf() 393: if (long0Amount != 0) long0BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0) + long0Amount; /// @audit positionOf() 397: if (long1Amount != 0) long1BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1) + long1Amount; /// @audit timeswapV2PoolDeleverageCallback() 403: data = ITimeswapV2PoolDeleverageCallback(msg.sender).timeswapV2PoolDeleverageCallback( /// @audit positionOf() 411: if (long0Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0), long0BalanceTarget); /// @audit positionOf() 413: if (long1Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1), long1BalanceTarget); /// @audit positionOf() 444: uint256 balanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short) + shortAmount; /// @audit timeswapV2PoolLeverageCallback() 453: data = ITimeswapV2PoolLeverageCallback(msg.sender).timeswapV2PoolLeverageCallback( /// @audit positionOf() 461: Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short), balanceTarget); /// @audit positionOf() 479: uint256 balanceTarget = ITimeswapV2Option(optionPair).positionOf( /// @audit timeswapV2PoolRebalanceCallback() 497: data = ITimeswapV2PoolRebalanceCallback(msg.sender).timeswapV2PoolRebalanceCallback( /// @audit positionOf() 511: ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), param.isLong0ToLong1 ? TimeswapV2OptionPosition.Long0 : TimeswapV2OptionPosition.Long1),
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol /// @audit liquidityOf() 125: uint160 liquidityBalanceTarget = ITimeswapV2Pool(poolPair).liquidityOf(param.strike, param.maturity, address(this)) + param.liquidityAmount; /// @audit timeswapV2LiquidityTokenMintCallback() 131: data = ITimeswapV2LiquidityTokenMintCallback(msg.sender).timeswapV2LiquidityTokenMintCallback( /// @audit liquidityOf() 143: Error.checkEnough(ITimeswapV2Pool(poolPair).liquidityOf(param.strike, param.maturity, address(this)), liquidityBalanceTarget); /// @audit timeswapV2LiquidityTokenBurnCallback() 163: data = ITimeswapV2LiquidityTokenBurnCallback(msg.sender).timeswapV2LiquidityTokenBurnCallback( /// @audit timeswapV2LiquidityTokenCollectCallback() 202: data = ITimeswapV2LiquidityTokenCollectCallback(msg.sender).timeswapV2LiquidityTokenCollectCallback( /// @audit feeGrowth() 246: (long0FeeGrowth, long1FeeGrowth, shortFeeGrowth) = ITimeswapV2Pool(poolPair).feeGrowth(timeswapV2LiquidityTokenPosition.strike, timeswapV2LiquidityTokenPosition.maturity); /// @audit feeGrowth() 272: (long0FeeGrowth, long1FeeGrowth, shortFeeGrowth) = ITimeswapV2Pool(poolPair).feeGrowth(timeswapV2LiquidityTokenPosition.strike, timeswapV2LiquidityTokenPosition.maturity);
File: packages/v2-token/src/TimeswapV2Token.sol /// @audit positionOf() 87: long0BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0) + param.long0Amount; /// @audit positionOf() 117: long1BalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1) + param.long1Amount; /// @audit positionOf() 146: shortBalanceTarget = ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short) + param.shortAmount; /// @audit timeswapV2TokenMintCallback() 172: data = ITimeswapV2TokenMintCallback(msg.sender).timeswapV2TokenMintCallback( /// @audit positionOf() 186: if (param.long0Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long0), long0BalanceTarget); /// @audit positionOf() 189: if (param.long1Amount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Long1), long1BalanceTarget); /// @audit positionOf() 192: if (param.shortAmount != 0) Error.checkEnough(ITimeswapV2Option(optionPair).positionOf(param.strike, param.maturity, address(this), TimeswapV2OptionPosition.Short), shortBalanceTarget); /// @audit timeswapV2TokenBurnCallback() 216: data = ITimeswapV2TokenBurnCallback(msg.sender).timeswapV2TokenBurnCallback(
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 are 2 instances of this issue:
File: packages/v2-pool/src/base/OwnableTwoSteps.sol /// @audit owner on line 24 27: chosenPendingOwner.checkIfAlreadyOwner(owner); /// @audit pendingOwner on line 29 31: emit SetOwner(pendingOwner);
public
/external
function names and public
member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted
There are 12 instances of this issue:
File: packages/v2-option/src/interfaces/ITimeswapV2OptionFactory.sol /// @audit get(), getByIndex(), numberOfPairs(), create() 6: interface ITimeswapV2OptionFactory {
File: packages/v2-option/src/interfaces/ITimeswapV2Option.sol /// @audit optionFactory(), token0(), token1(), getByIndex(), numberOfOptions(), totalPosition(), positionOf(), transferPosition(), mint(), burn(), swap(), collect() 11: interface ITimeswapV2Option {
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolDeleverageCallback.sol /// @audit timeswapV2PoolDeleverageChoiceCallback(), timeswapV2PoolDeleverageCallback() 7: interface ITimeswapV2PoolDeleverageCallback {
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolLeverageCallback.sol /// @audit timeswapV2PoolLeverageChoiceCallback(), timeswapV2PoolLeverageCallback() 7: interface ITimeswapV2PoolLeverageCallback {
File: packages/v2-pool/src/interfaces/callbacks/ITimeswapV2PoolMintCallback.sol /// @audit timeswapV2PoolMintChoiceCallback(), timeswapV2PoolMintCallback() 7: interface ITimeswapV2PoolMintCallback {
File: packages/v2-pool/src/interfaces/IOwnableTwoSteps.sol /// @audit pendingOwner(), setPendingOwner(), acceptOwner() 4: interface IOwnableTwoSteps {
File: packages/v2-pool/src/interfaces/ITimeswapV2PoolFactory.sol /// @audit transactionFee(), protocolFee(), get(), getByIndex(), numberOfPairs(), create() 8: interface ITimeswapV2PoolFactory is IOwnableTwoSteps {
File: packages/v2-pool/src/interfaces/ITimeswapV2Pool.sol /// @audit poolFactory(), optionPair(), transactionFee(), protocolFee(), getByIndex(), numberOfPools(), totalLiquidity(), sqrtInterestRate(), liquidityOf(), feeGrowth(), feesEarnedOf(), protocolFeesEarned(), totalLongBalance(), totalLongBalanceAdjustFees(), totalPositions(), transferLiquidity(), transferFees(), initialize(), collectProtocolFees(), collectTransactionFees(), mint(), mint(), burn(), burn(), deleverage(), deleverage(), leverage(), leverage(), rebalance() 9: interface ITimeswapV2Pool {
File: packages/v2-pool/src/structs/Pool.sol /// @audit feeGrowth(), feesEarnedOf(), protocolFeesEarned(), totalLongBalanceAdjustFees(), totalPositions(), transferLiquidity(), transferFees(), initialize(), collectProtocolFees(), collectTransactionFees(), mint(), burn(), deleverage(), leverage(), rebalance() 56: library PoolLibrary {
File: packages/v2-token/src/interfaces/ITimeswapV2LiquidityToken.sol /// @audit optionFactory(), poolFactory(), positionOf(), transferTokenPositionFrom(), transferFeesFrom(), mint(), burn(), collect() 10: interface ITimeswapV2LiquidityToken is IERC1155Enumerable {
File: packages/v2-token/src/interfaces/ITimeswapV2Token.sol /// @audit optionFactory(), positionOf(), transferTokenPositionFrom(), mint(), burn() 11: interface ITimeswapV2Token is IERC1155 {
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol /// @audit positionOf(), transferTokenPositionFrom(), mint(), burn(), collect() 27: contract TimeswapV2LiquidityToken is ITimeswapV2LiquidityToken, ERC1155Enumerable {
Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining
Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads
Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require()
strings
Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value
There are 2 instances of this issue:
File: packages/v2-token/src/base/ERC1155Enumerable.sol 4: pragma solidity ^0.8.0;
File: packages/v2-token/src/interfaces/IERC1155Enumerable.sol 4: pragma solidity ^0.8.0;
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
There are 19 instances of this issue:
File: packages/v2-library/src/SafeCast.sol /// @audit uint16 result 20: result = uint16(value); /// @audit uint96 result 29: result = uint96(value); /// @audit uint160 result 38: result = uint160(value);
File: packages/v2-pool/src/libraries/ConstantProduct.sol /// @audit uint160 liquidityAmount 67: liquidityAmount = getLiquidityGivenLong(rate, longAmount, !isAdd); /// @audit uint160 liquidityAmount 82: liquidityAmount = getLiquidityGivenShort(rate, shortAmount, duration, !isAdd); /// @audit uint160 liquidityAmount 104: liquidityAmount = getLiquidityGivenLong(rate, amount, !isAdd); /// @audit uint160 liquidityAmount 110: liquidityAmount = getLiquidityGivenShort(rate, amount, duration, !isAdd); /// @audit uint160 newRate 142: newRate = isAdd ? rate + deltaRate : rate - deltaRate; /// @audit uint160 newRate 174: newRate = getNewSqrtInterestRateGivenLong(liquidity, rate, longAmount + (isAdd ? 0 : fees), isAdd); /// @audit uint160 newRate 241: else newRate = getNewSqrtInterestRateGivenLong(liquidity, rate, amount, false); /// @audit uint160 deltaRate 316: deltaRate = FullMath.mulDiv(shortAmount, uint256(1) << 192, denominator, !isAdd).toUint160(); /// @audit uint160 newRate 318: if (isAdd) newRate = rate + deltaRate; /// @audit uint160 newRate 319: else if (rate > deltaRate) newRate = rate - deltaRate;
File: packages/v2-pool/src/libraries/DurationCalculation.sol /// @audit uint96 duration 19: duration = uint256(blockTimestamp).unsafeSub(lastTimestamp).toUint96(); /// @audit uint96 duration 27: duration = maturity.unsafeSub(uint256(blockTimestamp)).toUint96(); /// @audit uint96 duration 35: duration = maturity.unsafeSub(lastTimestamp).toUint96(); /// @audit uint96 duration 44: duration = maturity.min(blockTimestamp).unsafeSub(lastTimestamp).toUint96();
File: packages/v2-pool/src/structs/Pool.sol /// @audit uint160 liquidityAmount 308: liquidityAmount = param.delta.toUint160(), /// @audit uint160 liquidityAmount 398: liquidityAmount = param.delta.toUint160(),
These findings are excluded from awards calculations because there are publicly-available automated tools that find them. The valid ones appear here for completeness
Issue | Instances | Total Gas Saved | |
---|---|---|---|
[Gā01] | <array>.length should not be looked up in every loop of a for -loop | 4 | 12 |
[Gā02] | Using bool s for storage incurs overhead | 1 | 17100 |
[Gā03] | ++i costs less gas than i++ , especially when it's used in for -loops (--i /i-- too) | 2 | 10 |
[Gā04] | Use custom errors rather than revert() /require() strings to save gas | 2 | - |
Total: 9 instances over 4 issues with 17122 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.
<array>.length
should not be looked up in every loop of a for
-loopThe overheads outlined below are PER LOOP, excluding the first loop
MLOAD
(3 gas)CALLDATALOAD
(3 gas)Caching the length changes each of these to a DUP<N>
(3 gas), and gets rid of the extra DUP<N>
needed to store the stack offset
There are 4 instances of this issue:
File: packages/v2-option/src/structs/Process.sol /// @audit (valid but excluded finding) 34: for (uint256 i; i < processing.length; ) {
File: packages/v2-token/src/base/ERC1155Enumerable.sol /// @audit (valid but excluded finding) 48: for (uint256 i; i < ids.length; ) { /// @audit (valid but excluded finding) 82: for (uint256 i; i < ids.length; ) {
File: packages/v2-token/src/TimeswapV2LiquidityToken.sol /// @audit (valid but excluded finding) 227: for (uint256 i; i < ids.length; ) {
bool
s for storage incurs overhead// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27
Use uint256(1)
and uint256(2)
for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from false
to true
, after having been true
in the past
There is 1 instance of this issue:
File: packages/v2-option/src/TimeswapV2Option.sol /// @audit (valid but excluded finding) 53: mapping(uint256 => mapping(uint256 => bool)) private hasInteracted;
++i
costs less gas than i++
, especially when it's used in for
-loops (--i
/i--
too)Saves 5 gas per loop
There are 2 instances of this issue:
File: packages/v2-library/src/FullMath.sol /// @audit (valid but excluded finding) 167: quotient1++;
File: packages/v2-option/src/structs/Process.sol /// @audit (valid but excluded finding) 42: i++;
revert()
/require()
strings to save gasCustom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas
There are 2 instances of this issue:
File: packages/v2-library/src/BytesLib.sol /// @audit (valid but excluded finding) 13: require(_length + 31 >= _length, "slice_overflow"); /// @audit (valid but excluded finding) 14: require(_bytes.length >= _start + _length, "slice_outOfBounds");
#0 - c4-judge
2023-02-02T12:12:51Z
Picodes marked the issue as grade-b