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: 27/59
Findings: 2
Award: $113.89
🌟 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
65.3481 USDC - $65.35
According to the package.json file, the project is using 4.7.0, to be exact ^4.7.2 which is not the latest updated version.
"dependencies": { "@openzeppelin/contracts": "^4.7.2"
VULNERABILITY VULNERABLE VERSION H Improper Verification of Cryptographic Signature <4.7.3
It is recommended to use the latest non vulnerable version 4.8.0
Old version of solidity is used (^0.8.8), but newer version 0.8.17 can be used.
For security, it is recommended to use the latest solidity version. security fix list for different solidity versions can be found in the following link.
https://github.com/ethereum/solidity/blob/develop/Changelog.md
Above issue is found in all contracts.
NatSpec is missing for the following contracts, functions, constructor and modifier
TimeswapV2LiquidityToken.sol
is a main contract of the project. But NatSpec is missing in the code.
It is recommended to use the NatSpec for commenting for better readability and understanding of the code base.
There are 4 more instances of this issue:
https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-option/src/TimeswapV2OptionFactory.sol#L1-L57 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-pool/src/TimeswapV2Pool.sol#L1-L519 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-pool/src/TimeswapV2PoolFactory.sol#L1-L71 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-token/src/base/ERC1155Enumerable.sol#L1-L112
chosenOptionFactory != address(0)
FOR THE NULL ADDRESS CHECK WHEN PASSING IN ADDRESS PARAMETERS INTO THE CONSTRUCTORconstructor(address chosenOptionFactory, address chosenPoolFactory) ERC1155("Timeswap V2 uint160 address") { optionFactory = chosenOptionFactory; poolFactory = chosenPoolFactory; }
It is a best practice to check for the address(0)
when assigning address values to the state variables in solidity.
Above code can be re-written as follows:
constructor(address chosenOptionFactory, address chosenPoolFactory) ERC1155("Timeswap V2 uint160 address") { if (chosenOptionFactory == address(0)) Error.zeroAddress(); if (chosenPoolFactory == address(0)) Error.zeroAddress(); optionFactory = chosenOptionFactory; poolFactory = chosenPoolFactory; }
There are 2 more instances of this issue:
https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-token/src/TimeswapV2Token.sol#L42 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-pool/src/base/OwnableTwoSteps.sol#L19
The best practice is to link the library to the contract by using the using
keyword.
But in the TimeswapV2LiquidityToken.sol
file under the mint
function the check
internal function of library ParaLibrary
is called as follows:
function mint(TimeswapV2LiquidityTokenMintParam calldata param) external returns (bytes memory data) { ParamLibrary.check(param);
But ParamLibrary
is not linked to the contract using the using
keyword. Hence the check
internal function is called using the public API of ParamLibrary
.
This is allowed in the compiler for solidity version 0.8.8
. But in the future solidity versions this could lead to compile time error.
Hence it is adviced to link the ParamLibrary
to the TimeswapV2LiquidityToken.sol
using the using
keyword as follows:
using ParamLibrary for TimeswapV2LiquidityTokenMintParam;
There are 7 more instances of this issue:
https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-token/src/TimeswapV2LiquidityToken.sol#L122 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-token/src/TimeswapV2LiquidityToken.sol#L151 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-token/src/TimeswapV2LiquidityToken.sol#L157 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-token/src/TimeswapV2LiquidityToken.sol#L190 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-token/src/TimeswapV2LiquidityToken.sol#L244 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-token/src/TimeswapV2LiquidityToken.sol#L270 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-option/src/TimeswapV2OptionFactory.sol#L31
openzeppelin
libraries such as Math.sol
and safeMath.sol
are tested and proven codes used by many applications.
Hence it is recommended to use openzeppelin
libraries for math operations by importing them and then customizing the contracts according to the application specific requirements.
In the timeswap project locally coded math libraries (FullMath.sol
,Math.sol
) have been used. This deprives users of the benefits of highly secure and battle tested openzeppelin
math libraries which are prone to less errors.
Further to the above the two step ownership transfer logic is implemented via in-house coded OwnableTwoSteps.sol
. But instead it is recommended to use the tried and tested openzeppelin Ownable2Step.sol
for two step ownership transfer.
function div(uint256 dividend, uint256 divisor, bool roundUp) internal pure returns (uint256 quotient) { quotient = dividend / divisor;
In the Math.sol
library, the div
function is implementing the division of two numbers.
But it is not checking the divisor for the zero value. This could trigger unwanted implications since Math.sol library is used by other libraries (FullMath.sol) and smart contracts.
The above code should implement the zero value check for the divisor as follows:
function div(uint256 dividend, uint256 divisor, bool roundUp) internal pure returns (uint256 quotient) { require(divisor != uint256(0), "Division by Zero"); quotient = dividend / divisor;
https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-library/src/Math.sol#L48-L49
msg.sender.checkIfPendingOwner(pendingOwner); owner = msg.sender; delete pendingOwner; emit AcceptOwner(msg.sender);
Above code only logs the new owner address in the log. But as a best practice it is recommended to log the old owner details as well for future use. So the above code can be refactored as follows:
msg.sender.checkIfPendingOwner(pendingOwner); address oldOwner = owner; owner = msg.sender; delete pendingOwner; emit AcceptOwner(oldOwner, msg.sender);
#0 - c4-judge
2023-02-01T22:14:04Z
Picodes marked the issue as grade-b
🌟 Selected for report: 0xSmartContract
Also found by: 0x1f8b, 0xackermann, Aymen0909, Beepidibop, IllIllI, Iurii3, Rageur, RaymondFam, ReyAdmirado, Rolezn, SaeedAlipoor01988, Udsen, Viktor_Cortess, W0RR1O, W_Max, atharvasama, c3phas, chaduke, descharre, fatherOfBlocks, kaden, matrix_0wl, shark
48.5424 USDC - $48.54
block.timestamp
DIRECTLY TO GET THE CURRENT BLOCK TIMESTAMP WITHOUT CALLING THE INTERNAL FUNCTION blockTimestamp(0)
if (blockTimestamp(0) > maturity) Error.alreadyMatured(maturity, blockTimestamp(0));
This code can be re-written as follows:
if (blockTimestamp(0) > maturity) Error.alreadyMatured(maturity, block.timestamp);
Calling the internal function and doing arithmetic operation will consume additional gas for the specific usecase of blockTimestamp(0)
.
since blockTimestamp(0)
represents the block.timestamp
it recommended to use the latter to save gas rather than unnecessarily calling the internal
function
There are 2 more instances of this issue:
https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-pool/src/TimeswapV2Pool.sol#L159 https://github.com/code-423n4/2023-01-timeswap/blob/main/packages/v2-pool/src/TimeswapV2Pool.sol#L169
pendingOwner = chosenPendingOwner; emit SetOwner(pendingOwner);
In the above code pendingOwner
is a state variable and chosenPendingOwner is calldata variable passed into the function as a parameter.
Above code can be rewritten as follows to save on one SLOAD
and save gas by providing calldata variable to the event.
pendingOwner = chosenPendingOwner; emit SetOwner(chosenPendingOwner);
The private function feeOverflow()
in the FeeCalculaton.sol
is not used inside the contract.
Hence this can be removed from the contract. This will reduce the contract deployment gas cost.
#0 - c4-judge
2023-02-02T11:59:20Z
Picodes marked the issue as grade-b