Platform: Code4rena
Start Date: 26/09/2022
Pot Size: $50,000 USDC
Total HM: 13
Participants: 113
Period: 5 days
Judge: 0xean
Total Solo HM: 6
Id: 166
League: ETH
Rank: 15/113
Findings: 4
Award: $466.16
๐ Selected for report: 2
๐ Solo Findings: 0
๐ Selected for report: 0xSmartContract
Also found by: 0xDecorativePineapple, Jeiwan, berndartmueller, brgltd, kaden, rbserver
247.1407 USDC - $247.14
The safeTransfer
function does not check for the existence of the ERC20 token contract , TransferHelper.sol
performs a transfer with a low-level call without confirming the contract's existence
function safeTransfer( address token, address to, uint256 value ) internal { (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); }
The low-level functionsย call,ย delegatecallย andย staticcallย returnย trueย as their first return value if the account called is non-existent, as part of the design of the EVM. Account existence must be checked prior to calling if needed.
It allows malicious people to pair with a qualified token like ETH with shit tokens that they can destroy later, and most importantly, to run the safeTransfer function even if the token contract is later destroyed.
1 Alice creates a pair of A and B Tokens (For exampleETH - TestToken Pair) The creator and only owner of TestToken is Alice.
2 Next, Alice destroys the TestToken with a Selfdestruct based on onlyowner privilege.
3 Bob, unaware of this, deposits ETH into the pool to trade from the ETH-TestToken pair, but cannot receive any tokens because safeTransfer
does not check for the existence of the contract
Manual code review
Have the SafeTransfer function check the existence of the contract before every transaction
#0 - 0xean
2022-10-02T21:38:30Z
This is technically correct but requires some pretty explicit external requirements that cannot be easily fixed by a permission-less protocol.
Mainly that if someone creates a pool with an ERC20 that can be self destructed, that pool's paired asset is going to be locked in some form as well.
I am going to downgrade to M due to the external factors required for this to occur. The recommended fix could ensure more assets don't become locked as a result of the self destruct.
#1 - sameepsi
2022-10-04T06:17:50Z
Tokens that are destructible are non-standard tokens. We cannot add support all types of non-standard tokens in the protocol. This protocol is intent to work fine with standard tokens.
#2 - 0xean
2022-10-04T13:26:05Z
Going to leave as M, I understand the sponsors point here, but also believe there is a fix that would at least reduce some of the risk here. I do agree it is certainly an edge case.
๐ Selected for report: 0xSmartContract
Also found by: 0xDecorativePineapple, 0xNazgul, 0xmatt, Jeiwan, Trust, berndartmueller, brgltd, catchup, ch13fd357r0y3r, cryptonue, ladboy233, minhtrng, neko_nyaa, rbserver, rvierdiiev, s3cunda
35.4829 USDC - $35.48
The initialize function in AlgebraPool.sol#L193-L206
is a very important function and sets the liquidity price at the beginning of the pool.
Performs some checks (For example, if the price is not 0)
However it is unprotected against running from the front, and a bot listening to Mempool will run from the front and cause its pool to start at too high or too low a price.
It is very important that this function is not enabled for FrontRunning operation.
function initialize(uint160 initialPrice) external override { require(globalState.price == 0, 'AI'); // getTickAtSqrtRatio checks validity of initialPrice inside int24 tick = TickMath.getTickAtSqrtRatio(initialPrice); uint32 timestamp = _blockTimestamp(); IDataStorageOperator(dataStorageOperator).initialize(timestamp, tick); globalState.price = initialPrice; globalState.unlocked = true; globalState.tick = tick; emit Initialize(initialPrice, tick); }
1- Alice starts a new pool in Algebra and triggers the price determination transaction with initialize
2 - Bob listens to the mempool with the following code, which is a minimal example, and sees at what price the initialize
function is triggered with the initialPrice
argument, and starts the pool at the price he wants by pre-executing it and makes Alice deposit the wrong amount at the wrong price.
mempool.js var customWsProvider = new ethers.providers.WebSocketProvider(url); customWsProvider.on("pending", (tx) => { let pendingTx = await connect.provider.getTransaction(txHash); if (pendingTx) { // filter pendingTx.data if (pendingTx.data.indexOf("0xf637731d") !== -1) { // func. signature : f637731d => initialize(uint160) console.log(pendingTx); } }
Manual code review
Add a modifier
that ensures that only the authorized person, that is, the first liquidator to the pool, initiates the initialize
function
Or, divide the process of determining the price of the pool into parts, first print the price amounts to the state variable, and then make the initialize
run before this price can be changed.
#0 - 0xean
2022-10-02T21:09:43Z
Downgrading to Medium. Alice shouldn't be relying on this initial price to determine the "fair" market price. When there is very limited liquidity the price is extremely easy to move along the x*y=k curve in any case so even after the initialized call is made someone could manipulate the pools pricing very easily when liquidity is low.
#1 - sameepsi
2022-10-04T06:15:52Z
I don't think it's a bug. Even if someone sets the wrong price initially then it will be arbitraged. That's how AMMs work by design.
#2 - 0xean
2022-10-04T13:28:52Z
Going to leave as M - even if for nothing else as to warn users in the future to not trust on-chain pricing for new pools or pools with low liquidity.
๐ Selected for report: 0xNazgul
Also found by: 0x1f8b, 0x52, 0xDecorativePineapple, 0xSmartContract, 0xmatt, Aeros, Aymen0909, Bnke0x0, Chom, CodingNameKiki, Deivitto, DimitarDimitrov, IllIllI, JC, Jeiwan, Lambda, Matin, Migue, Mukund, Ocean_Sky, Olivierdem, RaymondFam, RockingMiles, Rolezn, Ruhum, Satyam_Sharma, Shinchan, Tomo, Trabajo_de_mates, V_B, Waze, __141345__, a12jmx, ajtra, asutorufos, aysha, brgltd, bulej93, carrotsmuggler, catchup, cccz, chrisdior4, cryptonue, cryptphi, d3e4, defsec, delfin454000, durianSausage, erictee, fatherOfBlocks, gogo, kaden, karanctf, ladboy233, lukris02, mahdikarimi, martin, mics, natzuu, oyc_109, p_crypt0, pedr02b2, rbserver, reassor, rotcivegaf, rvierdiiev, sikorico, slowmoses, sorrynotsorry, tnevler, trustindistrust
126.7868 USDC - $126.79
Number | Issues Details | Context |
---|---|---|
[N-01] | Update to owner emit | All contracts |
[N-02] | Use require instead of assert | 1 |
[N-03] | Include return parameters in NatSpec comments | All contracts |
[N-04] | 0 address check | 3 |
[N-05] | Long lines are not suitable for the Solidity Style Guide | 10 |
[N-06] | For modern and more readable code; Update import usages | All contracts |
[N-07] | Update to owner emit | 1 |
[N-08] | 0 address require in setFactory function causes factory address to be updated only 1 time | 1 |
[N-09] | The use of new{salt} came in Solidity 8.0.0, but the contract uses 0.7.6 | 1 |
[N-10] | Use private instead of using internal | 1 |
[N-11] | Redundant 0 Address check | 1 |
Total 11 issues
Number | Issues Details |
---|---|
[L-01] | Add to blacklist function |
Total 1 issue
Context: All Contracts
Description:
Old version of Solidity is used (0.7.6
), newer version should be used.
require
instead of assert
Context: DataStorage.sol#L190
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: 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: 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: AlgebraFactory.sol#L54 AlgebraFactory.sol#L55 PoolImmutables.sol#L30
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();
Solidity Style Guide
Context: AlgebraPool.sol#L221 AlgebraPool.sol#L297 AlgebraPool.sol#L355 AlgebraPool.sol#L392 AlgebraPool.sol#L472 AlgebraPool.sol#L601 AlgebraPool.sol#L654 AlgebraPool.sol#L876 DataStorage.sol#L255 DataStorage.sol#L133
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
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
thisFunctionCallIsReallyLong( longArgument1, longArgument2, longArgument3 );
Context: All Contracts
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";
owner
emitContext: AlgebraFactory.sol#L52
Recommendation: In this way, it is more readable and comprehensible for web pages and other users that read emit.
Current Code :
constructor(address _poolDeployer, address _vaultAddress) { owner = msg.sender; emit Owner(msg.sender); poolDeployer = _poolDeployer; vaultAddress = _vaultAddress; }
Recommendation Code :
constructor(address _poolDeployer, address _vaultAddress) { owner = msg.sender; emit OwnerUpdated(address(0), owner); poolDeployer = _poolDeployer; vaultAddress = _vaultAddress; }
0 address
require in setFactory
function causes factory address to be updated only 1 timeContext: AlgebraPoolDeployer.sol#L38
Recommendation:
setFactory
require(factory == address(0)); query allows the function to be executed only once and restricts the set property, remove this from the code.
If it needs to be run once, add initalize or assign value from constrcutor.
function setFactory(address _factory) external override onlyOwner { require(_factory != address(0)); require(factory == address(0)); emit Factory(_factory); factory = _factory; }
Context: AlgebraPoolDeployer.sol#L51
Description: Contracts can be created by other contracts using theย newย keyword. Since 0.8.0,ย newย keyword supportsย create2ย feature by specifyingย saltย options. Using the old pragma version of the contract may cause unexpected errors
pool = address(new AlgebraPool{salt: keccak256(abi.encode(token0, token1))}());
Recommendation: Use new solidity version.
private
instead of using internal
Context: AlgebraFactory.sol#L116
Recommendation:
POOL_INIT_CODE_HASH variable use only own contract,so Use private
instead of using internal
.
this is best practice for security
Current Code:
bytes32 internal constant POOL_INIT_CODE_HASH = 0x6ec6c9c8091d160c0aa74b2b14ba9c1717e95093bd3ac085cee99a49aab294a4;
Recommendation Code:
bytes32 private constant POOL_INIT_CODE_HASH = 0x6ec6c9c8091d160c0aa74b2b14ba9c1717e95093bd3ac085cee99a49aab294a4;
0 Address
checkContext: AlgebraFactory.sol#L62
Recommendation: This check looks redundant. It is anyway possible to pass an address that doesnโt actually refers to a token smart contract.
Current Code:
require(token0 != address(0));
This code is executed even if the owner is not changed.
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;
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.
๐ Selected for report: IllIllI
Also found by: 0x1f8b, 0x5rings, 0xNazgul, 0xRoxas, 0xSmartContract, 0xbepresent, 0xmatt, Aeros, Amithuddar, Awesome, Aymen0909, B2, Bnke0x0, ChristianKuri, CodingNameKiki, Deivitto, Diraco, Fitraldys, HardlyCodeMan, JC, Mukund, Noah3o6, Olivierdem, RaymondFam, ReyAdmirado, RockingMiles, Rolezn, Ruhum, Saintcode_, Shinchan, SnowMan, TomJ, Tomio, Tomo, V_B, Waze, __141345__, ajtra, asutorufos, aysha, beardofginger, bobirichman, brgltd, bulej93, c3phas, ch0bu, cryptonue, defsec, delfin454000, dharma09, durianSausage, emrekocak, erictee, fatherOfBlocks, francoHacker, gianganhnguyen, gogo, imare, kaden, karanctf, ladboy233, lukris02, m_Rassska, martin, medikko, mics, natzuu, oyc_109, peiw, rbserver, ret2basic, rotcivegaf, saian, shark, slowmoses, tnevler, trustindistrust, zeesaw, zishansami
56.7486 USDC - $56.75
Number | Optimization Details | Per gas saved | Context |
---|---|---|---|
[G-01] | Use assembly to write address storage values | 33 | 3 |
[G-02] | Function ordering via Method ID | 22 | All contracts |
[G-03] | x += y costs more gas than x = x + y for state variables | 16 | 4 |
[G-04] | Use assembly to check for address(0) | 6 | 2 |
[G-05] | Deploy the contract with clone instead of new | 1 | |
[G-06] | Setting the constructor to payable | 13 | 4 |
[G-07] | Free gas savings for using solidity 0.8.10+ | All contracts | |
[G-08] | Use Custom Errors rather than revert() / require() strings to save deployment gasย | 68 | 4 |
[G-09] | Functions guaranteed to revert when callled by normal users can be marked payable ย | 24 | 5 |
[G-10] | Change to setOwner function | 5.6k | 1 |
[G-11] | Remove to require(factory == address(0)); | 1 | |
[G-12] | Use double require instead of using && | 1 |
Total 12 issues
assembly
to write address storage values [33 gas per instance]Context: BuyCrowdfundBase.sol#L127 AlgebraFactory.sol#L87 AlgebraFactory.sol#L94
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 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโฏ
Method ID
[22 gas per instance]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 AlgebraPool.sol
contract will be the most used; A lower method ID may be given.
Proof of Consept: https://coinsbench.com/advanced-gas-optimizations-tips-for-solidity-85c47f413dc5
AlgebraPool.sol function names can be named and sorted according to METHOD ID
Sighash | Function Signature ======================== 71334694 => swapSupportingFeeOnInputTokens(address,address,bool,int256,uint160,bytes) 54429aa9 => balanceToken0() f81fb785 => balanceToken1() 74eceae6 => timepoints(uint256) 920c34e5 => getInnerCumulatives(int24,int24) 9d3a5241 => getTimepoints(uint32[]) f637731d => initialize(uint160) 378934af => _recalculatePosition(Position,int128,uint256,uint256) 37c50c53 => _updatePositionTicksAndFees(address,int24,int24,int128) 341a44dd => _getAmountsForLiquidity(int24,int24,int128,int24,uint160) 8525eb6e => getOrCreatePosition(address,int24,int24) aafe29c0 => mint(address,address,int24,int24,uint128,bytes) 4f1eb3d8 => collect(address,int24,int24,uint128,uint128) a34123a7 => burn(int24,int24,uint128) 2609fee3 => _getNewFee(uint32,int24,uint16,uint128) 10ff8907 => _payCommunityFee(address,uint256) 3dce4c59 => _writeTimepoint(uint16,uint32,int24,uint128,uint128) 4f1e3721 => _getSingleTimepoint(uint32,uint32,int24,uint16,uint128) 38e6ef28 => _swapCallback(int256,int256,bytes) 128acb08 => swap(address,bool,int256,uint160,bytes) 6607d5c2 => _calculateSwapAndLock(bool,int256,uint160) 545da887 => require(unlocked,'LOK') 490e6cbc => flash(address,uint256,uint256,bytes) 7c0112b7 => setCommunityFee(uint8,uint8) 7c1fe0c8 => setIncentive(address) 289fe9b0 => setLiquidityCooldown(uint32)
x += y
costs more gas than x = x + y
for state variables [16 gas per instance]Context: AlgebraPool.sol#L257 AlgebraPool.sol#L258 AlgebraPool.sol#L811 AlgebraPool.sol#L931
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 ย ย ย โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ
Use assembly
to check for address(0)
[6 gas per instance]Context:ย AlgebraFactory.sol#L62 AlgebraPoolDeployer.sol#L38
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 { error Callback_InvalidParams(); function setOperator(address operator_) public pure { if (address(operator_) == address(0)) revert Callback_InvalidParams(); } } 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 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโดโโโโโโโโโดโโโโโโดโโโโโโโโโโฏ
clone
instead of new
Context: AlgebraFactory.sol#L65
Proof Of Concept: Thereโs a way to save a significant amount of gas on deployment using Clones:ย https://www.youtube.com/watch?v=3Mw-pMmJ7TAย . It is extremely inexpensive to distribute by Cloneing with Create2.
Gas usage difference between the two? (answer: a new clone is 10x cheaper)
constructor
to payable
[13 gas per instance]Context: AlgebraFactory.sol#L50 AlgebraPool.sol#L66 AlgebraPoolDeployer.sol#L31 PoolImmutables.sol#L29
Description:
You can cut out 10 opcodes in the creation-time EVM bytecode if you declare a constructor payable. Making the constructor payable eliminates the need for an initial check ofย msg.value == 0
ย and saves 13 gas
on deployment with no security risks.
Recommendation:
Set the constructor to payable
Proof Of Concept: https://forum.openzeppelin.com/t/a-collection-of-gas-optimisation-tricks/19966/5?u=pcaversaccio
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.x(); c2.x(); } } contract Contract1 { uint256 public dummy; constructor() payable { dummy = 1; } function x() public { } } contract Contract2 { uint256 public dummy; constructor() { dummy = 1; } function x() public { } }
Gas Report:
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโฌโโโโโโโโโฌโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract1 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโชโโโโโโโโโชโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ 49563 โ 159 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโฌโโโโโโโโโฌโโโโโโฌโโโโโโโโโโฎ โ src/test/GasTest.t.sol:Contract2 contract โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโชโโโโโโโโโชโโโโโโชโโโโโโโโโโก โ Deployment Cost โ Deployment Size โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค โ 49587 โ 172 โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโผโโโโโโผโโโโโโโโโโค
Context: All Contracts
Description: Solidity 0.8.10 has a useful change which reduced gas costs of external calls which expect a return value:ย https://blog.soliditylang.org/2021/11/09/solidity-0.8.10-release-announcement/
Code Generator: Skip existence check for external contract if return data is expected. In this case, the ABI decoder will revert if the contract does not exist.
Recommendation: Consider to upgrade pragma to at least 0.8.10
Custom Errors
rather than revert() / require() strings to save deployment gasย [68 gas per instance]Context:ย AlgebraPool.sol โ 58 instances AlgebraFactory.sol โ 9 instances AlgebraPoolDeployer.sol โ 4 instances DataStorageOperator.sol โ 4 instances
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 ย ย ย โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโดโโโโโโโดโโโโโโโโโโฏ
payable
[24 gas per instance]Context:ย AlgebraFactory.sol#L77 AlgebraFactory.sol#L84 AlgebraFactory.sol#L91 AlgebraFactory.sol#L108 AlgebraPoolDeployer.sol#L36
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 ย ย ย โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโฏ
setOwner
function [Save 5606 gas with a simpler design]Context:ย AlgebraFactory.sol#L77-L81
Recommendation: Current Code : (Deployment Gas Cost : 114876)
modifier onlyOwner() { require(msg.sender == owner); _; } address public owner = msg.sender; event Owner (address oldown); function setOwner(address _owner) external onlyOwner { require(owner != _owner); emit Owner(_owner); owner = _owner; }
Recommendation Code : : (Deployment Gas Cost : 109270)
address public owner= msg.sender; event OwnerChanged (address oldown, address newown); function setOwner(address _owner) external { require(msg.sender == owner); emit OwnerChanged(owner, _owner); owner = _owner; }
require(factory == address(0));
Context:ย AlgebraPoolDeployer.sol#L38
Recommendation:
setFactory
require(factory == address(0)); query allows the function to be executed only once and restricts the set property, remove this from the code for gas saving.
If it needs to be run once, add initalize or assign value from Constrcutor
function setFactory(address _factory) external override onlyOwner { require(_factory != address(0)); require(factory == address(0)); //remove this emit Factory(_factory); factory = _factory; }
double require
instead of using &&
Context:ย AlgebraFactory.sol#L110
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.
Current Code:
require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0โ);
Recommendation Code:
require(gamma1 != 0 && gamma2 != 0, 'Gammas must be > 0โ); require(volumeGamma != 0, 'Gammas must be > 0โ);