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: 62/69
Findings: 1
Award: $49.17
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
49.1659 USDC - $49.17
Several optimizations allow to save significant amounts of gas upon deployment of contracts and function invocations. The most interesting saving is in
MIMOProxy.sol
, as it is will get deployed by many users, where setting a storage variable that is not modified asconstant
allows to save a lot of gas on deployment and onexecute
calls. This is the first issue detailed in this report. It is also a very simple change for the team to make Changingrequire
statements into custom errors is also worth considering, as there is a few instances (5) inMIMOEmptyVault
, and each change would yield high gas savings upon deployment.
If a variable is set to a fixed value and never modified afterwards, marking it as constant
can save a storage slot.
1 instance:
initialize()
as 5_000
and never modified afterwards.Manual Analysis
Mark minGasReserve
as constant
. Instead of writing it in initialize(), hardcode it with its value.
+uint256 public constant override minGasReserve = 5_000; -uint256 public override minGasReserve;
Contract | Method | Min | Max | Avg |
---|---|---|---|---|
MIMOProxy Deployment | 895092 | |||
------------ | ------------------- | ----- | ----- | -------- |
MIMOProxy execute | 491976 |
Contract | Method | Min | Max | Avg |
---|---|---|---|---|
MIMOProxy Deployment | 893400 | |||
------------ | ------------------- | ----- | ----- | -------- |
MIMOProxy execute | 488999 |
This saves 1,692
gas upon deployment for MIMOProxy.sol
, and 2,977
gas per execute
call
Booleans are more expensive than uint256: each write operation emits an extra SLOAD
to first read the slot's contents, replace the bits taken up by the boolean, and then write back.
Instances:
MIMOProxyFactory.sol:53: _proxies[address(proxy)] = true
Manual Analysis
Use uint256(2)
and uint256(1)
instead of true
and false
- mapping(address => bool) internal _proxies; + mapping(address => uint256) internal _proxies; - function isProxy(address proxy) external view override returns (bool result) { + function isProxy(address proxy) external view override returns (uint256 result) { - _proxies[address(proxy)] = true; + _proxies[address(proxy)] = 2;
Contract | Method | Min | Max | Avg |
---|---|---|---|---|
MIMOProxyFactory Deployment | 243297 |
Contract | Method | Min | Max | Avg |
---|---|---|---|---|
MIMOProxyFactory Deployment | 234703 |
This saves 8,594
gas on deployment for MIMOProxyFactory.sol
Marking constants as private
save gas upon deployment, as the compiler does not have to create getter functions for these variables. It is worth noting that a private
variable can still be read using either the verified contract source code or the bytecode.
Manual Analysis
- address public immutable mimoProxyBase; + address private immutable mimoProxyBase;
Contract | Method | Min | Max | Avg |
---|---|---|---|---|
MIMOProxyFactory Deployment | 243297 |
Contract | Method | Min | Max | Avg |
---|---|---|---|---|
MIMOProxyFactory Deployment | 232742 |
This saves 10,555
gas on deployment for MIMOProxyFactory.sol
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met) while providing the same amount of information, as explained here
Custom errors are defined using the error statement
5 instances:
MIMOEmptyVault.sol:95: require(flashloanRepayAmount <= vaultCollateral.balanceOf(address(this)), Errors.CANNOT_REPAY_FLASHLOAN); MIMOLeverage.sol:129: require(collateralBalanceAfter >= flashloanRepayAmount, Errors.CANNOT_REPAY_FLASHLOAN); MIMORebalance.sol:128: require( MIMORebalance.sol:129: a.vaultsData().vaultCollateralBalance(rbData.vaultId) >= flashloanRepayAmount, MIMORebalance.sol:130: Errors.CANNOT_REPAY_FLASHLOAN, MIMORebalance.sol:131: ); MIMOSwap.sol:46: require(proxy != address(0), Errors.INVALID_AGGREGATOR); MIMOSwap.sol:47: require(router != address(0), Errors.INVALID_AGGREGATOR);
Manual Analysis
Replace require and revert statements with custom errors.
For instance, in MIMOEmptyVault.sol
:
- require(flashloanRepayAmount <= vaultCollateral.balanceOf(address(this)), Errors.CANNOT_REPAY_FLASHLOAN); + if (flashloanRepayAmount > vaultCollateral.balanceOf(address(this))) { + revert CustomErrors.CANNOT_REPAY_FLASHLOAN; + }
And define CANNOT_REPAY_FLASHLOAN
as a CustomErrors
.
Contract | Method | Min | Max | Avg |
---|---|---|---|---|
MIMOEmptyVault | Deployment | 1381499 |
Contract | Method | Min | Max | Avg |
---|---|---|---|---|
MIMOEmptyVault | Deployment | 1375408 |
This one require statement changed into a custom error saves 6,091
gas upon deployment for MIMOEmptyVault.sol
#0 - joestakey
2022-08-22T20:18:24Z
@gzeoneth Can you compare this report with this one ? It's not clear why this report scored less, while it details more high gas savings issues.
#86 details two high gas savings issues, 2 and 7, which are both included in this report. This report even includes more instances of the require statements
issue.
#1 - gzeoneth
2022-08-24T10:39:36Z
@gzeoneth Can you compare this report with this one ? It's not clear why this report scored less, while it details more high gas savings issues. #86 details two high gas savings issues, 2 and 7, which are both included in this report. This report even includes more instances of the
require statements
issue.
thanks I will review