Platform: Code4rena
Start Date: 22/09/2022
Pot Size: $30,000 USDC
Total HM: 12
Participants: 133
Period: 3 days
Judge: 0xean
Total Solo HM: 2
Id: 165
League: ETH
Rank: 67/133
Findings: 2
Award: $40.88
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: rotcivegaf
Also found by: 0x040, 0x1f8b, 0x4non, 0xNazgul, 0xSmartContract, 0xf15ers, 8olidity, Aymen0909, B2, Bahurum, Bnke0x0, Ch_301, CodingNameKiki, Deivitto, Diana, Funen, IllIllI, JC, JLevick, KIntern_NA, Lambda, OptimismSec, PaludoX0, RockingMiles, Rolezn, Sm4rty, Soosh, Tagir2003, Tointer, TomJ, Triangle, Trust, V_B, Waze, Yiko, __141345__, a12jmx, ajtra, asutorufos, ayeslick, aysha, bbuddha, bharg4v, bobirichman, brgltd, bytera, c3phas, cryptostellar5, cryptphi, csanuragjain, datapunk, delfin454000, durianSausage, exd0tpy, gogo, got_targ, jag, joestakey, karanctf, ladboy233, leosathya, lukris02, mics, millersplanet, natzuu, neko_nyaa, obront, oyc_109, parashar, peritoflores, rbserver, ret2basic, rokinot, ronnyx2017, rvierdiiev, sach1r0, seyni, sikorico, slowmoses, tnevler, yasir, yongskiws
28.0705 USDC - $28.07
Unlocked pragma Contracts should be deployed using the same compiler version/flags with which they have been tested. Locking the pragma (for e.g. by not using ^ in pragma solidity 0.5.10) ensures that contracts do not accidentally get deployed using an older compiler version with unfixed bugs. https://swcregistry.io/docs/SWC-103
Missing zero address validation Setters of address type parameters should include a zero-address check otherwise contract functionality may become inaccessible or tokens burnt forever. https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation
Unindexed event parameters Parameters of certain events are expected to be indexed (e.g. ERC20 Transfer/Approval events) so that they’re included in the block’s bloom filter for faster access. Failure to do so might confuse off-chain tooling looking for such indexed events. https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-oarameters
Uncalled public functions Public functions that are never called from within the contracts should be declared external to save gas. https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external
NatSpec Comments NatSpec stands for “Ethereum Natural Language Specification Format.” These are written with a triple slash (///) or a double asterisk block(/** ... */) directly above function declarations or statements to generate documentation in JSON format for developers and end-users. It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI). These comments contain different types of tags: https://docs.soliditylang.org/en/v0.8.17/natspec-format.html
@title: A title that should describe the contract/interface
@author: The name of the author (for contract, interface)
@notice: Explain to an end user what this does (for contract, interface, function, public state variable, event)
@dev: Explain to a developer any extra details (for contract, interface, function, state variable, event)
@param: Documents a parameter (just like in doxygen) and must be followed by parameter name (for function, event)
@return: Documents the return variables of a contract’s function (function, public state variable)
@inheritdoc: Copies all missing tags from the base function and must be followed by the contract name (for function, public state variable)
@custom…: Custom tag, semantics is application-defined (for everywhere)
🌟 Selected for report: pfapostol
Also found by: 0x040, 0x1f8b, 0x4non, 0x5rings, 0xA5DF, 0xNazgul, 0xSmartContract, 0xmatt, 0xsam, Amithuddar, Aymen0909, B2, Ben, Bnke0x0, Chom, CodingNameKiki, Deivitto, Diana, Fitraldys, Funen, IllIllI, JAGADESH, JC, Metatron, Ocean_Sky, PaludoX0, Pheonix, RaymondFam, ReyAdmirado, RockingMiles, Rohan16, Rolezn, Satyam_Sharma, Sm4rty, SnowMan, SooYa, Tagir2003, TomJ, Tomio, Triangle, V_B, Waze, __141345__, ajtra, albincsergo, asutorufos, aysha, beardofginger, bobirichman, brgltd, bulej93, bytera, c3phas, ch0bu, cryptostellar5, cryptphi, d3e4, delfin454000, dharma09, drdr, durianSausage, emrekocak, erictee, fatherOfBlocks, gogo, got_targ, imare, jag, karanctf, ladboy233, leosathya, lukris02, medikko, mics, millersplanet, natzuu, neko_nyaa, oyc_109, peanuts, prasantgupta52, rbserver, ret2basic, rokinot, ronnyx2017, rotcivegaf, sach1r0, samruna, seyni, slowmoses, tnevler, wagmi, zishansami
12.8108 USDC - $12.81
Upgrade Pragma to at Least 0.8.4 Using newer compiler versions and the optimizer give gas optimizations. Also, additional safety checks are available for free.
The advantages here are:
Copying a Full Array From Storage to Memory Isn’t Optimal Here, what’s happening is a full copy of a storage array in memory, and then a second copy of each memory element in a CToken struct:
Validator[] memory original_validators = validators; //@audit this here is a VERY expensive copy of a whole storage array in memory (as many SLOADs as there are elements) // Clear the original validators list delete validators; // Fill the new validators array with all except the value to remove for (uint256 i = 0; i < original_validators.length; ++i) { if (i != remove_idx) { validators.push(original_validators[i]); //@audit here is a copy of a memory value in a memory variable
The code should be optimized that way:
Help the Optimizer by Saving a Storage Variable’s Reference Instead of Repeatedly Fetching It To help the optimizer, declare a storage type variable and use it instead of repeatedly fetching the reference in a map or an array.
The effect can be quite significant.
As an example, instead of repeatedly calling “popped”, save its reference like this: Validator storage popped = validators[numVals - 1]; and use it.
Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if-statement Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn’t possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked block: https://docs.soliditylang.org/en/v0.8.10/control-structures.html#checked-or-unchecked-arithmetic
Boolean Comparisons Comparing to a constant (true or false) is a bit more expensive than directly checking the returned boolean value. I suggest using require(minters[msg.sender], "Only minters"); instead of require(minters[msg.sender] == true, "Only minters"); here (same for require statements):
It costs more gas to initialize non-constant/non-immutable variables to zero than to let the default of zero be applied Not overwriting the default for stack variables saves 8 gas. Storage and memory variables have larger savings
require()/revert() strings longer than 32 bytes cost extra gas Each extra memory word of bytes past the original 32 incurs an MSTORE: https://gist.github.com/hrkrshnn/ee8fabd532058307229d65dcd5836ddc#consider-having-short-revert-strings which costs 3 gas
Use Custom Errors Rather Than Revert()/require() Strings To Save Deployment Gas Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Source: https://blog.soliditylang.org/2021/04/21/custom-errors/:
Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
I suggest replacing revert strings with custom errors.