Platform: Code4rena
Start Date: 02/08/2022
Pot Size: $50,000 USDC
Total HM: 12
Participants: 69
Period: 5 days
Judge: gzeon
Total Solo HM: 5
Id: 150
League: ETH
Rank: 16/69
Findings: 2
Award: $778.19
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0xDjango, 0xNazgul, 0xc0ffEE, 8olidity, Bnke0x0, Chom, CodingNameKiki, Deivitto, Dravee, Funen, JC, JohnSmith, NoamYakov, ReyAdmirado, Rohan16, Rolezn, Sm4rty, SooYa, TomFrenchBlockchain, TomJ, Waze, __141345__, ajtra, ak1, aysha, bin2chen, bobirichman, brgltd, bulej93, c3phas, delfin454000, durianSausage, erictee, fatherOfBlocks, gogo, horsefacts, hyh, ladboy233, mics, natzuu, nxrblsrpr, oyc_109, rbserver, samruna, sikorico, simon135, tofunmi, wagmi
396.35 USDC - $396.35
Overview
Risk Rating | Number of issues |
---|---|
Low Risk | 6 |
Non-Critical Risk | 4 |
Codebase Impressions Overall, the code is of very high quality. It does indeed feel like the sponsor already went through a previous contest =). The usual QA findings are almost non-existent. In terms of Gas Optimizations, the sponsor did mention the different conscious choices, which is excellent in terms of communication with auditors.
Table of Contents
@openzeppelin/contracts
versionAs some known vulnerabilities exist in the current @openzeppelin/contracts
version, consider updating package.json
with at least @openzeppelin/contracts@4.7.2
here:
File: package.json 17: "@openzeppelin/contracts": "^4.6.0",
While vulnerabilities are known, the current scope isn't affected (this might not hold true for the whole solution)
The protocol is using low level calls with solidity version <= 0.8.14 which can result in optimizer bug.
https://medium.com/certora/overly-optimistic-optimizer-certora-bug-disclosure-2101e3f7994d
Consider upgrading to solidity 0.8.15 here:
actions/MIMOSwap.sol:2:pragma solidity 0.8.10; actions/MIMOSwap.sol:52: (bool success, bytes memory response) = router.call(swapData.dexTxData); proxy/MIMOProxy.sol:2:pragma solidity >=0.8.4; proxy/MIMOProxy.sol:133: (bool success, bytes memory response) = targets[i].call(data[i]);
As per OpenZeppelin’s (OZ) recommendation, “The guidelines are now to make it impossible for anyone to run initialize
on an implementation contract, by adding an empty constructor with the initializer
modifier. So the implementation contract gets initialized automatically upon deployment.”
Note that this behaviour is also incorporated the OZ Wizard since the UUPS vulnerability discovery: “Additionally, we modified the code generated by the Wizard 19 to include a constructor that automatically initializes the implementation when deployed.”
Furthermore, this thwarts any attempts to frontrun the initialization tx of these contracts:
proxy/MIMOProxy.sol:29: function initialize() external initializer {
Low-level calls call/delegatecall/staticcall return true even if the account called is non-existent (per EVM design).
Consider preventing the address(0)
entry and adding a targets[i].code.length != 0
check after the call here:
proxy/MIMOProxy.sol:133: (bool success, bytes memory response) = targets[i].call(data[i]);
Consider adding an address(0)
on the immutable address here:
proxy/MIMOProxyFactory.sol:16: address public immutable mimoProxyBase;
Please notice that the address(0)
checks are already applied everywhere else on immutable addresses in the solution:
actions/automated/MIMOAutoAction.sol:12: IAddressProvider public immutable a; actions/automated/MIMOAutoRebalance.sol:27: address public immutable mimoRebalance; actions/managed/MIMOManagedAction.sol:12: IAddressProvider public immutable a; actions/managed/MIMOManagedRebalance.sol:20: address public immutable mimoRebalance; actions/MIMOSwap.sol:19: IAddressProvider public immutable a; actions/MIMOSwap.sol:20: IDexAddressProvider public immutable dexAP;
There isn't a withdraw mechanism and a fallback method accepting Ether is implemented here:
proxy/MIMOProxy.sol:38: receive() external payable {}
Also, in the same contract, a method is payable but doesn't use or transfer Ether. This is another place funds can get locked:
File: MIMOProxy.sol 54: function execute(address target, bytes calldata data) public payable override returns (bytes memory response) {
File: MIMOProxy.sol 054: function execute(address target, bytes calldata data) public payable override returns (bytes memory response) { .... 086: // Log the execution. 087: emit Execute(target, data, response); 088: 089: // Check if the call was successful or not. 090: if (!success) { 091: // If there is return data, the call reverted with a reason or a custom error. 092: if (response.length > 0) { 093: assembly { 094: let returndata_size := mload(response) 095: revert(add(32, response), returndata_size) 096: } 097: } else { 098: revert CustomErrors.EXECUTION_REVERTED(); 099: } 100: } 101: }
receive()
functionIf the intention is for the Ether to be used, the function should call another function, otherwise it should revert
proxy/MIMOProxy.sol:38: receive() external payable {}
There are 0.8.10
, ^0.8.4
and >=0.8.4
:
actions/automated/interfaces/IMIMOAutoAction.sol:2:pragma solidity 0.8.10; actions/automated/interfaces/IMIMOAutoRebalance.sol:2:pragma solidity 0.8.10; actions/automated/MIMOAutoAction.sol:2:pragma solidity 0.8.10; actions/automated/MIMOAutoRebalance.sol:2:pragma solidity 0.8.10; actions/interfaces/IMIMOEmptyVault.sol:2:pragma solidity 0.8.10; actions/interfaces/IMIMOFlashloan.sol:2:pragma solidity 0.8.10; actions/interfaces/IMIMOLeverage.sol:2:pragma solidity 0.8.10; actions/interfaces/IMIMOProxyAction.sol:2:pragma solidity 0.8.10; actions/interfaces/IMIMORebalance.sol:2:pragma solidity 0.8.10; actions/interfaces/IMIMOSwap.sol:2:pragma solidity 0.8.10; actions/interfaces/IMIMOVaultActions.sol:2:pragma solidity 0.8.10; actions/managed/interfaces/IMIMOManagedAction.sol:2:pragma solidity 0.8.10; actions/managed/interfaces/IMIMOManagedRebalance.sol:2:pragma solidity 0.8.10; actions/managed/MIMOManagedAction.sol:2:pragma solidity 0.8.10; actions/managed/MIMOManagedRebalance.sol:2:pragma solidity 0.8.10; actions/MIMOEmptyVault.sol:2:pragma solidity 0.8.10; actions/MIMOFlashloan.sol:2:pragma solidity 0.8.10; actions/MIMOLeverage.sol:2:pragma solidity 0.8.10; actions/MIMORebalance.sol:2:pragma solidity 0.8.10; actions/MIMOSwap.sol:2:pragma solidity 0.8.10; actions/MIMOVaultActions.sol:3:pragma solidity 0.8.10; proxy/interfaces/IMIMOProxy.sol:2:pragma solidity ^0.8.4; proxy/interfaces/IMIMOProxyFactory.sol:2:pragma solidity ^0.8.4; proxy/interfaces/IMIMOProxyRegistry.sol:2:pragma solidity ^0.8.4; proxy/MIMOProxy.sol:2:pragma solidity >=0.8.4; proxy/MIMOProxyFactory.sol:2:pragma solidity >=0.8.4; proxy/MIMOProxyRegistry.sol:2:pragma solidity >=0.8.4;
proxy/MIMOProxy.sol:2:pragma solidity >=0.8.4; proxy/MIMOProxyFactory.sol:2:pragma solidity >=0.8.4; proxy/MIMOProxyRegistry.sol:2:pragma solidity >=0.8.4;
#0 - m19
2022-08-18T08:10:10Z
This is an outstanding QA report
🌟 Selected for report: Dravee
Also found by: 0x040, 0x1f8b, 0xDjango, 0xNazgul, 0xSmartContract, 0xc0ffEE, Aymen0909, Bnke0x0, Chom, CodingNameKiki, Deivitto, Fitraldys, Funen, IllIllI, JC, JohnSmith, NoamYakov, ReyAdmirado, Rolezn, TomJ, Waze, ajtra, bearonbike, bobirichman, brgltd, c3phas, durianSausage, fatherOfBlocks, gogo, ignacio, jag, joestakey, ladboy233, mics, oyc_109, rbserver, samruna, sikorico, simon135
381.8352 USDC - $381.84
Overview
Risk Rating | Number of issues |
---|---|
Gas Issues | 13 |
Table of Contents:
memory
keyword when storage
should be usedimmutable
variable<array>.length
should not be looked up in every loop of a for-loop
++i
costs less gas compared to i++
or i += 1
(same for --i
vs i--
or i -= 1
)The code can be optimized by minimizing the number of SLOADs.
SLOADs are expensive (100 gas after the 1st one) compared to MLOADs/MSTOREs (3 gas each). Storage values read multiple times should instead be cached in memory the first time (costing 1 SLOAD) and then read from this cache to avoid multiple SLOADs.
76: if (msg.sender != address(lendingPool)) { //@audit gas: SLOAD (lendingPool) 77: revert CustomErrors.CALLER_NOT_LENDING_POOL(msg.sender, address(lendingPool)); //@audit gas: SLOAD (lendingPool) 98: vaultCollateral.safeIncreaseAllowance(address(lendingPool), flashloanRepayAmount); //@audit gas: SLOAD (lendingPool)
81: if (msg.sender != address(lendingPool)) { //@audit gas: SLOAD (lendingPool) 82: revert CustomErrors.CALLER_NOT_LENDING_POOL(msg.sender, address(lendingPool)); //@audit gas: SLOAD (lendingPool) 100: asset.safeIncreaseAllowance(address(lendingPool), flashloanRepayAmount); //@audit gas: SLOAD (lendingPool)
79: if (msg.sender != address(lendingPool)) { //@audit gas: SLOAD (lendingPool) 80: revert CustomErrors.CALLER_NOT_LENDING_POOL(msg.sender, address(lendingPool)); //@audit gas: SLOAD (lendingPool) 101: fromCollateral.safeIncreaseAllowance(address(lendingPool), flashloanRepayAmount); //@audit gas: SLOAD (lendingPool)
107: if (msg.sender != address(lendingPool)) { //@audit gas: SLOAD (lendingPool) 108: revert CustomErrors.CALLER_NOT_LENDING_POOL(msg.sender, address(lendingPool)); //@audit gas: SLOAD (lendingPool) 129: fromCollateral.safeIncreaseAllowance(address(lendingPool), flashloanRepayAmount); //@audit gas: SLOAD (lendingPool)
108: if (msg.sender != address(lendingPool)) { //@audit gas: SLOAD (lendingPool) 109: revert CustomErrors.CALLER_NOT_LENDING_POOL(msg.sender, address(lendingPool)); //@audit gas: SLOAD (lendingPool) 130: fromCollateral.safeIncreaseAllowance(address(lendingPool), flashloanRepayAmount); //@audit gas: SLOAD (lendingPool)
56: if (owner != msg.sender) { //@audit gas: SLOAD (owner), this is important here as it impacts both the happy and sad paths 62: revert CustomErrors.EXECUTION_NOT_AUTHORIZED(owner, msg.sender, target, selector); //@audit gas: SLOAD (owner) 72: address owner_ = owner; //@audit gas: SLOAD (owner) 82: if (owner_ != owner) { //@audit gas: SLOAD (owner) 83: revert CustomErrors.OWNER_CHANGED(owner_, owner); //@audit gas: SLOAD (owner)
Caching a mapping's value in a local storage
or calldata
variable when the value is accessed multiple times saves ~42 gas per access due to not having to perform the same offset calculation every time.
Affected code:
MIMOEmptyVault.sol#executeOperation()
: amounts[0]
File: MIMOEmptyVault.sol 63: function executeOperation( 64: address[] calldata assets, 65: uint256[] calldata amounts, ... 81: uint256 amount = amounts[0]; 82: vaultCollateral.safeTransfer(address(mimoProxy), amounts[0]);
MIMOLeverage.sol#executeOperation()
: amounts[0]
File: MIMOLeverage.sol 68: function executeOperation( 69: address[] calldata assets, 70: uint256[] calldata amounts, ... 86: asset.safeTransfer(address(mimoProxy), amounts[0]); 87: uint256 flashloanRepayAmount = amounts[0] + premiums[0];
MIMORebalance.sol#executeOperation()
: amounts[0]
File: MIMORebalance.sol 63: function executeOperation( 64: address[] calldata assets, 65: uint256[] calldata amounts, ... 84: uint256 amount = amounts[0]; 85: fromCollateral.safeTransfer(address(mimoProxy), amounts[0]);
MIMOAutoRebalance.sol#executeOperation()
: amounts[0]
File: MIMOAutoRebalance.sol 090: function executeOperation( 091: address[] calldata assets, 092: uint256[] calldata amounts, ... 112: uint256 amount = amounts[0]; 113: fromCollateral.safeTransfer(address(mimoProxy), amounts[0]); 114: uint256 flashloanRepayAmount = amounts[0] + premiums[0];
MIMOManagedRebalance.sol#executeOperation()
: amounts[0]
File: MIMOManagedRebalance.sol 091: function executeOperation( 092: address[] calldata assets, 093: uint256[] calldata amounts, ... 113: uint256 amount = amounts[0]; 114: fromCollateral.safeTransfer(address(mimoProxy), amounts[0]); 115: uint256 flashloanRepayAmount = amounts[0] + premiums[0];
memory
keyword when storage
should be usedWhen copying a state struct in memory, there are as many SLOADs and MSTOREs as there are slots. When reading the whole struct multiple times is not needed, it's better to actually only read the relevant field(s). When only some of the fields are read several times, these particular values should be cached instead of the whole state struct.
Consider using a storage
pointer instead of memory
location here:
File: DexAddressProvider.sol 51: function getDex(uint256 index) external view override returns (address, address) { - 52: Dex memory dex = _dexMapping[index]; + 52: Dex storage dex = _dexMapping[index]; 53: return (dex.proxy, dex.router); 54: }
immutable
variableimmutable
variables aren't storage variable, their instances get replaced in the code with their value. This caching operation is unnecessary here:
File: MIMOVaultActions.sol 65: function depositAndBorrow( 66: IERC20 collateral, 67: uint256 depositAmount, 68: uint256 borrowAmount 69: ) external override { 70: IVaultsCore core_ = core; //@audit gas: unnecessary MSTORE as core is immutable 71: collateral.safeTransferFrom(msg.sender, address(this), depositAmount); 72: collateral.safeIncreaseAllowance(address(core_), depositAmount);//@audit gas: unnecessary MLOAD as core is immutable 73: core_.depositAndBorrow(address(collateral), depositAmount, borrowAmount);//@audit gas: unnecessary MLOAD as core is immutable 74: }
External calls are expensive. Consider using the already existing cached value for the following:
File: MIMOManagedRebalance.sol 50: function rebalance( ... 61: address vaultOwner = vaultsData.vaultOwner(rbData.vaultId); ... - 65: _takeFlashLoan(flData, abi.encode(vaultsData.vaultOwner(rbData.vaultId), managerFee, rbData, swapData)); + 65: _takeFlashLoan(flData, abi.encode(vaultOwner, managerFee, rbData, swapData));
Solidity version 0.8+ comes with implicit overflow and underflow checks on unsigned integers. When an overflow or an underflow isn't possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by using an unchecked
block: https://docs.soliditylang.org/en/v0.8.10/control-structures.html#checked-or-unchecked-arithmetic
Consider wrapping with an unchecked
block here (around 25 gas saved per instance):
097: if (swapResultValue >= rebalanceValue) { 098: return true; 099: } 100: 101: uint256 vaultVariation = (rebalanceValue - swapResultValue).wadDiv(rebalanceValue); //@audit should be unchecked due to L97
120: if (swapResultValue >= rebalanceValue) { 121: return true; 122: } 123: 124: uint256 vaultVariation = (rebalanceValue - swapResultValue).wadDiv(rebalanceValue); //@audit should be unchecked due to L120
132: if (collateralBalanceAfter > flashloanRepayAmount) { 133: token.safeIncreaseAllowance(address(core), collateralBalanceAfter - flashloanRepayAmount); //@audit should be unchecked due to L132
<array>.length
should not be looked up in every loop of a for-loop
Note : This is describing an optimization that the sponsor already chose to ignore, to be thorough: https://github.com/code-423n4/2022-08-mimo/tree/main/docs/#for-loop-syntax
Reading array length at each iteration of the loop consumes more gas than necessary.
In the best case scenario (length read on a memory variable), caching the array length in the stack saves around 3 gas per iteration. In the worst case scenario (external calls at each iteration), the amount of gas wasted can be massive.
Here, consider storing the array's length in a variable before the for-loop, and use this new variable instead:
proxy/MIMOProxy.sol:132: for (uint256 i = 0; i < targets.length; i++) {
++i
costs less gas compared to i++
or i += 1
(same for --i
vs i--
or i -= 1
)Pre-increments and pre-decrements are cheaper.
For a uint256 i
variable, the following is true with the Optimizer enabled at 10k:
Increment:
i += 1
is the most expensive formi++
costs 6 gas less than i += 1
++i
costs 5 gas less than i++
(11 gas less than i += 1
)Decrement:
i -= 1
is the most expensive formi--
costs 11 gas less than i -= 1
--i
costs 5 gas less than i--
(16 gas less than i -= 1
)Note that post-increments (or post-decrements) return the old value before incrementing or decrementing, hence the name post-increment:
uint i = 1; uint j = 2; require(j == i++, "This will be false as i is incremented after the comparison");
However, pre-increments (or pre-decrements) return the new value:
uint i = 1; uint j = 2; require(j == ++i, "This will be true as i is incremented before the comparison");
In the pre-increment case, the compiler has to create a temporary variable (when used) for returning 1
instead of 2
.
Affected code:
proxy/MIMOProxy.sol:132: for (uint256 i = 0; i < targets.length; i++) {
Consider using pre-increments and pre-decrements where they are relevant (meaning: not where post-increments/decrements logic are relevant).
Note : This is describing an optimization that the sponsor already chose to ignore, to be thorough: https://github.com/code-423n4/2022-08-mimo/tree/main/docs/#for-loop-syntax
In Solidity 0.8+, there's a default overflow check on unsigned integers. It's possible to uncheck this in for-loops and save some gas at each iteration, but at the cost of some code readability, as this uncheck cannot be made inline.
Consider wrapping with an unchecked
block here (around 25 gas saved per instance):
proxy/MIMOProxy.sol:132: for (uint256 i = 0; i < targets.length; i++) {
The change would be:
- for (uint256 i; i < numIterations; i++) { + for (uint256 i; i < numIterations;) { // ... + unchecked { ++i; } }
The same can be applied with decrements (which should use break
when i == 0
).
The risk of overflow is non-existent for uint256
here.
If a variable is not set/initialized, it is assumed to have the default value (0
for uint
, false
for bool
, address(0)
for address...). Explicitly initializing it with its default value is an anti-pattern and wastes gas (around 3 gas per instance).
Affected code:
proxy/MIMOProxy.sol:132: for (uint256 i = 0; i < targets.length; i++) {
Consider removing explicit initializations for default values.
Using newer compiler versions and the optimizer give gas optimizations. Also, additional safety checks are available for free.
The advantages here are:
Consider upgrading here :
proxy/MIMOProxy.sol:2:pragma solidity >=0.8.4; proxy/MIMOProxyFactory.sol:2:pragma solidity >=0.8.4; proxy/MIMOProxyRegistry.sol:2:pragma solidity >=0.8.4;
The original warden who proved these type of findings is 0xKitsune. Clone the repo 0xKitsune/gas-lab, copy/paste the contract examples and run forge test --gas-report
to replicate the gas reports with the optimizer turned on and set to 10000 runs.
Use assembly for math instead of Solidity. You can check for overflow/underflow in assembly to ensure safety. If using Solidity versions < 0.8.0 and you are using Safemath, you can gain significant gas savings by using assembly to calculate values and checking for overflow/underflow.
POC Contract:
contract GasTest is DSTest { Contract0 c0; Contract1 c1; Contract2 c2; Contract3 c3; Contract4 c4; Contract5 c5; Contract6 c6; Contract7 c7; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); c2 = new Contract2(); c3 = new Contract3(); c4 = new Contract4(); c5 = new Contract5(); c6 = new Contract6(); c7 = new Contract7(); } function testGas() public { c0.addTest(34598345, 100); c1.addAssemblyTest(34598345, 100); c2.subTest(34598345, 100); c3.subAssemblyTest(34598345, 100); c4.mulTest(34598345, 100); c5.mulAssemblyTest(34598345, 100); c6.divTest(34598345, 100); c7.divAssemblyTest(34598345, 100); } } contract Contract0 { //addition in Solidity function addTest(uint256 a, uint256 b) public pure { uint256 c = a + b; } } contract Contract1 { //addition in assembly function addAssemblyTest(uint256 a, uint256 b) public pure { assembly { let c := add(a, b) if lt(c, a) { mstore(0x00, "overflow") revert(0x00, 0x20) } } } } contract Contract2 { //subtraction in Solidity function subTest(uint256 a, uint256 b) public pure { uint256 c = a - b; } } contract Contract3 { //subtraction in assembly function subAssemblyTest(uint256 a, uint256 b) public pure { assembly { let c := sub(a, b) if gt(c, a) { mstore(0x00, "underflow") revert(0x00, 0x20) } } } } contract Contract4 { //multiplication in Solidity function mulTest(uint256 a, uint256 b) public pure { uint256 c = a * b; } } contract Contract5 { //multiplication in assembly function mulAssemblyTest(uint256 a, uint256 b) public pure { assembly { let c := mul(a, b) if lt(c, a) { mstore(0x00, "overflow") revert(0x00, 0x20) } } } } contract Contract6 { //division in Solidity function divTest(uint256 a, uint256 b) public pure { uint256 c = a * b; } } contract Contract7 { //division in assembly function divAssemblyTest(uint256 a, uint256 b) public pure { assembly { let c := div(a, b) if gt(c, a) { mstore(0x00, "underflow") revert(0x00, 0x20) } } } }
POC Gas Report:
╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 40493 ┆ 233 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ addTest ┆ 303 ┆ 303 ┆ 303 ┆ 303 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 37087 ┆ 216 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ addAssemblyTest ┆ 263 ┆ 263 ┆ 263 ┆ 263 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract2 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 40293 ┆ 232 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ subTest ┆ 300 ┆ 300 ┆ 300 ┆ 300 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract3 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 37287 ┆ 217 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ subAssemblyTest ┆ 263 ┆ 263 ┆ 263 ┆ 263 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract4 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 41893 ┆ 240 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ mulTest ┆ 325 ┆ 325 ┆ 325 ┆ 325 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract5 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 37087 ┆ 216 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ mulAssemblyTest ┆ 265 ┆ 265 ┆ 265 ┆ 265 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract6 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 41893 ┆ 240 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ divTest ┆ 325 ┆ 325 ┆ 325 ┆ 325 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract7 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 37287 ┆ 217 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ divAssemblyTest ┆ 265 ┆ 265 ┆ 265 ┆ 265 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
Affected code:
actions/automated/MIMOAutoAction.sol:41: uint256 maxVarFee = (autoParams.targetRatio.wadDiv(toVaultMcr + autoParams.mcrBuffer) + WadRayMath.WAD).wadDiv( actions/automated/MIMOAutoAction.sol:101: uint256 vaultVariation = (rebalanceValue - swapResultValue).wadDiv(rebalanceValue); actions/automated/MIMOAutoRebalance.sol:16: targetRatio * (vaultDebt + fixedFee) - collateralValue actions/automated/MIMOAutoRebalance.sol:18: targetRatio / mcrB - 1 - targetRatio * variableFee actions/automated/MIMOAutoRebalance.sol:114: uint256 flashloanRepayAmount = amounts[0] + premiums[0]; actions/automated/MIMOAutoRebalance.sol:180: uint256 targetRatio = autoVault.targetRatio + 1e15; // add 0.1% to account for rounding actions/automated/MIMOAutoRebalance.sol:184: uint256 rebalanceValue = (targetRatio.wadMul(vaultState.vaultDebt + autoVault.fixedFee) - actions/automated/MIMOAutoRebalance.sol:186: (targetRatio.wadDiv(toVaultMcr + autoVault.mcrBuffer) - targetRatio.wadMul(autoVault.varFee) - WadRayMath.WAD) actions/automated/MIMOAutoRebalance.sol:189: autoFee = autoVault.fixedFee + rebalanceValue.wadMul(autoVault.varFee); actions/automated/MIMOAutoRebalance.sol:191: mintAmount = rebalanceValue.wadDiv(toVaultMcr + autoVault.mcrBuffer) - autoFee; actions/automated/MIMOAutoRebalance.sol:275: uint256 swapResultValue = priceFeed.convertFrom(autoVault.toCollateral, vaultBBalanceAfter - vaultBBalanceBefore); actions/managed/MIMOManagedAction.sol:124: uint256 vaultVariation = (rebalanceValue - swapResultValue).wadDiv(rebalanceValue); actions/managed/MIMOManagedRebalance.sol:60: uint256 managerFee = managedVault.fixedFee + flData.amount.wadMul(managedVault.varFee); actions/managed/MIMOManagedRebalance.sol:115: uint256 flashloanRepayAmount = amounts[0] + premiums[0]; actions/managed/MIMOManagedRebalance.sol:193: uint256 swapResultValue = priceFeed.convertFrom(toCollateral, vaultBBalanceAfter - vaultBBalanceBefore); actions/managed/MIMOManagedRebalance.sol:206: uint256 minVaultBRatio = a.config().collateralMinCollateralRatio(toCollateral) + managedVault.mcrBuffer; actions/MIMOEmptyVault.sol:83: uint256 flashloanRepayAmount = amount + premiums[0]; actions/MIMOLeverage.sol:87: uint256 flashloanRepayAmount = amounts[0] + premiums[0]; actions/MIMOLeverage.sol:133: token.safeIncreaseAllowance(address(core), collateralBalanceAfter - flashloanRepayAmount); actions/MIMOLeverage.sol:134: core.deposit(address(token), collateralBalanceAfter - flashloanRepayAmount); actions/MIMORebalance.sol:86: uint256 flashloanRepayAmount = amount + premiums[0]; actions/MIMORebalance.sol:127: core.depositAndBorrow(address(rbData.toCollateral), depositAmount, rbData.mintAmount + fee); proxy/MIMOProxy.sol:75: uint256 stipend = gasleft() - minGasReserve;
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public view { c0.ownerNotZero(address(this)); c1.assemblyOwnerNotZero(address(this)); } } contract Contract0 { function ownerNotZero(address _addr) public pure { require(_addr != address(0), "zero address)"); } } contract Contract1 { function assemblyOwnerNotZero(address _addr) public pure { assembly { if iszero(_addr) { mstore(0x00, "zero address") revert(0x00, 0x20) } } } }
POC Gas Report:
╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 61311 ┆ 338 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ ownerNotZero ┆ 258 ┆ 258 ┆ 258 ┆ 258 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭──────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞══════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 44893 ┆ 255 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ assemblyOwnerNotZero ┆ 252 ┆ 252 ┆ 252 ┆ 252 ┆ 1 │ ╰──────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
Affected code:
actions/automated/MIMOAutoAction.sol:19: if (address(_a) == address(0) || address(_proxyRegistry) == address(0)) { actions/automated/MIMOAutoRebalance.sol:41: if (_mimoRebalance == address(0)) { actions/managed/MIMOManagedAction.sol:20: if (address(_a) == address(0) || address(_proxyRegistry) == address(0)) { actions/managed/MIMOManagedRebalance.sol:34: if (_mimoRebalance == address(0)) { actions/MIMOEmptyVault.sol:31: if (address(_proxyRegistry) == address(0)) { actions/MIMOFlashloan.sol:22: if (address(_lendingPool) == address(0)) { actions/MIMOLeverage.sol:31: if (address(_proxyRegistry) == address(0)) { actions/MIMORebalance.sol:31: if (address(_proxyRegistry) == address(0)) { actions/MIMOSwap.sol:27: if (address(_a) == address(0) || address(_dexAP) == address(0)) { actions/MIMOSwap.sol:47: require(proxy != address(0), Errors.INVALID_AGGREGATOR); actions/MIMOSwap.sol:48: require(router != address(0), Errors.INVALID_AGGREGATOR); actions/MIMOVaultActions.sol:33: if (address(_core) == address(0) || address(_vaultsData) == address(0) || address(_stablex) == address(0)) { proxy/MIMOProxyRegistry.sol:49: if (address(currentProxy) != address(0) && currentProxy.owner() == owner) {
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testGas() public { c0.updateOwner(0x158B28A1b1CB1BE12C6bD8f5a646a0e3B2024734); c1.assemblyUpdateOwner(0x158B28A1b1CB1BE12C6bD8f5a646a0e3B2024734); } } contract Contract0 { address owner = 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84; function updateOwner(address newOwner) public { owner = newOwner; } } contract Contract1 { address owner = 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84; function assemblyUpdateOwner(address newOwner) public { assembly { sstore(owner.slot, newOwner) } } }
POC Gas Report:
╭────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 60623 ┆ 261 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ updateOwner ┆ 5302 ┆ 5302 ┆ 5302 ┆ 5302 ┆ 1 │ ╰────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯ ╭────────────────────┬─────────────────┬──────┬────────┬──────┬─────────╮ │ Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪══════╪════════╪══════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 54823 ┆ 232 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ assemblyUpdateOwner┆ 5236 ┆ 5236 ┆ 5236 ┆ 5236 ┆ 1 │ ╰────────────────────┴─────────────────┴──────┴────────┴──────┴─────────╯
Affected code:
actions/automated/MIMOAutoAction.sol:32: function setAutomation(uint256 vaultId, AutomatedVault calldata autoParams) external override { actions/managed/MIMOManagedAction.sol:33: function setManagement(uint256 vaultId, ManagedVault calldata mgtParams) external override { actions/managed/MIMOManagedAction.sol:55: function setManager(address manager, bool isManager) external override { proxy/MIMOProxy.sol:104: function setPermission(
Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas
Additionally, custom errors can be used inside and outside of contracts (including interfaces and libraries).
Source: https://blog.soliditylang.org/2021/04/21/custom-errors/:
Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g.,
revert("Insufficient funds.");
), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
POC Contract:
contract GasTest is DSTest { Contract0 c0; Contract1 c1; function setUp() public { c0 = new Contract0(); c1 = new Contract1(); } function testFailGas() public { c0.stringErrorMessage(); c1.customErrorMessage(); } } contract Contract0 { function stringErrorMessage() public { bool check = false; require(check, "error message"); } } contract Contract1 { error CustomError(); function customErrorMessage() public { bool check = false; if (!check) { revert CustomError(); } } }
POC Gas Report:
╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract0 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 34087 ┆ 200 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ stringErrorMessage ┆ 218 ┆ 218 ┆ 218 ┆ 218 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯ ╭────────────────────┬─────────────────┬─────┬────────┬─────┬─────────╮ │ Contract1 contract ┆ ┆ ┆ ┆ ┆ │ ╞════════════════════╪═════════════════╪═════╪════════╪═════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 26881 ┆ 164 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ customErrorMessage ┆ 161 ┆ 161 ┆ 161 ┆ 161 ┆ 1 │ ╰────────────────────┴─────────────────┴─────┴────────┴─────┴─────────╯
Consider replacing all revert strings with custom errors in the solution.
actions/MIMOEmptyVault.sol:96: require(flashloanRepayAmount <= vaultCollateral.balanceOf(address(this)), Errors.CANNOT_REPAY_FLASHLOAN); actions/MIMOLeverage.sol:130: require(collateralBalanceAfter >= flashloanRepayAmount, Errors.CANNOT_REPAY_FLASHLOAN); actions/MIMORebalance.sol:129: require( actions/MIMOSwap.sol:47: require(proxy != address(0), Errors.INVALID_AGGREGATOR); actions/MIMOSwap.sol:48: require(router != address(0), Errors.INVALID_AGGREGATOR);
#0 - m19
2022-08-18T07:56:05Z
We found this gas report really outstanding