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 use of a logical AND in place of double if is slightly less gas efficient in instances where there isn't a corresponding else statement for the given if statement
Total: 88 instances over 36 issues with 89246 gas saved.
Gas totals use lower bounds of ranges and count two iterations of each for-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.
G001 - Cache Array Length Outside of Loop:
If not cached, the solidity compiler will always read the length of the array during each iteration. That is, if it is a storage array, this is an extra sload operation (100 additional extra gas for each iteration except for the first) and if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first).
File: src/Helpers.sol
164for (uint256 j; j < owners.length;) {
228for (uint256 i =1; i < modules.length;) {
G002 - Using private rather than public for constants, saves gas:
If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
Click to show 6 findings
File: src/PalmeraGuard.sol
20stringpublic constant NAME ="Palmera Guard";
23stringpublic constant VERSION ="0.2.0";
G004 - Use calldata instead of memory for function arguments that do not get mutated:
Mark data types as calldata instead of memory where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in as calldata. The one exception to this is if the argument must later be passed into another function that takes an argument that specifies memory storage.
G005 - Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate:
Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
File: src/DenyHelper.sol
40mapping(bytes32=>uint256) public listCount;
4142/// @dev Mapping of Orgs to Wallets Deny or Allowed43/// @dev Org ID ---> Mapping of Orgs to Wallets Deny or Allowed44mapping(bytes32=>mapping(address=>address)) internal listed;
G006 - Multiple accesses of a mapping/array should use a local variable cache.:
The instances below point to the second+ access of a value inside a mapping/array, within a function. Caching a mapping's value in a local storage or calldata variable when the value is accessed multiple times, saves ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations. Caching an array's struct avoids recalculating the array offsets into memory/calldata
G010 - The result of function calls should be cached rather than re-calling the function:
Caching the result of a function call in a local variable when the function is called multiple times can save gas due to avoiding the need to execute the function code multiple times.
G011 - Stack variable used as a cheaper cache for a state variable is only used once:
If the variable is only accessed once, it's cheaper to use the state variable directly that one time, and save the 3 gas the extra stack assignment would spend.
Payable functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided. A constructor can safely be marked as payable, since only the deployer would be able to pass funds, and the project itself would not pass any funds.
Using the scratch space for event arguments (two words or fewer) will save gas over needing Solidity's full abi memory expansion used for emitting normally.
G015 - State variables only set in the constructor should be declared immutable:
Avoids a Gsset (**20000 gas**) in the constructor, and replaces the first access in each transaction (Gcoldsload - **2100 gas**) and each access thereafter (Gwarmacces - **100 gas**) with a `PUSH32` (**3 gas**).
While `string`s are not value types, and therefore cannot be `immutable`/`constant` if not hard-coded outside of the constructor, the same behavior can be achieved by making the current contract `abstract` with `virtual` functions for the `string` accessors, and having a child contract override the functions with the hard-coded implementation-specific values.
G017 - Use uint256(1)/uint256(2) instead for true and false boolean states:
If you don't use boolean for storage you will avoid Gwarmaccess 100 gas. In addition, state changes of boolean from true to false can cost up to ~20000 gas rather than uint256(2) to uint256(1) that would cost significantly less.
File: src/DenyHelper.sol
35mapping(bytes32=>bool) public allowFeature;
36mapping(bytes32=>bool) public denyFeature;
G018 - ++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops:
The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop.
File: src/ReentrancyAttack.sol
102for (uint256 i; i < owners.length; ++i) {
G019 - keccak256() should only need to be called on a specific string literal once:
It should be saved to an immutable variable, and the variable used instead. If the hash is being used as a part of a function selector, the cast to bytes4 should also only be done once.
The IR-based code generator was introduced with an aim to not only allow code generation to be more transparent and auditable but also to enable more powerful optimization passes that span across functions.
You can enable it on the command line using `--via-ir` or with the option `{"viaIR": true}`.
This will take longer to compile, but you can just simple test it before deploying and if you got a better benchmark then you can add --via-ir to your deploy command
More on: https://docs.soliditylang.org/en/v0.8.17/ir-breaking-changes.html
File: Various Files
None
G021 - Consider using bytes32 rather than a string:
Using the bytes types for fixed-length strings is more efficient than having the EVM have to incur the overhead of string processing. Consider whether the value needs to be a string. A good reason to keep it as a string would be if the variable is defined in an interface that this project does not own.
File: src/PalmeraGuard.sol
20stringpublic constant NAME ="Palmera Guard";
23stringpublic constant VERSION ="0.2.0";
G026 - Avoid updating storage when the value hasn't changed:
If the old value is equal to the new value, not re-storing the value will avoid a Gsreset (2900 gas), potentially at the expense of a Gcoldsload (2100 gas) or a Gwarmaccess (100 gas).
G027 - The use of a logical AND in place of double if is slightly less gas efficient in instances where there isn't a corresponding else statement for the given if statement:
Using a double if statement instead of logical AND (&&) can provide similar short-circuiting behavior whereas double if is slightly more efficient.
G028 - Use the inputs/results of assignments rather than re-reading state variables:
When a state variable is assigned, it saves gas to use the value being assigned, later in the function, rather than re-reading the state variable itself. If needed, it can also be stored to a local variable, and be used in that way. Both options avoid a Gwarmaccess (100 gas). Note that if the operation is, say +=, the assignment also results in a value which can be used. The instances below point to the first reference after the assignment, since later references are already covered by issues describing the caching of state variable values.
The state variable should be cached in and read from a local variable, or accumulated in a local variable then written to storage once outside of the loop, rather than reading/updating it on every iteration of the loop, which will replace each Gwarmaccess (100 gas) with a much cheaper stack read.
File: src/PalmeraGuard.sol
84for (uint256 i =1; i < palmeraModule.indexId();) {
The instances below point to the second+ access of a state variable, via a storage pointer, within a function. Caching the value replaces each Gwarmaccess (100 gas) with a much cheaper stack read.
File: src/PalmeraModule.sol
441for (uint256 i; i < superSafe.child.length;) {
454for (uint256 i; i < _safe.child.length;) {
625for (uint256 i; i < oldSuper.child.length;) {
G031 - State variables only set in the constructor should be declared immutable:
Avoids a Gsset (20000 gas) in the constructor, and replaces the first access in each transaction (Gcoldsload - 2100 gas) and each access thereafter (Gwarmacces - 100 gas) with a PUSH32 (3 gas). While strings are not value types, and therefore cannot be immutable/constant if not hard-coded outside of the constructor, the same behavior can be achieved by making the current contract abstract with virtual functions for the string accessors, and having a child contract override the functions with the hard-coded implementation-specific values.
File: src/ReentrancyAttack.sol
23 PalmeraModule public palmeraModule;
Use the function/modifier's local copy of the state variable, rather than incurring an extra Gwarmaccess (100 gas). In the unlikely event that the state variable hasn't already been used by the function/modifier, consider whether it is really necessary to include it in the event, given the fact that it incurs a Gcoldsload (2100 gas), or whether it can be passed in to or back out of the functions that do use it
Github username: @saidqayoumsadat
Twitter username: S2AQ143
Submission hash (on-chain): 0xa9465f09b2906684a983510bc80a886bbe434fe0e236a2b930984a58e8cf3cf5
Severity: gas saving
Description:
Code changed Link: https://github.com/saidqayoumsadat/Palmera-contest
gas report link: https://github.com/saidqayoumsadat/Palmera-contest-gas-report
Summary
Gas Optimizations
private
rather thanpublic
for constants, saves gasaddress(0)
>=
costs less gas than>
immutable
_msgSender()
if not supporting EIP-2771uint256(1)
/uint256(2)
instead fortrue
andfalse
boolean states++i
/i++
should beunchecked{++i}
/unchecked{i++}
when it is not possible for them to overflow, as is the case when used infor
- andwhile
-loopskeccak256()
should only need to be called on a specific string literal oncevia-ir
for deployingunchecked {}
can be used on the division of twouints
in order to save gasTotal: 88 instances over 36 issues with 89246 gas saved.
Gas totals use lower bounds of ranges and count two iterations of each for-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.
G001 - Cache Array Length Outside of Loop:
If not cached, the solidity compiler will always read the length of the array during each iteration. That is, if it is a storage array, this is an extra sload operation (100 additional extra gas for each iteration except for the first) and if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first).
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/Helpers.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
G002 - Using
private
rather thanpublic
for constants, saves gas:If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
Click to show 6 findings
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraRoles.sol
G003 - Use assembly to check for
address(0)
:Saves 6 gas per instance
Click to show 4 findings
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/DenyHelper.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/Helpers.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
G004 - Use
calldata
instead ofmemory
for function arguments that do not get mutated:Mark data types as
calldata
instead ofmemory
where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in ascalldata
. The one exception to this is if the argument must later be passed into another function that takes an argument that specifiesmemory
storage.Click to show 7 findings
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/SafeInterfaces.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/SigningUtils.sol
G005 - Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, where appropriate:
Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/DenyHelper.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
G006 - Multiple accesses of a mapping/array should use a local variable cache.:
The instances below point to the second+ access of a value inside a mapping/array, within a function. Caching a mapping's value in a local storage or calldata variable when the value is accessed multiple times, saves ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations. Caching an array's struct avoids recalculating the array offsets into memory/calldata
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraRoles.sol
G007 - Internal functions only called once can be inlined to save gas:
Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.
Click to show 4 findings
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraRoles.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/SigningUtils.sol
G008 - Optimize names to save gas:
public/external function names and public member variable names can be optimized to save gas.
Click to show 11 findings
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/DenyHelper.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/Helpers.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraRoles.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/SigningUtils.sol
G009 - Structs should group like types together to save gas:
Structs can be more gas-efficient by grouping together members of the same type. This ordering can potentially save gas.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/SigningUtils.sol
G010 - The result of function calls should be cached rather than re-calling the function:
Caching the result of a function call in a local variable when the function is called multiple times can save gas due to avoiding the need to execute the function code multiple times.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
G011 - Stack variable used as a cheaper cache for a state variable is only used once:
If the variable is only accessed once, it's cheaper to use the state variable directly that one time, and save the 3 gas the extra stack assignment would spend.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
G012 - Constructors can be marked payable:
Payable functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided. A constructor can safely be marked as payable, since only the deployer would be able to pass funds, and the project itself would not pass any funds.
Click to show 4 findings
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraRoles.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
G013 -
>=
costs less gas than>
:The compiler uses opcodes
GT
andISZERO
for solidity code that uses>
, but only requiresLT
for>=
, which saves 3 gas.https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/Helpers.sol
G014 - Use assembly to emit events:
Using the scratch space for event arguments (two words or fewer) will save gas over needing Solidity's full abi memory expansion used for emitting normally.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraRoles.sol
G015 - State variables only set in the constructor should be declared
immutable
:https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
G016 - Don't use
_msgSender()
if not supporting EIP-2771:https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraRoles.sol
G017 - Use
uint256(1)
/uint256(2)
instead fortrue
andfalse
boolean states:If you don't use boolean for storage you will avoid Gwarmaccess 100 gas. In addition, state changes of boolean from
true
tofalse
can cost up to ~20000 gas rather thanuint256(2)
touint256(1)
that would cost significantly less.https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/DenyHelper.sol
G018 -
++i
/i++
should beunchecked{++i}
/unchecked{i++}
when it is not possible for them to overflow, as is the case when used infor
- andwhile
-loops:The
unchecked
keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop.https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
G019 -
keccak256()
should only need to be called on a specific string literal once:It should be saved to an immutable variable, and the variable used instead. If the hash is being used as a part of a function selector, the cast to
bytes4
should also only be done once.https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/SigningUtils.sol
G020 - Consider activating
via-ir
for deploying:File: Various Files None
G021 - Consider using bytes32 rather than a string:
Using the bytes types for fixed-length strings is more efficient than having the EVM have to incur the overhead of string processing. Consider whether the value needs to be a string. A good reason to keep it as a string would be if the variable is defined in an interface that this project does not own.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraRoles.sol
G022 - Inverting the condition of an if-else-statement:
Flipping the true and false blocks instead saves 3 gas.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
G023 -
unchecked {}
can be used on the division of twouints
in order to save gas:The division cannot overflow, since both the numerator and the denominator are non-negative.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/Helpers.sol
G024 - Private functions used once can be inlined:
Private functions used once can be inlined to save GAS
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
G025 - Use assembly to calculate hashes to save gas:
Using assembly to calculate hashes can save 80 gas per instance
Click to show 6 findings
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/Helpers.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/SigningUtils.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/libraries/Constants.sol
G026 - Avoid updating storage when the value hasn't changed:
If the old value is equal to the new value, not re-storing the value will avoid a Gsreset (2900 gas), potentially at the expense of a Gcoldsload (2100 gas) or a Gwarmaccess (100 gas).
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
G027 - The use of a logical
AND
in place of doubleif
is slightly less gas efficient in instances where there isn't a corresponding else statement for the given if statement:Using a double if statement instead of logical AND (&&) can provide similar short-circuiting behavior whereas double if is slightly more efficient.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/Helpers.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
G028 - Use the inputs/results of assignments rather than re-reading state variables:
When a state variable is assigned, it saves gas to use the value being assigned, later in the function, rather than re-reading the state variable itself. If needed, it can also be stored to a local variable, and be used in that way. Both options avoid a Gwarmaccess (100 gas). Note that if the operation is, say +=, the assignment also results in a value which can be used. The instances below point to the first reference after the assignment, since later references are already covered by issues describing the caching of state variable values.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
G029 - State variable read in a loop:
The state variable should be cached in and read from a local variable, or accumulated in a local variable then written to storage once outside of the loop, rather than reading/updating it on every iteration of the loop, which will replace each Gwarmaccess (100 gas) with a much cheaper stack read.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraGuard.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
G030 - Storage re-read via storage pointer:
The instances below point to the second+ access of a state variable, via a storage pointer, within a function. Caching the value replaces each Gwarmaccess (100 gas) with a much cheaper stack read.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
G031 - State variables only set in the constructor should be declared immutable:
Avoids a Gsset (20000 gas) in the constructor, and replaces the first access in each transaction (Gcoldsload - 2100 gas) and each access thereafter (Gwarmacces - 100 gas) with a PUSH32 (3 gas). While strings are not value types, and therefore cannot be immutable/constant if not hard-coded outside of the constructor, the same behavior can be achieved by making the current contract abstract with virtual functions for the string accessors, and having a child contract override the functions with the hard-coded implementation-specific values.
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/ReentrancyAttack.sol
G032 - Use local variables for emitting:
Use the function/modifier's local copy of the state variable, rather than incurring an extra Gwarmaccess (100 gas). In the unlikely event that the state variable hasn't already been used by the function/modifier, consider whether it is really necessary to include it in the event, given the fact that it incurs a Gcoldsload (2100 gas), or whether it can be passed in to or back out of the functions that do use it
https://github.com/saidqayoumsadat/Palmera-contest/blob/master/src/PalmeraModule.sol
The text was updated successfully, but these errors were encountered: