Platform: Code4rena
Start Date: 13/01/2023
Pot Size: $100,500 USDC
Total HM: 1
Participants: 23
Period: 10 days
Judge: hickuphh3
Total Solo HM: 1
Id: 201
League: ETH
Rank: 20/23
Findings: 1
Award: $140.67
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: horsefacts
Also found by: 0xSmartContract, ABA, Chom, IllIllI, Josiah, RaymondFam, Rickard, Rolezn, brgltd, btk, chaduke, charlesjhongc, csanuragjain, delfin454000, nadin, oyc_109
140.6728 USDC - $140.67
Number | Issues Details | Context |
---|---|---|
[L-01] | Low level calls with solidity version 0.8.14 and lower can result in optimiser bug | 5 |
[L-02] | Address(0) checks | 11 |
[L-03] | Multiple Pragma used | All Contracts |
Number | Issues Details | Context |
---|---|---|
[NC-01] | Include @return parameters in NatSpec comments | All Contracts |
[NC-02] | Contracts should have full test coverage | All Contracts |
[NC-03] | Need Fuzzing test | All Contracts |
[NC-04] | Use a more recent version of solidity | 32 |
[NC-05] | Missing natspec | All Contracts |
[NC-06] | Constants in comparisons should appear on the left side | 11 |
[NC-07] | Use bytes.concat() and string.concat() | 3 |
[NC-08] | Add I prefix to interface contracts | All Interfaces |
[NC-09] | Generate perfect code headers every time | All Contracts |
[NC-10] | Lock pragmas to specific compiler version | All Contracts |
[NC-11] | There is no need to cast a variable that is already an address, such as uint(x) | 2 |
[NC-12] | Consider using delete rather than assigning zero to clear values | 2 |
[NC-13] | Assembly Codes Specific – Should Have Comments | 6 |
[NC-14] | Adding a return statement when the function defines a named return variable, is redundant | 5 |
The protocol is using low level calls with solidity version less then 0.8.14 which can result in optimizer bug.
Ref: https://medium.com/certora/overly-optimistic-optimizer-certora-bug-disclosure-2101e3f7994d
Consider upgrading to solidity 0.8.17
Address(0)
checksCheck of address(0)
to protect the code from (0x0000000000000000000000000000000000000000)
address problem just in case. This is best practice or instead of suggesting that they verify _address != address(0)
, you could add some good NatSpec comments explaining what is valid and what is invalid and what are the implications of accidentally using an invalid address.
constructor(address conduitController) Consideration(conduitController) {}
Solidity pragma versions should be exactly same in all contracts. Currently some contracts use ^0.8.13
and others use ^0.8.17
Use one version for all contracts.
@return
parameters in NatSpec commentsIf Return parameters are declared, you must prefix them with /// @return
. Some code analysis programs do analysis by reading NatSpec details, if they can't see the @return
tag, they do incomplete analysis.
Include the @return
argument in the NatSpec comments.
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.
- What is the overall line coverage percentage provided by your tests?: >90%
Line coverage percentage should be 100%.
In total 14 contracts, more than 54 unchecked{}
are used, the functions used are critical. For this reason, there must be fuzzing tests in the tests of the project. Not seen in tests.
Use should fuzzing test like Echidna. As Alberto Cuesta Canada said: Fuzzing is not easy, the tools are rough, and the math is hard, but it is worth it. Fuzzing gives me a level of confidence in my smart contracts that I didn’t have before. Relying just on unit testing anymore and poking around in a testnet seems reckless now.
Ref: https://medium.com/coinmonks/smart-contract-fuzzing-d9b88e0b0a05
For security, it is best practice to use the latest Solidity version. For the security fix list in the versions.
https://github.com/ethereum/solidity/blob/develop/Changelog.md
pragma solidity ^0.8.13;
Old version of Solidity is used (^0.8.13)
, newer version can be used (0.8.17)
.
It is recommended that Solidity contracts are fully annotated using NatSpec, it is clearly stated in the Solidity official documentation.
In complex projects such as Defi, the interpretation of all functions and their arguments and returns is important for code readability and auditability.
Some code analysis programs do analysis by reading NatSpec details, if they can't see the tags (@param, @dev, @return)
, they do incomplete analysis.
Include NatSpec
comments in the codebase.
Constants in comparisons should appear on the left side, doing so will prevent typo bug.
if (maximumFulfilled == 0) {
Constants should appear on the left side.
if (0 == maximumFulfilled) {
bytes.concat()
and string.concat()
bytes.concat()
vs abi.encodePacked(<bytes>,<bytes>)
string.concat()
vs abi.encodePacked(<string>,<string>)
Use bytes.concat()
and string.concat()
.
I
prefix to interface contractsFilename prefixed by I
usually indicates an interface rather than a contract.
Add I
prefix to interface contracts
I recommend using header for Solidity code layout and readability
/*////////////////////////////////////////////////////////////// TESTING 123 //////////////////////////////////////////////////////////////*/
Pragma statements can be allowed to float when a contract is intended for consumption by other developers, as in the case with contracts in a library or EthPM package. Otherwise, the developer would need to manually update the pragma in order to compile locally.
pragma solidity ^0.8.13;
pragma solidity ^0.8.17;
Ethereum Smart Contract Best Practices - Lock pragmas to specific compiler version.
uint(x)
There is no need to cast a variable that is already an uint, such as uint(0), 0 is also uint.
if (identifierOrCriteria != uint256(0)) {
Use directly variable.
if (identifierOrCriteria != 0) {
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
advancedOrder.numerator = 0;
Use the delete
keyword.
delete advancedOrder.numerator;
Since this is a low level language that is more difficult to parse by readers, include extensive documentation, comments on the rationale behind its use, clearly explaining what each assembly instruction does
This will make it easier for users to trust the code, for reviewers to validate the code, and for developers to build on or update the code.
Note that using Aseembly removes several important security features of Solidity, which can make the code more insecure and more error-prone.
Include NatSpec in assembly codes.
return
statement when the function defines a named return variable, is redundantreturn amount;
#0 - c4-judge
2023-01-25T09:10:45Z
HickupHH3 marked the issue as grade-b