Platform: Code4rena
Start Date: 21/06/2022
Pot Size: $30,000 USDC
Total HM: 12
Participants: 96
Period: 3 days
Judge: HardlyDifficult
Total Solo HM: 5
Id: 140
League: ETH
Rank: 44/96
Findings: 2
Award: $45.76
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x29A, 0x52, 0xNazgul, 0xNineDec, 0xc0ffEE, 0xf15ers, 0xkatana, BowTiedWardens, Chom, ElKu, Funen, GalloDaSballo, JC, JMukesh, JohnSmith, Lambda, Limbooo, MadWookie, MiloTruck, Nethermind, Noah3o6, Nyamcil, Picodes, PwnedNoMore, Randyyy, RoiEvenHaim, SmartSek, StErMi, Tadashi, TerrierLover, TomJ, Tomio, Treasure-Seeker, UnusualTurtle, Varun_Verma, Wayne, Waze, _Adam, apostle0x01, asutorufos, berndartmueller, c3phas, catchup, cccz, cloudjunky, codexploder, cryptphi, defsec, delfin454000, dipp, ellahi, exd0tpy, fatherOfBlocks, hansfriese, hyh, joestakey, kebabsec, kenta, masterchief, minhquanym, naps62, oyc_109, pashov, peritoflores, reassor, rfa, robee, sach1r0, saian, sashik_eth, shenwilly, simon135, slywaters, sorrynotsorry, sseefried, unforgiven, xiaoming90, ych18, zuhaibmohd, zzzitron
28.2781 USDC - $28.28
Older versions of Solidity contained compilation errors that are fixed in version 0.8.9. The release notes strictly advise against using versions of Solidity older than 0.8.9.
For more information see the following resource:
The following lines should be updated to require the use of Solidity versions that are greater than or equal to 0.8.9:
nibbl-smartcontracts/contracts/Interfaces/IAccessControlMechanism.sol:4:pragma solidity ^0.8.0; nibbl-smartcontracts/contracts/Utilities/AccessControlMechanism.sol:4:pragma solidity ^0.8.0;
#0 - HardlyDifficult
2022-07-03T22:59:22Z
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0x29A, 0xKitsune, 0xNazgul, 0xf15ers, 0xkatana, 8olidity, ACai, BowTiedWardens, Chandr, Chom, ElKu, Fitraldys, Funen, IgnacioB, JC, Lambda, Limbooo, MiloTruck, Noah3o6, Nyamcil, Picodes, Randyyy, SmartSek, StErMi, TerrierLover, TomJ, Tomio, UnusualTurtle, Waze, _Adam, ajtra, c3phas, cRat1st0s, catchup, codexploder, cryptphi, defsec, delfin454000, ellahi, exd0tpy, fatherOfBlocks, hansfriese, joestakey, kebabsec, kenta, m_Rassska, minhquanym, oyc_109, pashov, reassor, rfa, robee, sach1r0, saian, sashik_eth, simon135, slywaters, ych18, ynnad, zuhaibmohd
17.48 USDC - $17.48
Uninitialized variables by default contain a value equivalent to 0: uint
s are initialized to 0; bool
s to false; address
es to address(0)
.
Explicitly assigning these values to variables when they are declared increases gas costs while providing no funciton.
e.g. change this code:
uint256 var = 0;
to
uint256 var;
For more information, please consult the following resources:
Tips and Tricks to Save Gas and Reduce Bytecode Size
The following lines of code are affected:
nibbl-smartcontracts/contracts/Basket.sol:43: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/Basket.sol:70: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/Basket.sol:93: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:506: for (uint256 i = 0; i < _assetAddresses.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:525: for (uint256 i = 0; i < _assets.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:547: for (uint256 i = 0; i < _assets.length; i++) {
Newer versions of the Solidity compiler will check for integer overflows and underflows automatically. This provides safety but increases gas costs.
When an unsigned integer is guaranteed to never overflow, the unchecked
feature of Solidity can be used to save gas costs.
A common case for this is for-loops using a strictly-less-than comparision in their conditional statement, e.g.:
uint256 length = someArray.length; for (uint256 i; i < length; ++i) { }
In cases like this, the maximum value for length
is 2**256 - 1
. Therefore, the maximum value of i
is 2**256 - 2
as it will always be strictly less than length
.
This example can be replaced with the following construction to reduce gas costs:
for (uint i = 0; i < length; i = unchecked_inc(i)) { // do something that doesn't change the value of i } function unchecked_inc(uint i) returns (uint) { unchecked { return i + 1; } }
For more information, consult the following resources:
Solidity docs: underflows, overflows, and unchecked
The following lines of code are affected:
nibbl-smartcontracts/contracts/Basket.sol:43: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/Basket.sol:70: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/Basket.sol:93: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:506: for (uint256 i = 0; i < _assetAddresses.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:525: for (uint256 i = 0; i < _assets.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:547: for (uint256 i = 0; i < _assets.length; i++) {
Using ++i
costs less gas than using i++
. In the context of a for-loop, gas is saved on each iteration.
The following lines of code are affected:
nibbl-smartcontracts/contracts/Basket.sol:43: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/Basket.sol:70: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/Basket.sol:93: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:506: for (uint256 i = 0; i < _assetAddresses.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:525: for (uint256 i = 0; i < _assets.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:547: for (uint256 i = 0; i < _assets.length; i++) {
In the context of a for-loop that iterates over an array, it costs less gas to cache the array's length in a variable and read from this variable rather than use the arrays .length
property. Reading the .length
property for on the array will cause a recalculation of the array's length on each iteration of the loop which is a more expensive operation than reading from a stack variable.
For example, the following code:
for (uint i; i < arr.length; ++i) { // ... }
should be changed to:
uint length = arr.length; for (uint i; i < length; ++i) { // ... }
Note that in the second case, the length of the array must not change during the loop's execution.
For more information, see the following resource:
The following lines of code are affected:
nibbl-smartcontracts/contracts/Basket.sol:43: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/Basket.sol:70: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/Basket.sol:93: for (uint256 i = 0; i < _tokens.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:506: for (uint256 i = 0; i < _assetAddresses.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:525: for (uint256 i = 0; i < _assets.length; i++) { nibbl-smartcontracts/contracts/NibblVault.sol:547: for (uint256 i = 0; i < _assets.length; i++) {
Both memory
and calldata
allow a developer to manage variables within the scope of a function. memory
is mutable which makes it a more flexible tool; however, it costs additional gas when compared with calldata
. calldata
is non-modifiable but cheaper. Therefore it is recommended to use calldata
for function parameters when they will not be modified.
For more information, see the following resources: Solidity Documentation recommending the use of calldata
Solidity — Storage vs Memory vs Calldata
The following function calls can be changed to use calldata
instead of memory
:
nibbl-smartcontracts/contracts/Basket.sol:109: function onERC1155BatchReceived(address, address from, uint256[] memory ids, uint256[] memory amounts, bytes memory) external vi rtual override returns (bytes4) { nibbl-smartcontracts/contracts/NibblVaultFactory.sol:80: function createBasket(address _curator, string memory _mix) public override returns(address) { nibbl-smartcontracts/contracts/NibblVaultFactory.sol:88: function getBasketAddress(address _curator, string memory _mix) public override view returns(address _basket) {
When compiled, Solidity code using the >=
or <=
comparison operators in fact executes two separate checks: one for 'is-equal-to' and a second for 'is-greater-than/is-less-than'. By contrast, using >
or <
performs only one check. Therefore code that is written to use strict comparison operators is more gas-efficient.
If this change is applied, be sure to update the relevant variables being evaluated. For clarity, it is also advised to rename the variables to make this change explicit, e.g. renaming a variable from MINIMUM
to MINIMUM_PLUS_ONE
.
The following lines are affected:
nibbl-smartcontracts/contracts/NibblVault.sol:147: require(buyoutEndTime <= block.timestamp, "NibblVault: buyoutEndTime <= now"); nibbl-smartcontracts/contracts/NibblVault.sol:184: require(_secondaryReserveRatio <= primaryReserveRatio, "NibblVault: Excess initial funds"); nibbl-smartcontracts/contracts/NibblVault.sol:185: require(_secondaryReserveRatio >= MIN_SECONDARY_RESERVE_RATIO, "NibblVault: secResRatio too low"); nibbl-smartcontracts/contracts/NibblVault.sol:224: _feeCurve = _maxSecondaryBalanceIncrease > _feeCurve ? _feeCurve : _maxSecondaryBalanceIncrease; // the curve fee is cap ped so that secondaryReserveBalance <= fictitiousPrimaryReserveBalance nibbl-smartcontracts/contracts/NibblVault.sol:311: if (_totalSupply >= _initialTokenSupply) { nibbl-smartcontracts/contracts/NibblVault.sol:315: if (_lowerCurveDiff >= msg.value) { nibbl-smartcontracts/contracts/NibblVault.sol:325: require(_minAmtOut <= _purchaseReturn, "NibblVault: Return too low"); nibbl-smartcontracts/contracts/NibblVault.sol:351: require(_secondaryReserveBalance - _saleReturn >= MIN_SECONDARY_RESERVE_BALANCE, "NibblVault: Excess sell"); nibbl-smartcontracts/contracts/NibblVault.sol:374: if ((_initialTokenSupply + _amtIn) <= _totalSupply) { nibbl-smartcontracts/contracts/NibblVault.sol:387: require(_saleReturn >= _minAmtOut, "NibblVault: Return too low"); nibbl-smartcontracts/contracts/NibblVault.sol:397: /// Buyout is initiated only when total bid amount >= currentValuation but extra funds over currentValuation are sent back t o bidder. nibbl-smartcontracts/contracts/NibblVault.sol:399: require(block.timestamp >= minBuyoutTime, "NibblVault: minBuyoutTime < now"); nibbl-smartcontracts/contracts/NibblVault.sol:404: require(_buyoutBid >= _currentValuation, "NibblVault: Bid too low"); nibbl-smartcontracts/contracts/NibblVault.sol:426: if (_twav >= buyoutRejectionValuation) { nibbl-smartcontracts/contracts/NibblVault.sol:561: require(block.timestamp <= deadline, "NibblVault: expired deadline"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:107: require(basketUpdateTime != 0 && block.timestamp >= basketUpdateTime, "NibblVaultFactory: UPDATE_TIME has not pas sed"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:131: require(feeToUpdateTime != 0 && block.timestamp >= feeToUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passe d"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:141: require(_newFee <= MAX_ADMIN_FEE, "NibblVaultFactory: Fee value greater than MAX_ADMIN_FEE"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:149: require(feeAdminUpdateTime != 0 && block.timestamp >= feeAdminUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passed"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:166: require(vaultUpdateTime != 0 && block.timestamp >= vaultUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passe d"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:48: require(msg.value >= MIN_INITIAL_RESERVE_BALANCE, "NibblVaultFactory: Initial reserve balance too low");
Using &&
operations within require()
statements increases gas costs. Separate these conditions into separate require()
calls.
The following require()
statements should be refactored:
nibbl-smartcontracts/contracts/NibblVaultFactory.sol:107: require(basketUpdateTime != 0 && block.timestamp >= basketUpdateTime, "NibblVaultFactory: UPDATE_TIME has not pas sed"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:131: require(feeToUpdateTime != 0 && block.timestamp >= feeToUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passe d"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:149: require(feeAdminUpdateTime != 0 && block.timestamp >= feeAdminUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passed"); nibbl-smartcontracts/contracts/NibblVaultFactory.sol:166: require(vaultUpdateTime != 0 && block.timestamp >= vaultUpdateTime, "NibblVaultFactory: UPDATE_TIME has not passe d");
The pragma declaration allows for Solidity versions less than version 0.8.4. Several gas optimization features have been introduced in versions of Solidity between 0.8.0 and 0.8.4, including:
Please note that Solidity version 0.8.9 contains bugfixes in addition to these gas improvements and that if possible it is advised to use versions greater than or equal to 0.8.9 for additional benefit.
For more information consult the following resources:
The following pragma statements should be updated:
nibbl-smartcontracts/contracts/Interfaces/IAccessControlMechanism.sol:4:pragma solidity ^0.8.0; nibbl-smartcontracts/contracts/Utilities/AccessControlMechanism.sol:4:pragma solidity ^0.8.0;