Platform: Code4rena
Start Date: 08/11/2022
Pot Size: $60,500 USDC
Total HM: 6
Participants: 72
Period: 5 days
Judge: Picodes
Total Solo HM: 2
Id: 178
League: ETH
Rank: 7/72
Findings: 2
Award: $1,286.35
š Selected for report: 1
š Solo Findings: 0
š Selected for report: RaymondFam
Also found by: 0x1f8b, 0x52, 0xSmartContract, 0xc0ffEE, 0xhacksmithh, 8olidity, Awesome, BClabs, Bnke0x0, Chom, Deivitto, Hashlock, IllIllI, Josiah, KingNFT, Nyx, R2, ReyAdmirado, Rolezn, SamGMK, Sathish9098, SinceJuly, V_B, Vadis, Waze, a12jmx, adriro, ajtra, aphak5010, bearonbike, bin, brgltd, carlitox477, carrotsmuggler, cccz, ch0bu, chaduke, datapunk, delfin454000, erictee, fatherOfBlocks, fs0c, horsefacts, jayphbee, ktg, ladboy233, pashov, perseverancesuccess, rbserver, ret2basic, tnevler, zaskoh
330.1837 USDC - $330.18
Issue | Instances | |
---|---|---|
[Lā01] | Unused/empty receive() /fallback() function | 2 |
[Lā02] | Missing checks for address(0x0) when assigning values to address state variables | 4 |
[Lā03] | Library function is vulnerable to signature malleability attacks | 1 |
Total: 7 instances over 3 issues
Issue | Instances | |
---|---|---|
[Nā01] | Duplicate import statements | 4 |
[Nā02] | Contract implements interface without extending the interface | 1 |
[Nā03] | constant s should be defined rather than using magic numbers | 15 |
[Nā04] | Constant redefined elsewhere | 3 |
[Nā05] | Lines are too long | 2 |
[Nā06] | Non-library/interface files should use fixed compiler versions, not floating ones | 5 |
[Nā07] | File is missing NatSpec | 7 |
[Nā08] | NatSpec is incomplete | 4 |
[Nā09] | Event is missing indexed fields | 8 |
Total: 49 instances over 9 issues
receive()
/fallback()
functionIf the intention is for the Ether to be used, the function should call another function, otherwise it should revert (e.g. require(msg.sender == address(weth))
). Having no access control on the function means that someone may send Ether to the contract, and have no way to get anything back out, which is a loss of funds
There are 2 instances of this issue:
File: contracts/LooksRareAggregator.sol 222: receive() external payable {}
File: contracts/proxies/SeaportProxy.sol 86: receive() external payable {}
address(0x0)
when assigning values to address
state variablesThere are 4 instances of this issue:
File: contracts/LooksRareAggregator.sol 122: erc20EnabledLooksRareAggregator = _erc20EnabledLooksRareAggregator;
File: contracts/OwnableTwoSteps.sol 102: potentialOwner = newPotentialOwner;
File: contracts/proxies/LooksRareProxy.sol 39: aggregator = _aggregator;
File: contracts/proxies/SeaportProxy.sol 47: aggregator = _aggregator;
Use OpenZeppelin's ECDSA
contract rather than calling ecrecover()
directly
There is 1 instance of this issue:
File: /contracts/SignatureChecker.sol 56 function _recoverEOASigner(bytes32 hash, bytes memory signature) internal pure returns (address signer) { 57 (bytes32 r, bytes32 s, uint8 v) = _splitSignature(signature); 58 59 // If the signature is valid (and not malleable), return the signer address 60 signer = ecrecover(hash, v, r, s); 61 62 if (signer == address(0)) revert NullSignerAddress(); 63: }
There are 4 instances of this issue:
File: contracts/LooksRareAggregator.sol 14: import {BasicOrder, FeeData, TokenTransfer} from "./libraries/OrderStructs.sol"; 15: import {LooksRareProxy} from "./proxies/LooksRareProxy.sol"; 16: import {TokenReceiver} from "./TokenReceiver.sol"; 17: import {TokenRescuer} from "./TokenRescuer.sol";
Not extending the interface may lead to the wrong function signature being used, leading to unexpected behavior. If the interface is in fact being implemented, use the override
keyword to indicate that fact
There is 1 instance of this issue:
File: contracts/TokenReceiver.sol /// @audit IERC1155Receiver.onERC1155Received(), IERC1155Receiver.onERC1155BatchReceived() 4: abstract contract TokenReceiver {
constant
s should be defined rather than using magic numbersEven assembly can benefit from using readable constants instead of hex/numeric literals
There are 15 instances of this issue:
File: contracts/LooksRareAggregator.sol /// @audit 10000 158: if (bp > 10000) revert FeeTooHigh();
File: contracts/proxies/SeaportProxy.sol /// @audit 10000 147: uint256 orderFee = (orders[i].price * feeBp) / 10000; /// @audit 10000 207: uint256 orderFee = (price * feeBp) / 10000;
File: contracts/SignatureChecker.sol /// @audit 64 28: if (signature.length == 64) { /// @audit 0x20 31: r := mload(add(signature, 0x20)) /// @audit 0x40 32: vs := mload(add(signature, 0x40)) /// @audit 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 33: s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) /// @audit 65 36: } else if (signature.length == 65) { /// @audit 0x20 38: r := mload(add(signature, 0x20)) /// @audit 0x40 39: s := mload(add(signature, 0x40)) /// @audit 0x60 40: v := byte(0, mload(add(signature, 0x60))) /// @audit 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 46: if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) revert BadSignatureS(); /// @audit 27 /// @audit 28 48: if (v != 27 && v != 28) revert BadSignatureV(v); /// @audit 0x1626ba7e 80: if (IERC1271(signer).isValidSignature(hash, signature) != 0x1626ba7e) revert InvalidSignatureERC1271();
Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant
in a library
. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.
There are 3 instances of this issue:
File: contracts/proxies/LooksRareProxy.sol /// @audit seen in contracts/ERC20EnabledLooksRareAggregator.sol 31: address public immutable aggregator;
File: contracts/proxies/SeaportProxy.sol /// @audit seen in contracts/proxies/LooksRareProxy.sol 20: SeaportInterface public immutable marketplace; /// @audit seen in contracts/proxies/LooksRareProxy.sol 21: address public immutable aggregator;
Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. Since the files will most likely reside in GitHub, and GitHub starts using a scroll bar in all cases when the length is over 164 characters, the lines below should be split when they reach that length
There are 2 instances of this issue:
File: contracts/proxies/SeaportProxy.sol 8: import {AdvancedOrder, CriteriaResolver, OrderParameters, OfferItem, ConsiderationItem, FulfillmentComponent, AdditionalRecipient} from "../libraries/seaport/ConsiderationStructs.sol"; 35: bytes32 zoneHash; // An arbitrary 32-byte value that will be supplied to the zone when fulfilling restricted orders that the zone can utilize when making a determination on whether to authorize the order
There are 5 instances of this issue:
File: contracts/lowLevelCallers/LowLevelERC1155Transfer.sol 2: pragma solidity ^0.8.14;
File: contracts/lowLevelCallers/LowLevelERC20Approve.sol 2: pragma solidity ^0.8.14;
File: contracts/lowLevelCallers/LowLevelERC20Transfer.sol 4: pragma solidity ^0.8.14;
File: contracts/lowLevelCallers/LowLevelERC721Transfer.sol 2: pragma solidity ^0.8.14;
File: contracts/lowLevelCallers/LowLevelETH.sol 2: pragma solidity ^0.8.14;
There are 7 instances of this issue:
File: contracts/interfaces/IERC1155.sol
File: contracts/interfaces/IERC20.sol
File: contracts/interfaces/IERC721.sol
File: contracts/libraries/OrderEnums.sol
File: contracts/libraries/OrderStructs.sol
File: contracts/libraries/seaport/ConsiderationEnums.sol
File: contracts/TokenReceiver.sol
There are 4 instances of this issue:
File: contracts/proxies/LooksRareProxy.sol /// @audit Missing: '@param bytes' /// @audit Missing: '@param uint256' /// @audit Missing: '@param address' 42 /** 43 * @notice Execute LooksRare NFT sweeps in a single transaction 44 * @dev extraData, feeBp and feeRecipient are not used 45 * @param orders Orders to be executed by LooksRare 46 * @param ordersExtraData Extra data for each order 47 * @param recipient The address to receive the purchased NFTs 48 * @param isAtomic Flag to enable atomic trades (all or nothing) or partial trades 49 */ 50 function execute( 51 BasicOrder[] calldata orders, 52 bytes[] calldata ordersExtraData, 53 bytes memory, 54 address recipient, 55 bool isAtomic, 56 uint256, 57: address
File: contracts/SignatureChecker.sol /// @audit Missing: '@return' 54 * @param signature Bytes containing the signature (64 or 65 bytes) 55 */ 56: function _recoverEOASigner(bytes32 hash, bytes memory signature) internal pure returns (address signer) {
indexed
fieldsIndex event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event
should use three indexed
fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.
There are 8 instances of this issue:
File: contracts/interfaces/IERC1155.sol 15: event ApprovalForAll(address indexed account, address indexed operator, bool approved); 17: event URI(string value, uint256 indexed id);
File: contracts/interfaces/IERC20.sol 5: event Transfer(address indexed from, address indexed to, uint256 value); 7: event Approval(address indexed owner, address indexed spender, uint256 value);
File: contracts/interfaces/IERC721.sol 9: event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
File: contracts/interfaces/ILooksRareAggregator.sol 44: event FeeUpdated(address proxy, uint256 bp, address recipient); 51: event FunctionAdded(address indexed proxy, bytes4 selector); 58: event FunctionRemoved(address indexed proxy, bytes4 selector);
#0 - c4-judge
2022-11-21T19:25:27Z
Picodes marked the issue as grade-b
#1 - c4-judge
2022-11-21T19:26:37Z
Picodes marked the issue as grade-a
#2 - 0xhiroshi
2022-11-24T11:53:43Z
L-01 - invalid for LooksRareAggregator and actually valid for SeaportProxy (I know I mentioned it's invalid before) L-02 - invalid L-03 - addressed in other issues N-01 - valid N-02 - not sure what the problem is exactly N-03 - invalid, addressed in other issues N-04 - invalid N-05 - valid N-06 - invalid, they are general libs N-07 - invalid N-08 - invalid N-09 - invalid, addressed elsewhere
#3 - c4-sponsor
2022-11-25T00:15:56Z
0xhiroshi requested judge review
956.1664 USDC - $956.17
Issue | Instances | Total Gas Saved | |
---|---|---|---|
[Gā01] | Multiple address /ID mappings can be combined into a single mapping of an address /ID to a struct , where appropriate | 1 | - |
[Gā02] | Using calldata instead of memory for read-only arguments in external functions saves gas | 3 | 360 |
[Gā03] | State variables should be cached in stack variables rather than re-reading them from storage | 2 | 194 |
[Gā04] | Optimize names to save gas | 8 | 176 |
[Gā05] | internal functions not called by the contract should be removed to save deployment gas | 2 | - |
[Gā06] | Functions guaranteed to revert when called by normal users can be marked payable | 13 | 273 |
Total: 29 instances over 6 issues with 1003 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.
address
/ID mappings can be combined into a single mapping
of an address
/ID to a struct
, where appropriateSaves 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.
There is 1 instance of this issue:
File: contracts/LooksRareAggregator.sol 45 mapping(address => mapping(bytes4 => bool)) private _proxyFunctionSelectors; 46: mapping(address => FeeData) private _proxyFeeData;
diff --git a/contracts/LooksRareAggregator.sol b/contracts/LooksRareAggregator.sol index f215f39..a5525aa 100644 --- a/contracts/LooksRareAggregator.sol +++ b/contracts/LooksRareAggregator.sol @@ -156,8 +156,10 @@ contract LooksRareAggregator is address recipient ) external onlyOwner { if (bp > 10000) revert FeeTooHigh(); - _proxyFeeData[proxy].bp = bp; - _proxyFeeData[proxy].recipient = recipient; + + FeeData storage pfd = _proxyFeeData[proxy]; + pfd.bp = bp; + pfd.recipient = recipient; emit FeeUpdated(proxy, bp, recipient); }
Note that the numbers below are wrong due to this forge bug, where forge doesn't properly track warm/cold slots in tests
diff --git a/tmp/gas_before b/tmp/gas_after index a3222f9..705e068 100644 --- a/tmp/gas_before +++ b/tmp/gas_after @@ -17 +17 @@ -ā 2504484 ā 12343 ā ā ā ā ā +ā 2504891 ā 12345 ā ā ā ā ā @@ -47 +47 @@ -ā setFee ā 2945 ā 43626 ā 47171 ā 49171 ā 21 ā +ā setFee ā 2945 ā 43631 ā 47176 ā 49176 ā 21 ā @@ -230 +230 @@ -ā 13089423 ā 64155 ā ā ā ā ā +ā 13089823 ā 64157 ā ā ā ā ā @@ -346,0 +347 @@ +Overall gas change: 4929 (0.099%)
calldata
instead of memory
for read-only arguments in external
functions saves gasWhen a function with a memory
array is called externally, the abi.decode()
step has to use a for-loop to copy each index of the calldata
to the memory
index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length
). Using calldata
directly, obliviates the need for such a loop in the contract code and runtime execution. Note that even if an interface defines a function as having memory
arguments, it's still valid for implementation contracs to use calldata
arguments instead.
If the array is passed to an internal
function which passes the array to another internal function where the array is modified and therefore memory
is used in the external
call, it's still more gass-efficient to use calldata
when the external
function uses modifiers, since the modifiers may prevent the internal functions from being called. Structs have the same overhead as an array of length one
Note that I've also flagged instances where the function is public
but can be marked as external
since it's not called by the contract, and cases where a constructor is involved
There are 3 instances of this issue:
File: contracts/proxies/LooksRareProxy.sol 50 function execute( 51 BasicOrder[] calldata orders, 52 bytes[] calldata ordersExtraData, 53 bytes memory, 54 address recipient, 55 bool isAtomic, 56 uint256, 57: address
File: contracts/TokenReceiver.sol 14 function onERC1155Received( 15 address, 16 address, 17 uint256, 18 uint256, 19 bytes memory 20: ) external virtual returns (bytes4) { 24 function onERC1155BatchReceived( 25 address, 26 address, 27 uint256[] memory, 28 uint256[] memory, 29 bytes memory 30: ) external virtual returns (bytes4) {
diff --git a/contracts/TokenReceiver.sol b/contracts/TokenReceiver.sol index a82e815..759d4ac 100644 --- a/contracts/TokenReceiver.sol +++ b/contracts/TokenReceiver.sol @@ -16,7 +16,7 @@ abstract contract TokenReceiver { address, uint256, uint256, - bytes memory + bytes calldata ) external virtual returns (bytes4) { return this.onERC1155Received.selector; } @@ -24,9 +24,9 @@ abstract contract TokenReceiver { function onERC1155BatchReceived( address, address, - uint256[] memory, - uint256[] memory, - bytes memory + uint256[] calldata, + uint256[] calldata, + bytes calldata ) external virtual returns (bytes4) { return this.onERC1155BatchReceived.selector; } diff --git a/contracts/proxies/LooksRareProxy.sol b/contracts/proxies/LooksRareProxy.sol index cbce118..1ebe936 100644 --- a/contracts/proxies/LooksRareProxy.sol +++ b/contracts/proxies/LooksRareProxy.sol @@ -50,7 +50,7 @@ contract LooksRareProxy is IProxy, TokenRescuer, TokenTransferrer, SignatureChec function execute( BasicOrder[] calldata orders, bytes[] calldata ordersExtraData, - bytes memory, + bytes calldata, address recipient, bool isAtomic, uint256,
diff --git a/tmp/gas_before b/tmp/gas_after index a3222f9..94f3986 100644 --- a/tmp/gas_before +++ b/tmp/gas_after @@ -17 +17 @@ -ā 2504484 ā 12343 ā ā ā ā ā +ā 2420786 ā 11925 ā ā ā ā ā @@ -27 +27 @@ -ā execute ā 1370 ā 434636 ā 429633 ā 960082 ā 67 ā +ā execute ā 1370 ā 434530 ā 429633 ā 960082 ā 67 ā @@ -31 +31 @@ -ā onERC1155Received ā 1631 ā 1631 ā 1631 ā 1631 ā 4 ā +ā onERC1155Received ā 1305 ā 1305 ā 1305 ā 1305 ā 4 ā @@ -56 +56 @@ -ā 1402373 ā 6944 ā ā ā ā ā +ā 1321684 ā 6541 ā ā ā ā ā @@ -62 +62 @@ -ā execute ā 268230 ā 373671 ā 361477 ā 503500 ā 4 ā +ā execute ā 268230 ā 373543 ā 361349 ā 503244 ā 4 ā @@ -71 +71 @@ -ā 1683078 ā 8635 ā ā ā ā ā +ā 1699292 ā 8716 ā ā ā ā ā @@ -75 +75 @@ -ā execute ā 1962 ā 260929 ā 279156 ā 506876 ā 23 ā +ā execute ā 1640 ā 260616 ā 278900 ā 506620 ā 23 ā @@ -230 +230 @@ -ā 13089423 ā 64155 ā ā ā ā ā +ā 12924886 ā 63334 ā ā ā ā ā @@ -288 +288 @@ -ā mint ā 26410 ā 28497 ā 26410 ā 31628 ā 5 ā +ā mint ā 26410 ā 28366 ā 26410 ā 31302 ā 5 ā @@ -346,0 +347 @@ +Overall gas change: -1174338 (-24.848%)
The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.
There are 2 instances of this issue:
File: contracts/OwnableTwoSteps.sol /// @audit owner on line 87 91: emit NewOwner(owner); /// @audit earliestOwnershipRenouncementTime on line 114 116: emit InitiateOwnershipRenouncement(earliestOwnershipRenouncementTime);
diff --git a/contracts/OwnableTwoSteps.sol b/contracts/OwnableTwoSteps.sol index 99d8a7d..366bd03 100644 --- a/contracts/OwnableTwoSteps.sol +++ b/contracts/OwnableTwoSteps.sol @@ -84,11 +84,10 @@ abstract contract OwnableTwoSteps is IOwnableTwoSteps { if (ownershipStatus != Status.TransferInProgress) revert TransferNotInProgress(); if (msg.sender != potentialOwner) revert WrongPotentialOwner(); - owner = msg.sender; delete ownershipStatus; delete potentialOwner; - emit NewOwner(owner); + emit NewOwner(owner = msg.sender); } /** @@ -111,9 +110,8 @@ abstract contract OwnableTwoSteps is IOwnableTwoSteps { if (ownershipStatus != Status.NoOngoingTransfer) revert TransferAlreadyInProgress(); ownershipStatus = Status.RenouncementInProgress; - earliestOwnershipRenouncementTime = block.timestamp + delay; - emit InitiateOwnershipRenouncement(earliestOwnershipRenouncementTime); + emit InitiateOwnershipRenouncement(earliestOwnershipRenouncementTime = block.timestamp + delay); } /**
Note that the numbers below are wrong due to this forge bug, where forge doesn't properly track warm/cold slots in tests
diff --git a/tmp/gas_before b/tmp/gas_after index a3222f9..f393f3c 100644 --- a/tmp/gas_before +++ b/tmp/gas_after @@ -174 +174 @@ -ā confirmOwnershipTransfer ā 507 ā 2329 ā 2379 ā 4101 ā 3 ā +ā confirmOwnershipTransfer ā 507 ā 2328 ā 2379 ā 4099 ā 3 ā @@ -180 +180 @@ -ā initiateOwnershipRenouncement ā 529 ā 27941 ā 46039 ā 50039 ā 7 ā +ā initiateOwnershipRenouncement ā 529 ā 27942 ā 46042 ā 50042 ā 7 ā @@ -346,0 +347 @@ +Overall gas change: 7 (0.008%)
public
/external
function names and public
member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted
There are 8 instances of this issue:
File: contracts/ERC20EnabledLooksRareAggregator.sol /// @audit execute() 15: contract ERC20EnabledLooksRareAggregator is IERC20EnabledLooksRareAggregator, LowLevelERC20Transfer {
File: contracts/interfaces/IERC20EnabledLooksRareAggregator.sol /// @audit execute() 7: interface IERC20EnabledLooksRareAggregator {
File: contracts/interfaces/ILooksRareAggregator.sol /// @audit execute() 6: interface ILooksRareAggregator {
File: contracts/interfaces/IProxy.sol /// @audit execute() 6: interface IProxy {
File: contracts/interfaces/SeaportInterface.sol /// @audit fulfillBasicOrder(), fulfillOrder(), fulfillAdvancedOrder(), fulfillAvailableOrders(), fulfillAvailableAdvancedOrders(), matchOrders(), matchAdvancedOrders(), cancel(), validate(), incrementCounter(), getOrderHash(), getOrderStatus(), getCounter(), information() 28: interface SeaportInterface {
File: contracts/LooksRareAggregator.sol /// @audit execute(), setERC20EnabledLooksRareAggregator(), addFunction(), removeFunction(), setFee(), approve(), supportsProxyFunction(), rescueERC721(), rescueERC1155() 25: contract LooksRareAggregator is
File: contracts/OwnableTwoSteps.sol /// @audit cancelOwnershipTransfer(), confirmOwnershipRenouncement(), confirmOwnershipTransfer(), initiateOwnershipTransfer(), initiateOwnershipRenouncement() 11: abstract contract OwnableTwoSteps is IOwnableTwoSteps {
File: contracts/TokenRescuer.sol /// @audit rescueETH(), rescueERC20() 14: contract TokenRescuer is OwnableTwoSteps, LowLevelETH, LowLevelERC20Transfer {
internal
functions not called by the contract should be removed to save deployment gasIf the functions are required by an interface, the contract should inherit from that interface and use the override
keyword
There are 2 instances of this issue:
File: contracts/lowLevelCallers/LowLevelERC1155Transfer.sol 23 function _executeERC1155SafeTransferFrom( 24 address collection, 25 address from, 26 address to, 27 uint256 tokenId, 28: uint256 amount
File: contracts/lowLevelCallers/LowLevelETH.sol 54 function _returnETHIfAnyWithOneWeiLeft() internal { 55: assembly {
payable
If a function modifier such as onlyOwner
is used, the function will revert if a normal user tries to pay the function. Marking the function as payable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are
CALLVALUE
(2),DUP1
(3),ISZERO
(3),PUSH2
(3),JUMPI
(10),PUSH1
(3),DUP1
(3),REVERT
(0),JUMPDEST
(1),POP
(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost
There are 13 instances of this issue:
File: contracts/LooksRareAggregator.sol 120: function setERC20EnabledLooksRareAggregator(address _erc20EnabledLooksRareAggregator) external onlyOwner { 132: function addFunction(address proxy, bytes4 selector) external onlyOwner { 143: function removeFunction(address proxy, bytes4 selector) external onlyOwner { 153 function setFee( 154 address proxy, 155 uint256 bp, 156 address recipient 157: ) external onlyOwner { 173 function approve( 174 address marketplace, 175 address currency, 176 uint256 amount 177: ) external onlyOwner { 197 function rescueERC721( 198 address collection, 199 address to, 200 uint256 tokenId 201: ) external onlyOwner { 213 function rescueERC1155( 214 address collection, 215 address to, 216 uint256[] calldata tokenIds, 217 uint256[] calldata amounts 218: ) external onlyOwner {
File: contracts/OwnableTwoSteps.sol 51: function cancelOwnershipTransfer() external onlyOwner { 68: function confirmOwnershipRenouncement() external onlyOwner { 98: function initiateOwnershipTransfer(address newPotentialOwner) external onlyOwner { 110: function initiateOwnershipRenouncement() external onlyOwner {
File: contracts/TokenRescuer.sol 22: function rescueETH(address to) external onlyOwner { 34: function rescueERC20(address currency, address to) external onlyOwner {
These findings are excluded from awards calculations because there are publicly-available automated tools that find them. The valid ones appear here for completeness
Issue | Instances | Total Gas Saved | |
---|---|---|---|
[Gā01] | <array>.length should not be looked up in every loop of a for -loop | 2 | 6 |
[Gā02] | Using bool s for storage incurs overhead | 1 | 17100 |
Total: 3 instances over 2 issues with 17106 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.
<array>.length
should not be looked up in every loop of a for
-loopThe overheads outlined below are PER LOOP, excluding the first loop
MLOAD
(3 gas)CALLDATALOAD
(3 gas)Caching the length changes each of these to a DUP<N>
(3 gas), and gets rid of the extra DUP<N>
needed to store the stack offset
There are 2 instances of this issue:
File: contracts/proxies/SeaportProxy.sol /// @audit (valid but excluded finding) 126: for (uint256 i; i < availableOrders.length; ) { /// @audit (valid but excluded finding) 187: for (uint256 i; i < orders.length; ) {
bool
s for storage incurs overhead// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27
Use uint256(1)
and uint256(2)
for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from false
to true
, after having been true
in the past
There is 1 instance of this issue:
File: contracts/LooksRareAggregator.sol /// @audit (valid but excluded finding) 45: mapping(address => mapping(bytes4 => bool)) private _proxyFunctionSelectors;
#0 - c4-judge
2022-11-17T17:20:04Z
Picodes marked the issue as grade-a
#1 - 0xhiroshi
2022-11-24T11:47:42Z
G-01 addressed in other issues G-02 valid G-03 valid G-04 invalid G-05 invalid - it's a generalized lib for multiple projects G-06 invalid - addressed in other issues
#2 - c4-sponsor
2022-11-24T11:47:49Z
0xhiroshi requested judge review
#3 - c4-judge
2022-12-11T17:07:50Z
Picodes marked the issue as selected for report
#4 - Picodes
2023-01-06T18:11:43Z
All findings are theoretically valid in my opinion, although it would not make sense to implement 5 and 4 and 6 provide very little value to the sponsor, if any