You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The code can be optimized by minimising the number of SLOADs. SLOADs are expensive (100 gas) compared to MLOADs/MSTOREs (3 gas). In the paragraphs below, please see the @audit-issue tags in the pieces of code's comments for more information about SLOADs that could be saved by caching the mentioned storage variables in memory variables.
Unchecking arithmetics operations that can't underflow/overflow
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, or the operation doesn't depend on user input), 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
@audit tags
The code is annotated at multiple places with //@audit comments to pinpoint the issues. Please, pay attention to them for more details.
File: PrePOMarket.sol
Tighly pack storage variables
Here, storage variables can be tightly packed from:
Emitting storage values instead of caching of function arguments
Emitting a storage value can be avoided to save a SLOAD at these places by using the function argument instead:
contracts/core/AccountAccessController.sol:
96 _root = _newRoot;
97: emitRootChanged(_root); //@audit should emit _newRoot
contracts/core/Collateral.sol:
191 _strategyController = _newStrategyController;
192: emitStrategyControllerChanged(address(_strategyController));//@audit should emit address(_strategyController)200 _delayedWithdrawalExpiry = _newDelayedWithdrawalExpiry;
201: emitDelayedWithdrawalExpiryChanged(_delayedWithdrawalExpiry); //@audit should emit address(_newDelayedWithdrawalExpiry)210 _mintingFee = _newMintingFee;
211: emitMintingFeeChanged(_mintingFee); //@audit should emit address(_newMintingFee)220 _redemptionFee = _newRedemptionFee;
221: emitRedemptionFeeChanged(_redemptionFee); //@audit should emit address(_newRedemptionFee)229 _depositHook = _newDepositHook;
230: emitDepositHookChanged(address(_depositHook)); //@audit should emit address(_depositHook)238 _withdrawHook = _newWithdrawHook;
239: emitWithdrawHookChanged(address(_withdrawHook)); //@audit should emit address(_newWithdrawHook)
contracts/core/CollateralDepositRecord.sol:
63 _globalDepositCap = _newGlobalDepositCap;
64: emitGlobalDepositCapChanged(_globalDepositCap); //@audit should emit _newGlobalDepositCap
Incrementing before emitting a storage value instead of emitting the increment's result
These can be optimized:
contracts/core/AccountAccessController.sol:
35 _blockedAccountsIndex++;
36: emitBlockedAccountsCleared(_blockedAccountsIndex); //@audit should use ++_blockedAccountsIndex101 _allowedAccountsIndex++;
102: emitAllowedAccountsCleared(_allowedAccountsIndex); //@audit should use ++_allowedAccountsIndex
Marking these as immutable (as they never change outside the constructor) would avoid them taking space in the storage:
contracts/core/Collateral.sol:
23: addressprivate _treasury;//@audit should be immutable26: IERC20Upgradeableprivate _baseToken; //@audit should be immutable
contracts/core/DepositHook.sol:
11: IAccountAccessController private _accountAccessController; //@audit should be immutable12: ICollateralDepositRecord private _depositRecord; //@audit should be immutable
contracts/core/WithdrawHook.sol:
10: ICollateralDepositRecord private _depositRecord; //@audit should be immutable
Help the optimizer by declaring a storage variable instead of repeatedly fetching the value
To help the optimizer, go from:
File: AccountAccessController.sol
61: function allowSelf(bytes32[] calldata_proof) externaloverride {
62: require(
63: _allowedAccounts[_allowedAccountsIndex][msg.sender] ==false, //@audit help the optimizer + use just require(!_allowedAccounts[_allowedAccountsIndex][msg.sender]) as this here is a comparison to a constant
...
69: _allowedAccounts[_allowedAccountsIndex][msg.sender] =true; //@audit help the optimizer
I suggest using ++i instead of i++ to increment the value of an uint variable.
Increments can be unchecked
In Solidity 0.8+, there's a default overflow check on unsigned integers. It's possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline.
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).
Gas Report
Table of Contents:
_finalLongPrice
_amount + _globalDepositAmount
_amount + _accountToNetDeposit[_sender]
_globalDepositAmount
_accountToNetDeposit
++i
costs less gas compared toi++
Foreword
@audit
tagsFile: PrePOMarket.sol
Tighly pack storage variables
Here, storage variables can be tightly packed from:
to
Which would save 1 storage slot.
function redeem()
Cache
_finalLongPrice
Caching this in memory can save around 2 SLOADs (around 200 gas)
File: CollateralDepositRecord.sol
function recordDeposit()
Cache
_amount + _globalDepositAmount
Cache
_amount + _accountToNetDeposit[_sender]
Optimized code:
function recordWithdrawal()
Cache
_globalDepositAmount
Cache
_accountToNetDeposit
Optimized code:
General recommendations
Storage Variables
Emitting storage values instead of caching of function arguments
Emitting a storage value can be avoided to save a SLOAD at these places by using the function argument instead:
Incrementing before emitting a storage value instead of emitting the increment's result
These can be optimized:
to
Some storage variables should be immutable
Marking these as immutable (as they never change outside the constructor) would avoid them taking space in the storage:
Help the optimizer by declaring a storage variable instead of repeatedly fetching the value
To help the optimizer, go from:
to
Also here, use
require(!_allowedAccounts[_allowedAccountsIndex][msg.sender])
at L63 to avoid the cost of a comparison to a constant.For-Loops
An array's length should be cached to save gas in for-loops
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
Caching the array length in the stack saves around 3 gas per iteration.
Here, I suggest storing the array's length in a variable before the for-loop, and use it instead:
++i
costs less gas compared toi++
++i
costs less gas compared toi++
for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration)i++
incrementsi
and returns the initial value ofi
. Which means:But
++i
returns the actual incremented value:In the first case, the compiler has to create a temporary variable (when used) for returning
1
instead of2
Instances include:
I suggest using
++i
instead ofi++
to increment the value of an uint variable.Increments can be unchecked
In Solidity 0.8+, there's a default overflow check on unsigned integers. It's possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline.
ethereum/solidity#10695
Instances include:
The code would go from:
to:
The risk of overflow is inexistant for a
uint256
here.Arithmetics
Uncheck calculations to save gas when an overflow/underflow is impossible
Instances include:
Errors
Use Custom Errors instead of Revert Strings to save 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/:
Custom errors are defined using the
error
statement, which can be used inside and outside of contracts (including interfaces and libraries).Instances include:
I suggest replacing revert strings with custom errors.
The text was updated successfully, but these errors were encountered: