Platform: Code4rena
Start Date: 05/10/2022
Pot Size: $50,000 USDC
Total HM: 2
Participants: 80
Period: 5 days
Judge: GalloDaSballo
Id: 168
League: ETH
Rank: 54/80
Findings: 2
Award: $83.13
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0x4non
Also found by: 0x1f8b, 0xNazgul, 0xSmartContract, Deivitto, IllIllI, Lambda, RaymondFam, Rolezn, RustyRabbit, Trust, arcoun, bin2chen, brgltd, csanuragjain, d3e4, enckrish, exd0tpy, ladboy233, nicobevi, rbserver, rotcivegaf, simon135, zzykxx
50.4817 USDC - $50.48
Number | Issues Details | Context |
---|---|---|
[N-01 ] | Insufficient coverage file | |
[N-02] | Include return parameters in NatSpec comments | All contracts |
[N-03] | 0 address check | 12 |
[N-04] | DOMAIN_TYPEHASH can change in future | 2 |
[N-05] | Long lines are not suitable for the Solidity Style Guide | 5 |
[N-06] | abicoder v2 is enabled by default | 1 |
[N-07] | Use a more recent version of Solidity | All contracts |
[N-08] | Require revert cause should be known | 4 |
[N-09] | Use require instead of assert | 1 |
[N-10] | For modern and more readable code; update import usages | 3 |
[N-11] | WETH address definition can be use directly | 1 |
[N-12] | Empty blocks should be removed or Emit something | 1 |
[N-13] | Function writing that does not comply with the Solidity Style Guide | 1 |
[N-14] | Importing IERC20 is sufficient for ERC20 transactions | 1 |
[N-15] | Implement some type of version counter that will be incremented automatically for contract upgrades | |
[N-16] | Use underscores for number literals | 1 |
[N-17] | Not using the named return variables anywhere in the function is confusing | 1 |
[N-18] | Compliance with NatSpec rules in Constant expressions | 1 |
[N-19] | NatSpec is Missing | 8 |
[N-20] | Omissions in Events | 4 |
[N-21] | BlurExchange.sol missing initial ownership Event | |
[N-22] | There is no need to use the _exists function | |
[N-23] | Constant values such as a call to keccak256(), should used to immutable rather than constant |
Total 23 issues
Number | Issues Details | Context |
---|---|---|
[L-01] | Add to blacklist function | |
[L-02] | Highest risk must be specified in NatSpec comments and documentation | |
[L-03] | Use abi.encode() to avoid collision | 4 |
[L-04] | Add to indexed parameter for countable Events | 2 |
[L-05] | Use block.number instead block.timestamp | 1 |
[L-06] | Use Ownable2StepUpgradeable instead of OwnableUpgradeable contract | 1 |
[L-07] | Unused import | 1 |
[L-08] | Use SafeTransferOwnership instead of transferOwnership function | 1 |
[L-09] | Owner can renounce Ownership | 1 |
Total 9 issues
Number | Suggestion Details |
---|---|
[S-01] | Generate perfect code headers every time |
[S-02] | Mark visibility of initialize(...) functions as external |
Total 2 suggestions
Description: The test coverage rate of the PolicyManager.sol file is 26%. Testing all functions is best practice in terms of security criteria.
-----------------------------|----------|----------|----------|----------|----------------| File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines | -----------------------------|----------|----------|----------|----------|----------------| contracts/ | 90.34 | 78.57 | 87.18 | 89.86 | | BlurExchange.sol | 99.12 | 86.76 | 96 | 99.05 | 503 | ExecutionDelegate.sol | 88.24 | 60 | 88.89 | 88.89 | 77,78 | PolicyManager.sol | 26.67 | 16.67 | 40 | 26.67 |... 75,77,78,81 | contracts/lib/ | 91.67 | 25 | 92.86 | 88.89 | | EIP712.sol | 100 | 100 | 100 | 100 | | ERC1967Proxy.sol | 100 | 100 | 100 | 100 | | MerkleVerifier.sol | 75 | 0 | 75 | 70 | 22,23,24 | OrderStructs.sol | 100 | 100 | 100 | 100 | | ReentrancyGuarded.sol | 100 | 50 | 100 | 100 | | contracts/matchingPolicies/ | 100 | 100 | 100 | 100 | | StandardPolicyERC1155.sol | 100 | 100 | 100 | 100 | | StandardPolicyERC721.sol | 100 | 100 | 100 | 100 | | contracts/test/ | 100 | 100 | 100 | 100 | | TestBlurExchange.sol | 100 | 100 | 100 | 100 | | -----------------------------|----------|----------|----------|----------|----------------| All files | 90.91 | 76.14 | 90 | 90.12 | | -----------------------------|----------|----------|----------|----------|----------------|
return parameters
in NatSpec commentsContext: All contracts
Description: It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI). It is clearly stated in the Solidity official documentation. In complex projects such as Defi, the interpretation of all functions and their arguments and returns is important for code readability and auditability.
https://docs.soliditylang.org/en/v0.8.15/natspec-format.html
If Return parameters are declared, you must prefix them with "/// @return".
Some code analysis programs do analysis by reading NatSpec details, if they can't see the "@return" tag, they do incomplete analysis.
Recommendation: Include return parameters in NatSpec comments
Recommendation Code Style:
/// @notice information about what a function does /// @param pageId The id of the page to get the URI for. /// @return Returns a page's URI if it has been minted function tokenURI(uint256 pageId) public view virtual override returns (string memory) { if (pageId == 0 || pageId > currentId) revert("NOT_MINTED"); return string.concat(BASE_URI, pageId.toString()); }
0 address
checkContext: BlurExchange.sol#L445-L446 BlurExchange.sol#L498-L499 BlurExchange.sol#L471-L472 BlurExchange.sol#L346 BlurExchange.sol#L95 ExecutionDelegate.sol#L36 ExecutionDelegate.sol#L46 EIP712.sol#L16 ERC1967Proxy.sol#L21 BlurExchange.sol#L116 BlurExchange.sol#L113 BlurExchange.sol#L499
Description: Also check of the address to protect the code from 0x0 address problem just in case. This is best practice or instead of suggesting that they verify address != 0x0, you could add some good NatSpec comments explaining what is valid and what is invalid and what are the implications of accidentally using an invalid address.
Recommendation:
like this;
if (_treasury == address(0)) revert ADDRESS_ZERO();
Context: EIP712.sol#L33 EIP712.sol#L39
Description: DOMAIN_TYPEHASH is assigned to the contract as a constant, the chain id value may change in hardforks, so a possible hardfork will also become invalid after deployment.
Recommendation:
constructor() { DOMAIN_SEPARATOR_CHAIN_ID = block.chainid; _DOMAIN_SEPARATOR = _calculateDomainSeparator(); } function _calculateDomainSeparator() internal view returns (bytes32 domainSeperator) { domainSeperator = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256(bytes("1")), block.chainid, address(this) ) ); } /// @notice EIP-712 typehash for this contract's domain. function DOMAIN_SEPARATOR() public view returns (bytes32 domainSeperator) { domainSeperator = block.chainid == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(); }
Solidity Style Guide
Context: EIP712.sol#L24 EIP712.sol#L27 BlurExchange.sol#L388 BlurExchange.sol#L425 BlurExchange.sol#L429
Description: It is generally recommended that lines in the source code should not exceed 80-120 characters. Today's screens are much larger, so in some cases it makes sense to expand that. The lines above should be split when they reach that length, as the files will most likely be on GitHub and GitHub always uses a scrollbar when the length is more than 164 characters.
(why-is-80-characters-the-standard-limit-for-code-width)[https://softwareengineering.stackexchange.com/questions/148677/why-is-80-characters-the-standard-limit-for-code-width]
Recommendation: Multiline output parameters and return statements should follow the same style recommended for wrapping long lines found in the Maximum Line Length section.
https://docs.soliditylang.org/en/v0.8.17/style-guide.html#introduction
abicoder v2
is enabled by defaultContext: ExecutionDelegate.sol#L3
Description: abicoder v2 is considered non-experimental as of Solidity 0.6.0 and it is enabled by default starting with Solidity 0.8.0. Therefore, there is no need to write
Context: All contracts
Description: The solidity version 0.8.13 has below two issues:
Vulnerability related to ABI-encoding
.
ref : https://blog.soliditylang.org/2022/05/18/solidity-0.8.14-release-announcement/
Vulnerability related to Optimizer Bug Regarding Memory Side Effects of Inline Assembly
ref : https://blog.soliditylang.org/2022/06/15/solidity-0.8.15-release-announcement/
Recommendation:
Old version of Solidity is used (0.8.13)
, newer version can be used (0.8.17)
Context:* BlurExchange.sol#L134 BlurExchange.sol#L183 BlurExchange.sol#L452 BlurExchange.sol#L513
Description: Vulnerability related to description is not written to require, it must be written so that the user knows why the process is reverted.
Recommendation: Current Code:
require(sell.order.side == Side.Sell);
Recommendation Code:
require(sell.order.side == Side.Sell), "Sell has invalid parameters");
require
instead of assert
Context: ERC1967Proxy.sol#L22
Description:
Assert should not be used except for tests, require
should be used
Prior to Solidity 0.8.0, pressing a confirm consumes the remainder of the process's available gas instead of returning it, as request()/revert() did.
Assertion() should be avoided even after solidity version 0.8.0, because its documentation states "The Assert function generates an error of type Panic(uint256). Code that works properly should never Panic, even on invalid external input. If this happens, you need to fix it in your contract. there's a mistake".
Context: BlurExchange.sol#L5-L16 ExecutionDelegate.sol#L5-L8 ERC1967Proxy.sol#L5_L6
Description:
Our Solidity code is also cleaner in another way that might not be noticeable: the struct Point. We were importing it previously with global import but not using it. The Point struct polluted the source code
with an unnecessary object we were not using because we did not need it. This was breaking the rule of modularity and modular programming: only import what you need
Specific imports with curly braces allow us to apply this rule better.
Recommendation:
import {contract1 , contract2} from "filename.sol";
WETH
address definition can be use directlyContext: BlurExchange.sol#L63
Description: WETH is a wrap Ether contract with a specific address in the Etherum network, giving the option to define it may cause false recognition, it is healthier to define it directly.
Advantages of defining a specific contract directly:
WETH Address : 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
constructor(address _manager, address _weth) payable initializer { manager = IManager(_manager); WETH = _weth; }
Recommendation:
address private constant WETH = 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2; constructor(address _manager) payable initializer { manager = IManager(_manager); }
Empty blocks
should be removed or Emit somethingContext: BlurExchange.sol#L53
Description: Code contains empty block.
Recommendation: The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting.
Current code:
function _authorizeUpgrade(address _newImpl) internal override onlyOwner {}
Suggested code:
function _authorizeUpgrade(address _newImpl) internal override onlyOwner { emit _authorizeUpgrade(address _newImpl); }
Function writing
that does not comply with the Solidity Style Guide
Context: BlurExchange.sol
Description: Order of Functions; ordering helps readers identify which functions they can call and to find the constructor and fallback definitions easier. But there are contracts in the project that do not comply with this.
https://docs.soliditylang.org/en/v0.8.17/style-guide.html
Functions should be grouped according to their visibility and ordered:
Context: BlurExchange.sol#L8
Description: In order to run the functions of erc20, it is sufficient to import only IERC20.
Description:
As part of the upgradeability of UUPS Proxies
, the contract can be upgraded multiple times, where it is a systematic approach to record the version of each upgrade.
I suggest implementing some kind of version counter that will be incremented automatically when you upgrade the contract.
Recommendation:
uint256 public authorizeUpgradeCounter; function _authorizeUpgrade(address _newImpl) internal view override onlyOwner { authorizeUpgradeCounter+=1; }
Description: There is occasions where certain number have been hardcoded, either in variable or in the code itself. Large numbers can become hard to read.
Recommendation: Consider using underscores for number literals to improve its readability.
Context: BlurExchange.sol#L419
Recommendation: Consider changing the variable to be an unnamed one.
Context: BlurExchange.sol#L57-L58
Recommendation: Variables are declared as constant utilize the UPPER_CASE_WITH_UNDERSCORES format.
NatSpec is missing for the following function:
MerkleVerifier.sol#L49 MerkleVerifier.sol#L45 BlurExchange.sol#L242 BlurExchange.sol#L233 BlurExchange.sol#L224 BlurExchange.sol#L215 BlurExchange.sol#L47 BlurExchange.sol#L43
Throughout the codebase, events are generally emitted when sensitive changes are made to the contracts. However, some events are missing important parameters
The events should include the new value and old value where possible: Events with no old value;; BlurExchange.sol#L239 BlurExchange.sol#L230 BlurExchange.sol#L221 BlurExchange.sol#L209
BlurExchange.sol
missing initial ownership EventThe BlurExchange.initialize function does not emit an initial OwnershipTransferred event.
Recommended Mitigation Steps
In initialize, emit OwnershipTransferred(address(0), owner_)
_exists
function_exists
function is used for _executeTokenTransfer
functions assert collection exist.
if an address contains code, it's not an EOA but a contract account. However, a contract does not have source code available during construction. This means that while the constructor is running, it can make calls to other contracts, but extcodesize for its address returns zero. Using this is inefficient
function _executeTokenTransfer( address collection, address from, address to, uint256 tokenId, uint256 amount, AssetType assetType ) internal { /* Assert collection exists. */ require(_exists(collection), "Collection does not exist"); /* Call execution delegate. */ if (assetType == AssetType.ERC721) { executionDelegate.transferERC721(collection, from, to, tokenId); } else if (assetType == AssetType.ERC1155) { executionDelegate.transferERC1155(collection, from, to, tokenId, amount); } } /** * @dev Determine if the given address exists * @param what address to check */ function _exists(address what) internal view returns (bool) { uint size; assembly { size := extcodesize(what) } return size > 0; } }
Recommended Mitigation Steps
Remove to require
from function;
require(_exists(collection), "Collection does not exist");
There is a difference between constant variables and immutable variables, and they should each be used in their appropriate contexts.
While it doesn't save any gas because the compiler knows that developers often make this mistake, it's still best to use the right tool for the task at hand.
Constants should be used for literal values written into the code, and immutable variables should be used for expressions, or values calculated in, or passed into the constructor.
There are 4 instances of this issue:
contracts/lib/EIP712.sol: 20: bytes32 constant public FEE_TYPEHASH = keccak256(... 23: bytes32 constant public ORDER_TYPEHASH = keccak256(... 26: bytes32 constant public ORACLE_ORDER_TYPEHASH = keccak256(... 29: bytes32 constant public ROOT_TYPEHASH = keccak256(...
Description:
Cryptocurrency mixing service, Tornado Cash, has been blacklisted in the OFAC.
A lot of blockchain companies, token projects, NFT Projects have blacklisted
all Ethereum addresses owned by Tornado Cash listed in the US Treasury Department's sanction against the protocol.
https://home.treasury.gov/policy-issues/financial-sanctions/recent-actions/20220808
In addition, these platforms even ban accounts that have received ETH on their account with Tornadocash.
Some of these Projects; USDC (https://www.circle.com/en/usdc) Flashbots (https://www.paradigm.xyz/portfolio/flashbots ) Aave (https://aave.com/) Uniswap Balancer Infura Alchemy Opensea dYdX
Details on the subject; https://twitter.com/bantg/status/1556712790894706688?s=20&t=HUTDTeLikUr6Dv9JdMF7AA
For this reason, every project in the Ethereum network must have a blacklist function, this is a good method to avoid legal problems in the future, apart from the current need.
Transactions from the project by an account funded by Tonadocash or banned by OFAC can lead to legal problems.Especially American citizens may want to get addresses to the blacklist legally, this is not an obligation
If you think that such legal prohibitions should be made directly by validators, this may not be possible: https://www.paradigm.xyz/2022/09/base-layer-neutrality
The ban on Tornado Cash makes little sense, because in the end, no one can prevent people from using other mixer smart contracts, or forking the existing ones. It neither hinders cybercrime, nor privacy.
Here is the most beautiful and close to the project example; Manifold
Manifold Contract https://etherscan.io/address/0xe4e4003afe3765aca8149a82fc064c0b125b9e5a#code
modifier nonBlacklistRequired(address extension) { require(!_blacklistedExtensions.contains(extension), "Extension blacklisted"); _; }
Recommended Mitigation Steps add to Blacklist function and modifier.
Description: Although the UUPS model has many advantages and the proposed proxy model is currently the UUPS model, there are some caveats we should be aware of before applying it to a real-world project.
One of the main attention: Since upgrades are done through the implementation contract with the help of the upgradeTo
method, there is a high risk of new implementations such as excluding the upgradeTo
method, which can permanently kill the smart contract's ability to upgrade. Also, this pattern is somewhat complicated to implement compared to other proxy patterns.
Recommendation: Therefore, this highest risk must be found in NatSpec comments and documents.
abi.encode()
to avoid collisionContext: EIP712.sol#L80 EIP712.sol#L117 EIP712.sol#L129 EIP712.sol#L144
Description: If you use keccak256(abi.encodePacked(a, b)) and both a and b are dynamic types, it is easy to craft collisions in the hash value by moving parts of a into b and vice-versa. More specifically, abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c"). If you use abi.encodePacked for signatures, authentication or data integrity, make sure to always use the same types and check that at most one of them is dynamic. Unless there is a compelling reason, abi.encode should be preferred.
When are these used?
abi.encode:
abi.encode encode its parameters using the ABI specs. The ABI was designed to make calls to contracts.
Parameters are padded to 32 bytes. If you are making calls to a contract you likely have to use abi.encode
If you are dealing with more than one dynamic data type as it prevents collision.
abi.encodePacked:
abi.encodePacked encode its parameters using the minimal space required by the type. Encoding an uint8 it will use 1 byte. It is used when you want to save some space, and not calling a contract.Takes all types of data and any amount of input.
Proof of Concept:
contract Contract0 { // abi.encode // (AAA,BBB) keccak = 0xd6da8b03238087e6936e7b951b58424ff2783accb08af423b2b9a71cb02bd18b // (AA,ABBB) keccak = 0x54bc5894818c61a0ab427d51905bc936ae11a8111a8b373e53c8a34720957abb function collision(string memory _text, string memory _anotherText) public pure returns (bytes32) { return keccak256(abi.encode(_text, _anotherText)); } } contract Contract1 { // abi.encodePacked // (AAA,BBB) keccak = 0xf6568e65381c5fc6a447ddf0dcb848248282b798b2121d9944a87599b7921358 // (AA,ABBB) keccak = 0xf6568e65381c5fc6a447ddf0dcb848248282b798b2121d9944a87599b7921358 function hard(string memory _text, string memory _anotherText) public pure returns (bytes32) { return keccak256(abi.encodePacked(_text, _anotherText)); } }
Context: BlurExchange.sol#L86 BlurExchange.sol#L90
Recommendation: Add to indexed parameter for countable Events.
block.number
instead block.timestampContext: BlurExchange.sol#L283
Description: Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.
Recommendation: Block timestamps should not be used for entropy or generating random numbers—i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.
Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking) , It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.
Ownable2StepUpgradeable
instead of OwnableUpgradeable
contractContext: BlurExchange.sol#L7
Description:
transferOwnership
function is used to change Ownership from OwnableUpgradeable.sol
.
There is another Openzeppelin Ownable contract (Ownable2StepUpgradeable.sol) has transferOwnership
function , use it is more secure due to 2-stage ownership transfer.
Context: BlurExchange.sol#L8
Description: ERC20.sol import file is not used, if ERC20 functions are to be used, IERC20 must be imported
safeTransferOwnership
instead of transferOwnership
functionContext: ExecutionDelegate.sol#L8
Description:
transferOwnership
function is used to change Ownership from Ownable.sol
.
Use a 2 structure transferOwnership which is safer.
safeTransferOwnership
, use it is more secure due to 2-stage ownership transfer.
Recommendation:
Use Ownable2Step.sol
Ownable2Step.sol
Context: BlurExchange.sol#L7
Description: Typically, the contract’s owner is the account that deploys the contract. As a result, the owner is able to perform certain privileged activities.
The Openzeppelin’s Ownable used in this project contract implements renounceOwnership. This can represent a certain risk if the ownership is renounced for any other reason than by design. Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner.
onlyOwner
functions;
7 results - 3 files contracts/BlurExchange.sol: 42 43: function open() external onlyOwner { 44 isOpen = 1; 47: function close() external onlyOwner { 48 isOpen = 0; 53: function _authorizeUpgrade(address) internal override onlyOwner {} 54 contracts/ExecutionDelegate.sol: 51: function approveContract(address _contract) onlyOwner external { 52 contracts[_contract] = true; 63: function denyContract(address _contract) onlyOwner external { 64 contracts[_contract] = false; contracts/PolicyManager.sol: 25: function addPolicy(address policy) external override onlyOwner { 26 require(!_whitelistedPolicies.contains(policy), "Already whitelisted"); 36: function removePolicy(address policy) external override onlyOwner { 37 require(_whitelistedPolicies.contains(policy), "Not whitelisted");
Recommendation: We recommend to either reimplement the function to disable it or to clearly specify if it is part of the contract design.
Description: I recommend using header for Solidity code layout and readability
https://github.com/transmissions11/headers
external
Description: If someone wants to extend via inheritance, it might make more sense that the overridden initialize(...) function calls the internal {...}_init function, not the parent public initialize(...) function.
External instead of public would give more the sense of the initialize(...) functions to behave like a constructor (only called on deployment, so should only be called externally)
Security point of view, it might be safer so that it cannot be called internally by accident in the child contract
It might cost a bit less gas to use external over public
It is possible to override a function from external to public (= "opening it up") ✅ but it is not possible to override a function from public to external (= "narrow it down"). ❌
For above reasons you can change initialize(...) to external
https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3750
#0 - GalloDaSballo
2022-10-22T20:44:46Z
[N-01 ] | Insufficient coverage file NC
[N-02] | Include return parameters in NatSpec comments NC
[N-03] | 0 address check L
[N-04] | DOMAIN_TYPEHASH can change in future L
[N-05] | Long lines are not suitable for the Solidity Style Guide NC
[N-06] | abicoder v2 is enabled by default NC
[N-07] | Use a more recent version of Solidity NC
[N-08] | Require revert cause should be known NC
[N-09] | Use require instead of assert R
[N-10] | For modern and more readable code; update import usages NC
[N-11] | WETH address definition can be use directly Disagree as the deployer may want to reuse the code in a different chain (also really minor)
[N-12] | Empty blocks should be removed or Emit something Disagree for that specific line as the sponsor probably just wants to make the code compile and doesn't plan to use the function
[N-13] | Function writing that does not comply with the Solidity Style Guide NC
[N-14] | Importing IERC20 is sufficient for ERC20 transactions Don't believe it makes a different
[N-15] | Implement some type of version counter that will be incremented automatically for contract upgrades Nice idea, R
[N-16] | Use underscores for number literals NC
[N-17] | Not using the named return variables anywhere in the function is confusing NC
[N-18] | Compliance with NatSpec rules in Constant expressions Skipping as already awarded natspec findings
[N-19] | NatSpec is Missing Same
[N-20] | Omissions in Events Not convinced by this one, it's fine as is
[N-21] | BlurExchange.sol missing initial ownership Event NC
[N-22] | There is no need to use the _exists function Sounds off as it doesn't explain why it wouldn't be used, the code uses the check to verify a contract exists which is the correct way (you should not use it to prove a contract doesn't exist)
[N-23] | Constant values such as a call to keccak256(), should used to immutable rather than constant This is wrong and has been for years now
[L-01] | Add to blacklist function Nope
[L-02] | Highest risk must be specified in NatSpec comments and documentation Disagree as not falsifiable
[L-03] | Use abi.encode() to avoid collision Disagree in lack of proof of a clash
[L-04] | Add to indexed parameter for countable Events By definition Non-Critical
[L-05] | Use block.number instead block.timestamp Simply not good advice
[L-06] | Use Ownable2StepUpgradeable instead of OwnableUpgradeable contract NC
[L-07] | Unused import NC
[L-08] | Use SafeTransferOwnership instead of transferOwnership function Same as L-06
[L-09] | Owner can renounce Ownership Disagree that this is an issue, renouncing ownership is a good thing
Disagree with both suggestions
Overall good NC, decent list of selectors but the Low findings are misguided, recommend either finding more manually or just not sending them
#1 - GalloDaSballo
2022-10-22T20:45:29Z
2L 1R 14NC
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0xNazgul, 0xSmartContract, Aymen0909, Heuss, Lambda, Pheonix, RaymondFam, ReyAdmirado, Ruhum, Shinchan, Shishigami, __141345__, adriro, ajtra, c3phas, ch0bu, cryptostellar5, d3e4, enckrish, gogo, halden, lucacez, mcwildy, medikko, neko_nyaa, pedr02b2, pfapostol, ret2basic, rvierdiiev, saian, sakman, sakshamguruji
32.6464 USDC - $32.65
Number | Optimization Details | Per gas saved | Context |
---|---|---|---|
[G-01] | Making the values assigned to the State Variable 1 and 2 instead of 0 and 1 is more gas-optimized | 20k | 1 |
[G-02] | Using uint256 instead of bool in mappings is more gas efficient | 146 | 4 |
[G-03] | Use assembly to write address storage values | 33 | 12 |
[G-04] | Use assembly to check for address(0) | 6 | 3 |
[G-05] | Catching the array length prior to loop | 13 | 3 |
[G-06] | Use ++index instead of index++ to increment a loop counter | 5 | 5 |
[G-07] | Use Custom Errors rather than revert() / require() strings to save deployment gas | 68 | 22 |
[G-08] | Using private rather than public for constants, saves gas | 10 | 8 |
[G-09] | Functions guaranteed to revert when callled by normal users can be marked ``payable` | 24 | 10 |
[G-10] | x += y costs more gas than x = x + y for state variables | 16 | 2 |
[G-11] | Direct writing of variables with constant definition saves gas | ~500 | 1 |
[G-12] | Reduce the size of error messages (Long revert Strings) | 18 | 1 |
[G-13] | Use double require instead of using && | 2 | |
[G-14] | Structs can be packed into fewer storage slots | 20k | 1 |
[G-15] | Optimize names to save gas | 22 | All contracts |
[G-16] | Delete - ABI Coder v2 for gas optimization | 1 | |
[G-17] | No need return keyword for gas efficient | 3 | 2 |
[G-18] | Instead of pre-calculating the state to be broadcast in an emit, calculate in the emit, this method saves gas | 4 | |
[G-19] | In ReentrancyGuard, an architecture with uint256 instead of bool has a more gas effect | 1 | |
[G-20] | ERC20 import | 1 |
Total 20 issues
Number | Suggestion Details |
---|---|
[S-01] | Missing zero-address check in `constructor |
[S-02] | Use v4.8.0 OpenZeppelin contracts |
[S-03] | Slot Packing can be done |
[S-04] | The solady Library's Ownable contract is significantly gas-optimized, which can be used |
Total 4 suggestions
Context: BlurExchange.sol#L43-L50
Description: When you change a state slot from 0 (the default) to non-0, you are increasing the size of the overall state data. The overall blockchain data - the "world state" - is 256-bits bigger than it was, and you are paying for that privilege. In the real world, this means you are storing extra data on the HDDs/SSDs of anyone running a full or archive node. If you're only changing the data from a non-0 value to another non-0 value, you are not increasing the size of the data, and not asking everyone running a node to increase the use of their storage hardware.
Recommendation:
Make open
and close
variable values 1 and 2 instead of 0 and 1. You can see the gas savings in the table below
Proof Of Concept: The optimizer was turned on and set to 10000 runs
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.open(); c0.close(); c0.change1(); c0.change2(); c1.open(); c1.close(); c1.change1(); c1.change2(); } } contract Contract0 { uint256 public isOpen; modifier whenOpen() { require(isOpen == 1, "Closed"); _; } event Opened(); event Closed(); function open() external { isOpen = 1; emit Opened(); } function close() external { isOpen = 0; emit Closed(); } function change1() external { isOpen = 0; emit Closed(); } function change2() external { isOpen = 1; emit Closed(); } } contract Contract1 { uint256 public isOpen; modifier whenOpen() { require(isOpen == 2, "Closed"); _; } event Opened(); event Closed(); function open() external { isOpen = 2; emit Opened(); } function close() external { isOpen = 1; emit Closed(); } function change1() external { isOpen = 1; emit Closed(); } function change2() external { isOpen = 2; emit Closed(); } }
Gas Report:
╭──────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/test.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞══════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 72523 ┆ 394 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ change1 ┆ 1040 ┆ 1040 ┆ 1040 ┆ 1040 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ change2 ┆ 20921 ┆ 20921 ┆ 20921 ┆ 20921 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ close ┆ 816 ┆ 816 ┆ 816 ┆ 816 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ open ┆ 23065 ┆ 23065 ┆ 23065 ┆ 23065 ┆ 1 │ ╰──────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯ ╭──────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/test.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞══════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 72923 ┆ 396 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ change1 ┆ 1043 ┆ 1043 ┆ 1043 ┆ 1043 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ change2 ┆ 1021 ┆ 1021 ┆ 1021 ┆ 1021 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ close ┆ 1022 ┆ 1022 ┆ 1022 ┆ 1022 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ open ┆ 23065 ┆ 23065 ┆ 23065 ┆ 23065 ┆ 1 │ ╰──────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯
uint256
instead of bool
in mappings is more gas efficient [146 gas per instance]Context: BlurExchange.sol#L71 BlurExchange.sol#L72 ExecutionDelegate.sol#L18 ExecutionDelegate.sol#L19
Description: OpenZeppelin uint256 and bool comparison: 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. The values being non-zero value makes deployment a bit more expensive.
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.
Recommendation: Use uint256(1) and uint256(2) instead of bool.
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0._setPolicyPermissions(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 79, true); c1._setPolicyPermissions(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 79, 2); } } contract Contract0 { mapping(address => mapping(uint256 => bool)) public modulePermissions; error boolcheck(); function _setPolicyPermissions(address addr_, uint256 id, bool log) external { modulePermissions[addr_][id] = log; if(modulePermissions[addr_][id] = false) revert boolcheck(); } } contract Contract1 { mapping(address => mapping(uint256 => uint256)) public modulePermissions; error boolcheck(); // Use uint256 instead of bool (1 = false , 2 = true) function _setPolicyPermissions(address addr_, uint256 id, uint256 log) external { modulePermissions[addr_][id] == log; if(modulePermissions[addr_][id] == 1) revert boolcheck(); } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 88335 ┆ 473 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ _setPolicyPermissions ┆ 2750 ┆ 2750 ┆ 2750 ┆ 2750 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 87135 ┆ 467 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ _setPolicyPermissions ┆ 2604 ┆ 2604 ┆ 2604 ┆ 2604 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
assembly
to write address storage values [33 gas per instance]Context: BlurExchange.sol#L44 BlurExchange.sol#L48 BlurExchange.sol#L113 BlurExchange.sol#L114 BlurExchange.sol#L115 BlurExchange.sol#L116 BlurExchange.sol#L117 BlurExchange.sol#L246 BlurExchange.sol#L238 BlurExchange.sol#L229 BlurExchange.sol#L220 BlurExchange.sol#L208
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTestFoundry is DSTest { Contract1 c1; Contract2 c2; function setUp() public { c1 = new Contract1(); c2 = new Contract2(); } function testGas() public { c1.setRewardTokenAndAmount(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,356); c2.setRewardTokenAndAmount(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4,356); } } contract Contract1 { address rewardToken ; uint256 reward; function setRewardTokenAndAmount(address token_, uint256 reward_) external { rewardToken = token_; reward = reward_; } } contract Contract2 { address rewardToken ; uint256 reward; function setRewardTokenAndAmount(address token_, uint256 reward_) external { assembly { sstore(rewardToken.slot, token_) sstore(reward.slot, reward_) } } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 50899 ┆ 285 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ setRewardTokenAndAmount ┆ 44490 ┆ 44490 ┆ 44490 ┆ 44490 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract2 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 38087 ┆ 221 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ setRewardTokenAndAmount ┆ 44457 ┆ 44457 ┆ 44457 ┆ 44457 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯
address(0)
[6 gas per instance]Context: BlurExchange.sol#L219 BlurExchange.sol#L228 BlurExchange.sol#L237 BlurExchange.sol#L265 BlurExchange.sol#L451 BlurExchange.sol#L506
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public view { c0.setOperator(address(this)); c1.setOperator(address(this)); } } contract Contract0 { function setOperator(address operator_) public pure { require(operator_) != address(0), "INVALID_RECIPIENT"); } } contract Contract1 { function setOperator(address operator_) public pure { assembly { if iszero(operator_) { mstore(0x00, "Callback_InvalidParams") revert(0x00, 0x20) } } } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 50899 ┆ 285 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ setOperator ┆ 258 ┆ 258 ┆ 258 ┆ 258 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 44893 ┆ 255 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ setOperator ┆ 252 ┆ 252 ┆ 252 ┆ 252 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
Context: BlurExchange.sol#L199 BlurExchange.sol#L476 MerkleVerifier.sol#L38
Description: One can save gas by caching the array length (in stack) and using that set variable in the loop. Replace state variable reads and writes within loops with local variable reads and writes. This is done by assigning state variable values to new local variables, reading and/or writing the local variables in a loop, then after the loop assigning any changed local variables to their equivalent state variables.
Recommendation:
Simply do something like so before the for loop: uint length = variable.length
Then add length in place of variable.length
in the for loop.
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract1 c1; Contract2 c2; function setUp() public { c1 = new Contract1(); c2 = new Contract2(); } function testGas() public { address[] memory Inputs = new address[](6); Inputs[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; Inputs[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; Inputs[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; Inputs[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; Inputs[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; Inputs[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; c1.executeProposal(Inputs); c2.executeProposal(Inputs); } } contract Contract1 { function executeProposal(address[] memory test) public pure { uint256 a = test.length; for (uint256 step; step < a; ) { unchecked { ++step; } } } } contract Contract2 { function executeProposal(address[] memory test) public pure { for (uint256 step; step < test.length; ) { unchecked { ++step; } } } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 92941 ┆ 496 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ executeProposal ┆ 1653 ┆ 1653 ┆ 1653 ┆ 1653 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract2 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 92541 ┆ 494 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ executeProposal ┆ 1666 ┆ 1666 ┆ 1666 ┆ 1666 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
++index
instead of index++
to increment a loop counter [5 gas per instance]Context: MerkleVerifier.sol#L38 BlurExchange.sol#L199 BlurExchange.sol#L476 PolicyManager.sol#L77 EIP712.sol#L77
Description: Due to reduced stack operations, using ++index saves 5 gas per iteration.
Recommendation: Use ++index to increment a loop counter.
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.foo(); c1.foo(); } } contract Contract0 { function foo() external { uint256 versionNFTDropCollection; ++versionNFTDropCollection; } } contract Contract1 { function foo() external { uint256 versionNFTDropCollection; versionNFTDropCollection++; } }
Gas Report:
╭──────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/test.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞══════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 42893 ┆ 245 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 193 ┆ 193 ┆ 193 ┆ 193 ┆ 1 │ ╰──────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭──────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/test.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞══════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 43293 ┆ 247 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 198 ┆ 198 ┆ 198 ┆ 198 ┆ 1 │ ╰──────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
Custom Errors
rather than revert() / require() strings to save deployment gas [68 gas per instance]Context: BlurExchange.sol#L36, BlurExchange.sol#L134, BlurExchange.sol#L139, BlurExchange.sol#L140, BlurExchange.sol#L142, BlurExchange.sol#L143, BlurExchange.sol#L183, BlurExchange.sol#L219, BlurExchange.sol#L228, BlurExchange.sol#L237, BlurExchange.sol#L318, BlurExchange.sol#L407, BlurExchange.sol#L424, BlurExchange.sol#L428, BlurExchange.sol#L431, BlurExchange.sol#L483, BlurExchange.sol#L534, BlurExchange.sol#L140
ExecutionDelegate.sol#L22, ExecutionDelegate.sol#L77, ExecutionDelegate.sol#L92, ExecutionDelegate.sol#L124
Description: Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they’re hitby avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas. https://blog.soliditylang.org/2021/04/21/custom-errors/
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.foo(); c1.foo(); } } contract Contract0 { uint256 version; error Error_Message(); function foo() external { if (version != 0) { revert Error_Message(); } } } contract Contract1 { uint256 version; function foo() external { require(version == 0, "error"); } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 33287 ┆ 196 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 2242 ┆ 2242 ┆ 2242 ┆ 2242 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 41693 ┆ 239 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 2310 ┆ 2310 ┆ 2310 ┆ 2310 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
private
rather than public
for constants, saves gas [ 10 gas per instance]Context: BlurExchange.sol#L57 BlurExchange.sol#L58 BlurExchange.sol#L59 EIP712.sol#L20 EIP712.sol#L23 EIP712.sol#L26 EIP712.sol#L29 EIP712.sol#L33
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.gas(); c1.gas(); } } contract Contract0 { uint256 public constant BASE_GAS = 36000; function gas() external returns(uint256 gasPrice) { gasPrice = gasleft() + BASE_GAS; } } contract Contract1 { uint256 private constant BASE_GAS = 36000; function gas() external returns(uint256 gasPrice) { gasPrice = gasleft() + BASE_GAS; } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 43693 ┆ 249 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ gas ┆ 263 ┆ 263 ┆ 263 ┆ 263 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 40893 ┆ 235 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ gas ┆ 253 ┆ 253 ┆ 253 ┆ 253 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
payable
[24 gas per instance]Context: BlurExchange.sol#L43, BlurExchange.sol#L47, BlurExchange.sol#L215, BlurExchange.sol#L224, BlurExchange.sol#L233, BlurExchange.sol#L242, PolicyManager.sol#L25, PolicyManager.sol#L36, ExecutionDelegate.sol#L36, ExecutionDelegate.sol#L45
Description: If a function modifier or require such as onlyOwner-admin 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.
Recommendation:
Functions guaranteed to revert when called by normal users can be marked payable (for only onlyowner or admin
functions)
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.foo(); c1.foo(); } } contract Contract0 { uint256 versionNFTDropCollection; function foo() external { versionNFTDropCollection++; } } contract Contract1 { uint256 versionNFTDropCollection; function foo() external payable { versionNFTDropCollection++; } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 44293 ┆ 252 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 22308 ┆ 22308 ┆ 22308 ┆ 22308 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬───────┬────────┬───────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪═══════╪════════╪═══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 41893 ┆ 240 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 22284 ┆ 22284 ┆ 22284 ┆ 22284 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴───────┴────────┴───────┴─────────╯
x += y
costs more gas than x = x + y
for state variables [16 gas per instance]Context: BlurExchange.sol#L208 BlurExchange.sol#L479
Description:
x += y
costs more gas than x = x + y
for state variables.
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.foo(33); c1.foo(33); } } contract Contract0 { uint256 _totalBorrow = 60; function foo(uint256 _interestEarned) public { _totalBorrow += interestEarned; } } contract Contract1 { uint256 _totalBorrow = 60; function foo(uint256 _interestEarned) public { _totalBorrow = _totalBorrow + interestEarned; } }
Gas Report
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 70805 ┆ 279 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 5302 ┆ 5302 ┆ 5302 ┆ 5302 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 69805 ┆ 274 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 5286 ┆ 5286 ┆ 5286 ┆ 5286 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
Context: BlurExchange.sol#L63
Description: WETH is a wrap Ether contract with a specific address in the Etherum network, giving the option to define it may cause false recognition, it is healthier to define it directly.
Advantages of defining a specific contract directly:
WETH Address : 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Context: ExecutionDelegate.sol#L22
Description:
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Revert strings that are longer than 32 bytes require at least one additional mstore
, along with additional overhead for computing memory offset, etc.
Recommendation: Revert strings > 32 bytes or use Custom Error()
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.foo(); c1.foo(); } } contract Contract0 { uint256 test1; function foo() external { require(test1 != 0, "This is Error"); } } contract Contract1 { uint256 test1; function foo() external { require(test1 != 0, "Your Project Drop List Has Error with ID and other parameters"); } }
Gas Report:
╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 44093 ┆ 251 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 2334 ┆ 2334 ┆ 2334 ┆ 2334 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭───────────────────────────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ src/test/GasTest.t.sol:Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞═══════════════════════════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 51705 ┆ 290 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ foo ┆ 2352 ┆ 2352 ┆ 2352 ┆ 2352 ┆ 1 │ ╰───────────────────────────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
double require
instead of using &&
Context: BlurExchange.sol#L265-L267 BlurExchange.sol#L283
Recommendation: Using double require instead of operator && can save more gas When having a require statement with 2 or more expressions needed, place the expression that cost less gas first.
Context: OrderStructs.sol#L13-L27
Recommendation: Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings.
Context: All Contracts
Description:
Contracts most called functions could simply save gas by function ordering via Method ID
. Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas
are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.
Recommendation:
Find a lower method ID
name for the most called functions for example Call() vs. Call1() is cheaper by 22 gas
For example, the function IDs in the BlurExchange.sol
contract will be the most used; A lower method ID may be given.
Proof of Consept: https://medium.com/joyso/solidity-how-does-function-name-affect-gas-consumption-in-smart-contract-47d270d8ac92
BlurExchange.sol function names can be named and sorted according to METHOD ID
Sighash | Function Signature ======================== fcfff16f => open() 43d726d6 => close() 5ec29272 => _authorizeUpgrade(address) 6ef1f014 => initialize(uint256,address,IExecutionDelegate,IPolicyManager,address,uint256) 1d2318bf => execute(Input,Input) 8bd0bcf1 => cancelOrder(Order) 16539ce3 => cancelOrders(Order[]) 627cdcb9 => incrementNonce() b592666b => setExecutionDelegate(IExecutionDelegate) a43df956 => setPolicyManager(IPolicyManager) 7adbf973 => setOracle(address) 6992aa36 => setBlockRange(uint256) f15bd944 => _validateOrderParameters(Order,bytes32) 2aaa2c5f => _canSettleOrder(uint256,uint256) e709d479 => _validateSignatures(Input,bytes32) d6598f78 => _validateUserAuthorization(bytes32,address,uint8,bytes32,bytes32,SignatureVersion,bytes) 714d219a => _validateOracleAuthorization(bytes32,SignatureVersion,bytes,uint256) 6a5a834b => _recover(bytes32,uint8,bytes32,bytes32) 6187db64 => _canMatchOrders(Order,Order) 57a57287 => _executeFundsTransfer(address,address,address,Fee[],uint256) 0ced6617 => _transferFees(Fee[],address,address,uint256) f49ec961 => _transferTo(address,address,address,uint256) 13ca597d => _executeTokenTransfer(address,address,address,uint256,uint256,AssetType) c3b3a3c9 => _exists(address)
Context: BlurExchange.sol#L3
From Pragma 0.8.0, ABI coder v2 is activated by default.
Recommended Mitigation Steps: ABI coder v2 is activated by default. It is recommended to delete redundant codes. From Solidity v0.8.0 Breaking Changes https://docs.soliditylang.org/en/v0.8.0/080-breaking-changes.html
return
keyword for gas efficient [ 3 gas saved]Context: BlurExchange.sol#L365 BlurExchange.sol#L380
Recommendation:
You must remove the return
keyword from the specified contexts.
Proof Of Concept: The optimizer was turned on and set to 10000 runs.
Context: BlurExchange.sol#L190 BlurExchange.sol#L209 BlurExchange.sol#L221 BlurExchange.sol#L230
Context: ReentrancyGuarded.sol
contract ReentrancyGuarded { bool reentrancyLock = false; /* Prevent a contract function from being reentrant-called. */ modifier reentrancyGuard { require(!reentrancyLock, "Reentrancy detected"); reentrancyLock = true; _; reentrancyLock = false; } }
Recommendation Code:
abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() virtual { require(locked == 1, " Reentrancy detected"); locked = 2; _; locked = 1; } }
Context: BlurExchange.sol#L8
You don't need to import the implementation to interact with the contract, you can import only an interface, e.g. here: Consider replacing ERC20 with IERC20 to reduce deployment costs.
zero-address
check in initialize
Context: BlurExchange.sol#L95
Description: Missing checks for zero-addresses may lead to infunctional protocol, if the variable addresses are updated incorrectly. It also wast gas as it requires the redeployment of the contract.
v4.8.0 OpenZeppelin
contractsDescription: The upcoming v4.8.0 version of OpenZeppelin provides many small gas optimizations.
https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v4.8.0-rc.0
v4.8.0-rc.0 ⛽ Many small optimizations
Description:
Contract storage in the EVM is built around the concept of "slots," where each slot is 32 bytes wide and can be indexed by any 256-bit number. In the simple case, the compiler will assign storage variables to successive slots as you declare them in your contracts. As a single operation, reading and writing to a storage slot is probably the most expensive thing your contract does regularly, with a single read costing up to 2,100 gas and single write costing up to 20,000 gas (on mainnet). Non-trivial contracts will usually need to read from and write to many storage variables in a single transaction, meaning the costs can quickly add up.
The following struct can also be rearranged;
OrderStrcucts.sol : struct Order { address trader; //160 bit Side side; //enum address matchingPolicy; //160 bit address collection; //160 bit uint256 tokenId; //256 bit uint256 amount; //256 bit address paymentToken; //160 bit uint256 price; //256 bit uint256 listingTime; //256 bit uint256 expirationTime; //256 bit Fee[] fees; // Dynamic array uint256 salt; //256 bit bytes extraParams; //256 bit }
Description: https://github.com/Vectorized/solady/blob/main/src/auth/OwnableRoles.sol
#0 - GalloDaSballo
2022-10-26T21:28:04Z
5k lock 300 gas rest
5300