Platform: Code4rena
Start Date: 18/10/2022
Pot Size: $75,000 USDC
Total HM: 27
Participants: 144
Period: 7 days
Judge: gzeon
Total Solo HM: 13
Id: 170
League: ETH
Rank: 17/144
Findings: 2
Award: $771.29
๐ Selected for report: 0
๐ Solo Findings: 0
๐ Selected for report: Rolezn
Also found by: 0x1f8b, 0x52, 0x5rings, 0xNazgul, 0xSmartContract, 0xZaharina, 0xhunter, 0xzh, 8olidity, Amithuddar, Aymen0909, B2, Bnke0x0, Chom, Deivitto, Diana, Diraco, Dravee, Franfran, JC, Jeiwan, Josiah, JrNet, Jujic, KingNFT, KoKo, Lambda, Margaret, Migue, Ocean_Sky, PaludoX0, Picodes, Rahoz, RaoulSchaffranek, RaymondFam, RedOneN, ReyAdmirado, Shinchan, Tagir2003, Trust, Waze, Yiko, __141345__, a12jmx, adriro, ajtra, arcoun, aysha, ballx, bin2chen, bobirichman, brgltd, bulej93, catchup, catwhiskeys, caventa, cccz, cdahlheimer, ch0bu, chaduke, chrisdior4, cloudjunky, cryptostellar5, cryptphi, csanuragjain, cylzxje, d3e4, delfin454000, djxploit, durianSausage, erictee, fatherOfBlocks, francoHacker, gianganhnguyen, gogo, hansfriese, i_got_hacked, ignacio, imare, karanctf, kv, leosathya, louhk, lukris02, lyncurion, m_Rassska, malinariy, martin, mcwildy, mics, minhtrng, nicobevi, oyc_109, pashov, peanuts, pedr02b2, peiw, rbserver, ret2basic, rotcivegaf, rvierdiiev, ryshaw, sakman, sakshamguruji, saneryee, securerodd, seyni, sikorico, svskaushik, teawaterwire, tnevler, w0Lfrum
549.0408 USDC - $549.04
Number | Issues Details | Context |
---|---|---|
[NC-01 ] | Include return parameters in NatSpec comments | All contracts |
[NC-02] | 0 address check | 12 |
[NC-03] | For modern and more readable code; update import usages | All contracts |
[NC-04] | Empty blocks should be removed or Emit something | 5 |
[NC-05] | Function writing that does not comply with the Solidity Style Guide | All contracts |
[NC-06] | Omissions in Events | 4 |
[NC-07] | Need Fuzzing test | 9 |
[NC-08] | Use a more recent version of Solidity | All contracts |
[NC-09] | NatSpec comments should be increased in contracts | All contracts |
Total 9 issues
Number | Issues Details | Context |
---|---|---|
[L-01] | Missing Event for Critical Parameters Change | 20 |
[L-02] | Use block.number instead block.timestamp | 1 |
[L-03] | Use safeTransferOwnership instead of transferOwnership function | 3 |
[L-04] | Use safeSetAdmin instead of setAdmin function | 8 |
[L-05] | Signature Malleability of EVM'sย ecrecover() | 1 |
Total 5 issues
Number | Suggestion Details |
---|---|
[S-01] | Add to blacklist function |
[S-02] | Generate perfect code headers every time |
Total 2 suggestions
Context: 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.17/natspec-format.html
Recommendation: Include return parameters in NatSpec comments
Recommendation Code Style: (from Uniswap3)
/// @notice Adds liquidity for the given recipient/tickLower/tickUpper position /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends /// on tickLower, tickUpper, the amount of liquidity, and the current price. /// @param recipient The address for which the liquidity will be created /// @param tickLower The lower tick of the position in which to add liquidity /// @param tickUpper The upper tick of the position in which to add liquidity /// @param amount The amount of liquidity to mint /// @param data Any data that should be passed through to the callback /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback function mint( address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data ) external returns (uint256 amount0, uint256 amount1);
0 address
checkContext: Holograph.sol#L69-L75 HolographBridge.sol#L65 HolographFactory.sol#L46 HolographOperator.sol#L143 Holographer.sol#L51 LayerZeroModule.sol#L61 Holograph.sol#L148 Holograph.sol#L189 Holograph.sol#L209 Holograph.sol#L229 Holograph.sol#L249 Holograph.sol#L269
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 (routerV2== address(0)) revert ADDRESS_ZERO();
Context: All scope contracts
Description:
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";
A good example from the ArtGobblers project;
import {Owned} from "solmate/auth/Owned.sol"; import {ERC721} from "solmate/tokens/ERC721.sol"; import {LibString} from "solmate/utils/LibString.sol"; import {MerkleProofLib} from "solmate/utils/MerkleProofLib.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {ERC1155, ERC1155TokenReceiver} from "solmate/tokens/ERC1155.sol"; import {toWadUnsafe, toDaysWadUnsafe} from "solmate/utils/SignedWadMath.sol";
Empty blocks
should be removed or Emit somethingContext: ERC20H.sol#L113 ERC721H.sol#L113 Holographer.sol#L124 HolographERC20.sol#L152 HolographERC721.sol#L863
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.
If the code is not upgradeable and this function is not used, remove it directly
Function writing
that does not comply with the Solidity Style Guide
Context: All scope contracts
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: All scope contracts
Recommendation: Variables are declared asย constantย utilize theย UPPER_CASE_WITH_UNDERSCORESย format.
Context: Project uncheckeds list:
9 results โ 1 file contracts/enforcer/HolographERC20.sol: 349 require(currentAllowance >= amount, "ERC20: amount exceeds allowance"); 350: unchecked { 351 _allowances[account][msg.sender] = currentAllowance - amount; 366 uint256 newAllowance; 367: unchecked { 368 newAllowance = currentAllowance - subtractedValue; 400 require(currentAllowance >= amount, "ERC20: amount exceeds allowance"); 401: unchecked { 402 _allowances[from][sender] = currentAllowance - amount; 422 uint256 newAllowance; 423: unchecked { 424 newAllowance = currentAllowance + addedValue; 425 } 426: unchecked { 427 require(newAllowance >= currentAllowance, "ERC20: increased above max value"); 529 require(currentAllowance >= amount, "ERC20: amount exceeds allowance"); 530: unchecked { 531 _allowances[account][msg.sender] = currentAllowance - amount; 599 require(currentAllowance >= amount, "ERC20: amount exceeds allowance"); 600: unchecked { 601 _allowances[account][msg.sender] = currentAllowance - amount; 629 require(accountBalance >= amount, "ERC20: amount exceeds balance"); 630: unchecked { 631 _balances[account] = accountBalance - amount; 698 require(accountBalance >= amount, "ERC20: amount exceeds balance"); 699: unchecked { 700 _balances[account] = accountBalance - amount;
Description: In total 1 contract, 9 unchecked are used, the functions used are critical. For this reason, there must be fuzzing tests in the tests of the project. Not seen in tests.
Recommendation: Use should fuzzing test like Echidna.
As Alberto Cuesta Canada said: Fuzzing is not easy, the tools are rough, and the math is hard, but it is worth it. Fuzzing gives me a level of confidence in my smart contracts that I didnโt have before. Relying just on unit testing anymore and poking around in a testnet seems reckless now.
https://medium.com/coinmonks/smart-contract-fuzzing-d9b88e0b0a05
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: 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
Recommendation: NatSpec comments should be increased in contracts
Context: PA1D.sol#L471, Holograph.sol#L107, Holograph.sol#L128, Holograph.sol#L148, Holograph.sol#L169, Holograph.sol#L189, Holograph.sol#L209, Holograph.sol#L229, Holograph.sol#L249, Holograph.sol#L269, HolographBridge.sol#L353, HolographBridge.sol#L373, HolographBridge.sol#L403, HolographBridge.sol#L423, HolographFactory.sol#L280, HolographFactory.sol#L300, LayerZeroModule.sol#L320 LayerZeroModule.sol#L340, LayerZeroModule.sol#L360, LayerZeroModule.sol#L380
Description: Events help non-contract tools to track changes, and events prevent users from being surprised by changes.
Recommendation:
Add event-emit
block.number
instead block.timestampContext: HolographERC20.sol#L469
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.
safeTransferOwnership
instead of transferOwnership
functionContext: HolographERC20.sol#L9 HolographERC721.sol#L7 PA1D.sol#L7
contracts/abstract/Owner.sol: 139 140: function transferOwnership(address newOwner) public onlyOwner { 141 require(newOwner != address(0), "HOLOGRAPH: zero address"); 142 assembly { 143 sstore(_ownerSlot, ownerAddress) 144 } 145 emit OwnershipTransferred(previousOwner, ownerAddress); 146 }
Description:
transferOwnership
function is used to change Ownership
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
/** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() external { address sender = _msgSender(); require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner"); _transferOwnership(sender); } }
safeSetAdmin
instead of setAdmin
functionContext:
8 results - 8 files contracts/Holograph.sol: 103 104: import "./abstract/Admin.sol"; contracts/HolographBridge.sol: 103 104: import "./abstract/Admin.sol"; contracts/HolographFactory.sol: 103 104: import "./abstract/Admin.sol"; contracts/enforcer/Holographer.sol: 103 104: import "../abstract/Admin.sol"; contracts/enforcer/HolographERC20.sol: 103 104: import "../abstract/Admin.sol"; contracts/enforcer/HolographERC721.sol: 103 104: import "../abstract/Admin.sol"; contracts/enforcer/PA1D.sol: 103 104: import "../abstract/Admin.sol"; contracts/module/LayerZeroModule.sol: 103 104: import "../abstract/Admin.sol";
Description:
setAdmin
function is used to change admin
Use a 2 structure transferOwnership which is safer.
setAdmin
, use it is more secure due to 2-stage ownership transfer.
Recommendation: Use 2-stage ownership transfer abstract contract:
abstract contract Ownable2Step is Ownable { address private _pendingAdmin; event AdminTransferStarted(address indexed previousAdmin, address indexed newAdmin); /** * @dev Returns the address of the pending Admin. */ function pendingAdmin() public view virtual returns (address) { return _pendingAdmin; } /** * @dev Starts the Admin transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current Admin. */ function transferAdmin(address newAdmin) public virtual override onlyAdmin { _pendingAdmin = newAdmin; emit AdminTransferStarted(Admin(), newAdmin); } /** * @dev Transfers Admin of the contract to a new account (`newAdmin`) and deletes any pending Admin. * Internal function without access restriction. */ function _transferAdmin(address newAdmin) internal virtual override { delete _pendingAdmin; super._transferAdmin(newAdmin); } /** * @dev The new Admin accepts the Admin transfer. */ function acceptAdmin() external { address sender = _msgSender(); require(pendingAdmin() == sender, "Ownable2Step: caller is not the new Admin"); _transferAdmin(sender); } }
Context: HolographFactory.sol#L333
Description: Description: The function calls the Solidity ecrecover() function directly to verify the given signatures. However, the ecrecover() EVM opcode allows malleable (non-unique) signatures and thus is susceptible to replay attacks.
Although a replay attack seems not possible for this contract, I recommend using the battle-tested OpenZeppelin's ECDSA library.
Recommendation:* Use the ecrecover function from OpenZeppelin's ECDSA library for signature verification. (Ensure using a version > 4.7.3 for there was a critical bug >= 4.1.0 < 4.7.3).
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
Some of these Projects; USDC Aave Uniswap Balancer Infura Alchemy Opensea dYdX
For this reason, every project in the Avalanche 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: I recommend using header for Solidity code layout and readability
https://github.com/transmissions11/headers
/*////////////////////////////////////////////////////////////// TESTING 123 //////////////////////////////////////////////////////////////*/
๐ Selected for report: oyc_109
Also found by: 0x040, 0x1f8b, 0x5rings, 0xNazgul, 0xSmartContract, 0xZaharina, 0xsam, 0xzh, 2997ms, Amithuddar, Aymen0909, B2, Bnke0x0, Deivitto, Diana, Dinesh11G, Franfran, JC, JrNet, Jujic, KingNFT, KoKo, Mathieu, Metatron, Mukund, Olivierdem, PaludoX0, Pheonix, Picodes, RaymondFam, RedOneN, ReyAdmirado, Rolezn, Saintcode_, Satyam_Sharma, Shinchan, Tagir2003, Tomio, Waze, Yiko, __141345__, adriro, ajtra, aysha, ballx, beardofginger, bobirichman, brgltd, bulej93, catchup, catwhiskeys, cdahlheimer, ch0bu, chaduke, chrisdior4, cryptostellar5, cylzxje, d3e4, delfin454000, dharma09, djxploit, durianSausage, emrekocak, erictee, exolorkistis, fatherOfBlocks, gianganhnguyen, gogo, halden, hxzy, i_got_hacked, iepathos, karanctf, leosathya, lucacez, lukris02, lyncurion, m_Rassska, martin, mcwildy, mics, nicobevi, peanuts, peiw, rbserver, ret2basic, rotcivegaf, ryshaw, sakman, sakshamguruji, saneryee, sikorico, skyle, svskaushik, tnevler, vv7, w0Lfrum, zishansami
222.2506 USDC - $222.25
Number | Optimization Details | Context |
---|---|---|
[G-01] | Using storage instead of memory for struct saves gas | 1 |
[G-02] | Functions guaranteed to revert when callled by normal users can be marked payable | 23 |
[G-03] | x += y (x -= y) costs more gas than x = x + y (x = x โ y) for state variables | 5 |
[G-04] | Optimize names to save gas | All contracts |
[G-05] | Use assembly to check for address(0) | 9 |
[G-06] | The solady Library's Ownable contract is significantly gas-optimized, which can be used | 21 |
[G-07] | It is more efficient to use abi.encodePacked instead of abi.encode ย | 5 |
[G-08] | Sort Solidity operations using short-circuit mode | 15 |
[G-09] | Use a different pattern when using for loops | 13 |
[G-10] | Use inline assembly for contract balance | 3 |
Total 10 issues
Context: HolographFactory.sol#L193-L194
Description: When fetching data from a storage location, assigning the data to a memory variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD rather than a cheap stack read.
Recommendation:
Current Code:
HolographFactory.sol#L193-L194 DeploymentConfig memory config, Verification memory signature,
Recommendation Code:
HolographFactory.sol#L193-L194 DeploymentConfig storage config, Verification storage signature,
payable
Context:ย
23 results 5 files contracts/enforcer/PA1D.sol: 470 */ 471: function configurePayouts(address payable[] memory addresses, uint256[] memory bps) public onlyOwner { 472 require(addresses.length == bps.length, "PA1D: missmatched array lenghts"); 532 uint256 bp 533: ) public onlyOwner { 534 if (tokenId == 0) { contracts/Holograph.sol: 205 */ 206: function setBridge(address bridge) external onlyAdmin { 207 assembly { 226 */ 227: function setChainId(uint256 chainId) external onlyAdmin { 228 assembly { 246 */ 247: function setFactory(address factory) external onlyAdmin { 248 assembly { 267 */ 268: function setHolographChainId(uint32 holographChainId) external onlyAdmin { 269 assembly { 287 */ 288: function setInterfaces(address interfaces) external onlyAdmin { 289 assembly { 307 */ 308: function setOperator(address operator) external onlyAdmin { 309 assembly { 327 */ 328: function setRegistry(address registry) external onlyAdmin { 329 assembly { 347 */ 348: function setTreasury(address treasury) external onlyAdmin { 349 assembly { 367 */ 368: function setUtilityToken(address utilityToken) external onlyAdmin { 369 assembly { contracts/HolographBridge.sol: 451 */ 452: function setFactory(address factory) external onlyAdmin { 453 assembly { 471 */ 472: function setHolograph(address holograph) external onlyAdmin { 473 assembly { 501 */ 502: function setOperator(address operator) external onlyAdmin { 503 assembly { 521 */ 522: function setRegistry(address registry) external onlyAdmin { 523 assembly { contracts/HolographFactory.sol: 279 */ 280: function setHolograph(address holograph) external onlyAdmin { 281 assembly { 299 */ 300: function setRegistry(address registry) external onlyAdmin { 301 assembly { contracts/module/LayerZeroModule.sol: 319 */ 320: function setBridge(address bridge) external onlyAdmin { 321 assembly { 339 */ 340: function setInterfaces(address interfaces) external onlyAdmin { 341 assembly { 359 */ 360: function setLZEndpoint(address lZEndpoint) external onlyAdmin { 361 assembly { 379 */ 380: function setOperator(address operator) external onlyAdmin { 381 assembly { 440 */ 441: function setBaseGas(uint256 baseGas) external onlyAdmin { 442 assembly { 469 */ 470: function setGasPerByte(uint256 gasPerByte) external onlyAdmin { 471 assembly {
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 (x -= y)
costs more gas than x = x + y (x = x โ y)
for state variablesContext:
5 results - 3 files contracts/HolographFactory.sol: 327 if (v < 27) { 328: v += 27; 329 } contracts/enforcer/HolographERC20.sol: 684 require(to != address(0), "ERC20: minting to burn address"); 685: _totalSupply += amount; 686: _balances[to] += amount; 687 emit Transfer(address(0), to, amount); 701 } 702: _balances[recipient] += amount; 703 emit Transfer(account, recipient, amount); contracts/enforcer/HolographERC20.sol: 632 } 633: _totalSupply -= amount; 634 emit Transfer(account, address(0), amount);
Description:
x += y (x -= y)
costs more gas than x = x + y (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.swap(2,3); c1.swap1(2,3); } } contract Contract0 { uint256 public amountIn = 10; function swap(uint256 amountInToBin, uint256 fee) external returns (uint256 ) { return amountIn -= amountInToBin + fee; } } contract Contract1 { uint256 public amountIn = 10; function swap1(uint256 amountInToBin, uint256 fee) external returns (uint256 result1) { return (amountIn = amountIn - (amountInToBin + fee)); } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/test.sol:Contract0 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 83017 โ 341 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ swap โ 5454 โ 5454 โ 5454 โ 5454 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/test.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 83017 โ 341 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ swap1 โ 5431 โ 5431 โ 5431 โ 5431 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ
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 HolographBridge.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
HolographBridge.sol function names can be named and sorted according to METHOD ID
======================== Sighash | Function Signature ======================== 4ddf47d4 => init(bytes) 16f1be70 => bridgeInRequest(uint256,uint32,address,address,address,uint256,bool,bytes) e5585666 => bridgeOutRequest(uint32,address,uint256,uint256,bytes) 565ff49e => revertedBridgeOutRequest(address,uint32,address,bytes) 636ee68b => getBridgeOutRequestPayload(uint32,address,uint256,uint256,bytes) ff1370d9 => getMessageFee(uint32,uint256,uint256,bytes) 88cc58e4 => getFactory() 5bb47808 => setFactory(address) 4827ae0c => getHolograph() 25d5cac8 => setHolograph(address) 6200d9fc => getJobNonce() e7f43c68 => getOperator() b3ab15fb => setOperator(address) 5ab1bd53 => getRegistry() a91ee0dc => setRegistry(address) c5cc6b6a => _factory() dbeebe4c => _holograph() 3b18ecb4 => _jobNonce() 70b0a843 => _operator() 79cbc5fa => _registry()
address(0)
Context:
9 results - 1 files contracts/enforcer/PA1D.sol: 549 function royaltyInfo(uint256 tokenId, uint256 value) public view returns (address, uint256) { 550: if (_getReceiver(tokenId) == address(0)) { 551 return (_getDefaultReceiver(), (_getDefaultBp() * value) / 10000); 559 uint256[] memory bps = new uint256[](1); 560: if (_getReceiver(tokenId) == address(0)) { 561 bps[0] = _getDefaultBp(); 570 address payable[] memory receivers = new address payable[](1); 571: if (_getReceiver(tokenId) == address(0)) { 572 receivers[0] = _getDefaultReceiver(); 592 uint256[] memory bps = new uint256[](1); 593: if (_getReceiver(tokenId) == address(0)) { 594 receivers[0] = _getDefaultReceiver(); 606 uint256[] memory bps = new uint256[](1); 607: if (_getReceiver(tokenId) == address(0)) { 608 receivers[0] = _getDefaultReceiver(); 626 address receiver = _getReceiver(tokenId); 627: if (receiver == address(0)) { 628 return _getDefaultReceiver(); 639 ) public view returns (uint256) { 640: if (_getReceiver(tokenId) == address(0)) { 641 return (_getDefaultBp() * amount) / 10000; 656 address receiver = _getReceiver(tokenId); 657: if (receiver == address(0)) { 658 return _getDefaultReceiver(); 668 bidShares.owner.value = 0; 669: if (_getReceiver(tokenId) == address(0)) { 670 bidShares.creator.value = _getDefaultBp();
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 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโดโโโโโโโโโดโโโโโโดโโโโโโโโโโฏ
Description:
The project uses the onlyOwner
authorization model with the Ownable.sol
contract. I recommend using Solady's highly gas optimized contract.
https://github.com/Vectorized/solady/blob/main/src/auth/OwnableRoles.sol
21 results - 4 file contracts/Holograph.sol: 205 */ 206: function setBridge(address bridge) external onlyAdmin { 207 assembly { 226 */ 227: function setChainId(uint256 chainId) external onlyAdmin { 228 assembly { 246 */ 247: function setFactory(address factory) external onlyAdmin { 248 assembly { 267 */ 268: function setHolographChainId(uint32 holographChainId) external onlyAdmin { 269 assembly { 287 */ 288: function setInterfaces(address interfaces) external onlyAdmin { 289 assembly { 307 */ 308: function setOperator(address operator) external onlyAdmin { 309 assembly { 327 */ 328: function setRegistry(address registry) external onlyAdmin { 329 assembly { 347 */ 348: function setTreasury(address treasury) external onlyAdmin { 349 assembly { 367 */ 368: function setUtilityToken(address utilityToken) external onlyAdmin { 369 assembly { contracts/HolographBridge.sol: 451 */ 452: function setFactory(address factory) external onlyAdmin { 453 assembly { 471 */ 472: function setHolograph(address holograph) external onlyAdmin { 473 assembly { 501 */ 502: function setOperator(address operator) external onlyAdmin { 503 assembly { 521 */ 522: function setRegistry(address registry) external onlyAdmin { 523 assembly { contracts/HolographFactory.sol: 279 */ 280: function setHolograph(address holograph) external onlyAdmin { 281 assembly { 299 */ 300: function setRegistry(address registry) external onlyAdmin { 301 assembly { contracts/module/LayerZeroModule.sol: 319 */ 320: function setBridge(address bridge) external onlyAdmin { 321 assembly { 339 */ 340: function setInterfaces(address interfaces) external onlyAdmin { 341 assembly { 359 */ 360: function setLZEndpoint(address lZEndpoint) external onlyAdmin { 361 assembly { 379 */ 380: function setOperator(address operator) external onlyAdmin { 381 assembly { 440 */ 441: function setBaseGas(uint256 baseGas) external onlyAdmin { 442 assembly { 469 */ 470: function setGasPerByte(uint256 gasPerByte) external onlyAdmin { 471 assembly {
abi.encodePacked
instead of abi.encode
Context: HolographFactory.sol#L252, HolographERC20.sol#L409, HolographERC20.sol#L471, HolographERC721.sol#L260, HolographERC721.sol#L426
Description:
It is more efficient to use abi.encodePacked
instead of abi.encode
.
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.
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.collision("AAA","BBB"); c1.hard("AAA","BBB"); } } 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)); } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract0 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 132377 โ 693 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ collision โ 1737 โ 1737 โ 1737 โ 1737 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 119365 โ 628 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ hard โ 1573 โ 1573 โ 1573 โ 1573 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ
Description:
Short-circuiting is a solidity contract development model that uses OR/AND
logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum virtual machine operation.
//f(x) is a low gas cost operation //g(y) is a high gas cost operation //Sort operations with different gas costs as follows f(x) || g(y) f(x) && g(y)
Context:
15 results - 5 files;
contracts/HolographBridge.sol: 203 require( 204: _registry().isHolographedContract(holographableContract) || address(_factory()) == holographableContract, 205 "HOLOGRAPH: not holographed" 255 require( 256: _registry().isHolographedContract(holographableContract) || address(_factory()) == holographableContract, 257 "HOLOGRAPH: not holographed" 352 require( 353: _registry().isHolographedContract(holographableContract) || address(_factory()) == holographableContract, 354 "HOLOGRAPH: not holographed" contracts/HolographFactory.sol: 313 } 314: return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); 315 } contracts/enforcer/Holographer.sol: 165 bytes4 selector = abi.decode(returnData, (bytes4)); 166: require(success && selector == InitializableInterface.init.selector, "initialization failed"); 167 _setInitialized(); contracts/enforcer/HolographERC20.sol: 288 if ( 289: interfaces.supportsInterface(InterfaceType.ERC20, interfaceId) || erc165Contract.supportsInterface(interfaceId) // check global interfaces // check if source supports interface 290 ) { 380 uint256 fee = 0; 381: if (gasPrice < type(uint256).max && gasLimit < type(uint256).max) { 382 (uint256 hlgFee, ) = _operator().getMessageFee(toChain, gasLimit, gasPrice, bridgeOutPayload); 526 if (account != msg.sender) { 527: if (msg.sender != _holograph().getBridge() && msg.sender != _holograph().getOperator()) { 528 uint256 currentAllowance = _allowances[account][msg.sender]; 596 if (account != msg.sender) { 597: if (msg.sender != _holograph().getBridge() && msg.sender != _holograph().getOperator()) { 598 uint256 currentAllowance = _allowances[account][msg.sender]; 720 } 721: return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); 722 } contracts/enforcer/HolographERC721.sol: 262 bytes4 selector = abi.decode(returnData, (bytes4)); 263: require(success && selector == InitializableInterface.init.selector, "ERC721: coud not init PA1D"); 264 } 298 if ( 299: interfaces.supportsInterface(InterfaceType.ERC721, interfaceId) || // check global interfaces 300: interfaces.supportsInterface(InterfaceType.PA1D, interfaceId) || // check if royalties supports interface 301 erc165Contract.supportsInterface(interfaceId) // check if source supports interface 907 address tokenOwner = _tokenOwner[tokenId]; 908: return (spender == tokenOwner || _tokenApprovals[tokenId] == spender || _operatorApprovals[tokenOwner][spender]); 909 } 915 } 916: return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); 917 }
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 { uint8 number = 50; uint256 numberbig = 50000000000000; function foo() public { require(numberbig == 50000000000000 ||number == 50 ); } } contract Contract1 { uint8 number = 50; uint256 numberbig = 50000000000000; function foo() public { require(number == 50 || numberbig == 50000000000000 ); } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract0 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 72911 โ 196 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ foo โ 2262 โ 2262 โ 2262 โ 2262 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 72911 โ 196 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ foo โ 2268 โ 2268 โ 2268 โ 2268 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ
Context:
13 results - 3 files contracts/enforcer/HolographERC20.sol: 563 function sourceMintBatch(address[] calldata wallets, uint256[] calldata amounts) external onlySource { 564: for (uint256 i = 0; i < wallets.length; i++) { 565 _mint(wallets[i], amounts[i]); contracts/enforcer/HolographERC721.sol: 356 tokenIds = new uint256[](length); 357: for (uint256 i = 0; i < length; i++) { 358 tokenIds[i] = _ownedTokens[wallet][index + i]; 715 tokenIds = new uint256[](length); 716: for (uint256 i = 0; i < length; i++) { 717 tokenIds[i] = _allTokens[index + i]; contracts/enforcer/PA1D.sol: 306 address payable value; 307: for (uint256 i = 0; i < length; i++) { 308 slot = keccak256(abi.encodePacked(i, slot)); 322 address payable value; 323: for (uint256 i = 0; i < length; i++) { 324 slot = keccak256(abi.encodePacked(i, slot)); 339 uint256 value; 340: for (uint256 i = 0; i < length; i++) { 341 slot = keccak256(abi.encodePacked(i, slot)); 355 uint256 value; 356: for (uint256 i = 0; i < length; i++) { 357 slot = keccak256(abi.encodePacked(i, slot)); 393 // uint256 sent; 394: for (uint256 i = 0; i < length; i++) { 395 sending = ((bps[i] * balance) / 10000); 413 //uint256 sent; 414: for (uint256 i = 0; i < length; i++) { 415 sending = ((bps[i] * balance) / 10000); 431 uint256 sending; 432: for (uint256 t = 0; t < tokenAddresses.length; t++) { 433 erc20 = ERC20(tokenAddresses[t]); 436 // uint256 sent; 437: for (uint256 i = 0; i < addresses.length; i++) { 438 sending = ((bps[i] * balance) / 10000); 453 address payable sender = payable(msg.sender); 454: for (uint256 i = 0; i < addresses.length; i++) { 455 if (addresses[i] == sender) { 473 uint256 totalBp; 474: for (uint256 i = 0; i < addresses.length; i++) { 475 totalBp = totalBp + bps[i];
Description: In the use of for loops, this structure, which will reduce gas costs, can be preferred. It saves approximately 400 gas in an array with 6 members.
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 { address[] memory Inputs = new address[](6); Inputs[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; Inputs[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; Inputs[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; Inputs[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; Inputs[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; Inputs[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; c0.dummy(Inputs); c1.dummy(Inputs); } } contract Contract0 { function dummy(address[] memory test) public { for (uint256 i = 0; i < test.length; ++i) { } } } contract Contract1 { function dummy(address[] memory test) public { for (uint256 i = 0; i < test.length; i = unchecked_inc(i)) { // do something that doesn't change the value of i } } function unchecked_inc(uint i) internal pure returns (uint) { unchecked { return i + 1; } } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract0 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 113159 โ 597 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ dummy โ 2044 โ 2044 โ 2044 โ 2044 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 92541 โ 494 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ dummy โ 1666 โ 1666 โ 1666 โ 1666 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ
Context:
3 results 1 file contracts/enforcer/HolographERC20.sol: 448 } 449: try ERC20(account).balanceOf(address(this)) returns (uint256 balance) { 450 require(balance >= amount, "ERC20: balance check failed"); contracts/enforcer/PA1D.sol: 409 ERC20 erc20 = ERC20(tokenAddress); 410: uint256 balance = erc20.balanceOf(address(this)); 411 require(balance > 10000, "PA1D: Not enough tokens to transfer"); 433 erc20 = ERC20(tokenAddresses[t]); 434: balance = erc20.balanceOf(address(this)); 435 require(balance > 10000, "PA1D: Not enough tokens to transfer");
Description:
You can useย balance(address)
ย instead ofย address.balance()
ย when getting an contractโs balance of ETH.
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._withdraw(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4); c1._withdraw(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4); } } contract Contract0 { function _withdraw(address addr) external { uint256 amount = address(this).balance; (bool sent, ) = addr.call{ value: amount }(''); } } contract Contract1 { function _withdraw(address addr) external returns (uint256) { uint256 amount ; assembly { amount := selfbalance() } (bool sent, ) = addr.call{ value: amount }(''); } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract0 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโชโโโโโโโโโชโโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ 55305 โ 308 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโผโโโโโโโผโโโโโโโโโโค โ _withdraw โ 2949 โ 2949 โ 2949 โ 2949 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโฌโโโโโโโโโฌโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโชโโโโโโโโโชโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ 59511 โ 329 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ Function Name โ min โ avg โ median โ max โ # calls โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ _withdraw โ 509 โ 509 โ 509 โ 509 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโดโโโโโโโโโดโโโโโโดโโโโโโโโโโฏ