Mimo DeFi contest - joestakey's results

Bridging the chasm between the DeFi world and the world of regulated financial institutions.

General Information

Platform: Code4rena

Start Date: 28/04/2022

Pot Size: $50,000 USDC

Total HM: 7

Participants: 43

Period: 5 days

Judge: gzeon

Total Solo HM: 2

Id: 115

League: ETH

Mimo DeFi

Findings Distribution

Researcher Performance

Rank: 14/43

Findings: 3

Award: $693.86

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: hyh

Also found by: 0xDjango, berndartmueller, cccz, defsec, delfin454000, joestakey, robee

Labels

bug
duplicate
2 (Med Risk)
upgraded by judge

Awards

247.8825 USDC - $247.88

External Links

Judge has assessed an item in Issue #114 as Medium risk. The relevant finding follows:

PROBLEM All external functions that can be called by users should have comments

SEVERITY Non-Critical

PROOF OF CONCEPT Instances include:

SuperVaultFactory.sol SuperVaultFactory.sol:23: function clone(bytes calldata _initdata) public TOOLS USED Manual Analysis

MITIGATION Add comments to these functions

#0 - gzeoneth

2022-06-05T15:26:41Z

Duplicate of #145

Awards

152.6741 USDC - $152.67

Labels

bug
QA (Quality Assurance)

External Links

QA Report

Table of Contents

summary

Few vulnerabilities were found examining the contracts. The main concerns are with ERC20 methods calls.

Events emitted early

PROBLEM

It is not recommended to emit events before the end of the computations, as the function might revert based on conditions ahead of the event emission

SEVERITY

Non-Critical

PROOF OF CONCEPT

Instances include:

DemandMinerV2.sol

DemandMinerV2.sol:88: emit WithdrawFeeReleased(fee); //ERC20 safeTransfer() is called after the emission of this event

TOOLS USED

Manual Analysis

MITIGATION

Place the event emission in the last position in the function.

Event should be emitted in setters

PROBLEM

Setters should emit an event so that Dapps can detect important changes to storage

SEVERITY

Non-Critical

PROOF OF CONCEPT

Instances include:

DexAddressProvider.sol

DexAddressProvider.sol:33: setDexMapping()

InceptionVaultsDataProvider.sol

InceptionVaultsDataProvider.sol:63: setCollateralBalance() InceptionVaultsDataProvider.sol:75: setBaseDebt()

PARMiner.sol

PARMiner.sol:82: setLiquidateCallerReward()

TOOLS USED

Manual Analysis

MITIGATION

Emit an event in all setters.

Function missing comments

PROBLEM

All external functions that can be called by users should have comments

SEVERITY

Non-Critical

PROOF OF CONCEPT

Instances include:

SuperVaultFactory.sol

SuperVaultFactory.sol:23: function clone(bytes calldata _initdata) public

TOOLS USED

Manual Analysis

MITIGATION

Add comments to these functions

Uint256 alias

IMPACT

uint is an alias for uint256.

It is better to use uint256: it brings readability and consistency in the code, and it future proofs it in case of any changes to the alias of uint

SEVERITY

Non-Critical

PROOF OF CONCEPT

Instances include:

SuperVault.sol

SuperVault.sol:30: uint dexIndex; SuperVault.sol:127: uint dexIndex; SuperVault.sol:143: uint dexIndex; SuperVault.sol:144: uint ; SuperVault.sol:176: uint dexIndex; SuperVault.sol:194: uint dexIndex; SuperVault.sol:223: uint dexIndex; SuperVault.sol:322: uint dexIndex; SuperVault.sol:324: uint ;

TOOLS USED

Manual Analysis

MITIGATION

replace uint with uint256

Unchecked inputs

PROBLEM

There should be a zero (address or integer) check in all setters

SEVERITY

Non-Critical

PROOF OF CONCEPT

Instances include:

DemandMinerV2.sol

DemandMinerV2.sol:181: function setFeeCollector(address feeCollector)

TOOLS USED

Manual Analysis

MITIGATION

Add a zero address check to this function.

Approve 0 first

PROBLEM

Some tokens (like USDT) do not work when changing the allowance from an existing non-zero allowance value. They must first be approved for zero and then the actual allowance must be approved. In case such token is used as collateral, it is safer to add a zero approval before changing the allowance in the contract.

SEVERITY

Low

PROOF OF CONCEPT

Instances include:

PARMinerV2.sol

PARMinerV2.sol:125: collateralToken.approve(proxy, collateralToken.balanceOf(address(this)));

TOOLS USED

Manual Analysis

MITIGATION

Approve 0 before approving a new value:

+collateralToken.approve(proxy, 0); collateralToken.approve(proxy, collateralToken.balanceOf(address(this)));

Unsafe call to decimals()

PROBLEM

In BalancerV2LPOracle.sol, _getNormalizedBalance() uses the decimals() method of the ERC20 token to compute its output. The problem is that decimals() is not part of the official ERC20 standard and might fail for tokens that do not implement it. While in practice it is very unlikely, as usually most of the tokens implement it, this should still be considered as a valid concern, as it means there is a risk the oracle returns an incorrect price.

SEVERITY

Low

PROOF OF CONCEPT

Instances include:

BalancerV2LPOracle.sol

BalancerV2LPOracle.sol:126: function _getNormalizedBalance(address token, uint256 balance) internal view returns (uint256) { uint8 decimals = ERC20(token).decimals(); return balance.mul(MathPow.pow(10, 18 - decimals)); }

GUniLPOracle.sol

GUniLPOracle.sol:45-51: uint256 decimalsA = ERC20(_pool.token0()).decimals(); _tokenDecimalsUnitA = 10**decimalsA; _tokenDecimalsOffsetA = 10**(18 - decimalsA); uint256 decimalsB = ERC20(_pool.token1()).decimals(); _tokenDecimalsUnitB = 10**decimalsB; _tokenDecimalsOffsetB = 10**(18 - decimalsB);

TOOLS USED

Manual Analysis

MITIGATION

While this reduces flexibility, the safest solution is to hardcode the value of decimals in these functions.

Awards

293.3066 USDC - $293.31

Labels

bug
G (Gas Optimization)

External Links

Gas Report

Table of Contents

Caching storage variables in memory to save gas

IMPACT

Anytime you are reading from storage more than once, it is cheaper in gas cost to cache the variable in memory: a SLOAD cost 100gas, while MLOAD and MSTORE cost 3 gas.

In particular, in for loops, when using the length of a storage array as the condition being checked after each loop, caching the array length in memory can yield significant gas savings if the array length is high

PROOF OF CONCEPT

Instances include:

AdminInceptionVault.sol

scope: withdraw()

  • _a is read twice:
AdminInceptionVault.sol:99 AdminInceptionVault.sol:100

scope: claimMimo()

  • _collateralCount is read (_collateralCount) times: number of reads depending on _collateralCount as it is in a for loop
AdminInceptionVault.sol:108

scope: claimMimo()

  • _debtNotifier is read (_collateralCount) times: number of reads depending on _collateralCount as it is in a for loop
AdminInceptionVault.sol:108

scope: deposit()

  • _a is read twice:
AdminInceptionVault.sol:152 AdminInceptionVault.sol:153

InceptionVaultsCore.sol

scope: borrow()

  • _a is read three times:
AdminInceptionVault.sol:137 AdminInceptionVault.sol:151 AdminInceptionVault.sol:158
  • _adminInceptionVault is read twice:
AdminInceptionVault.sol:138 AdminInceptionVault.sol:162
  • _inceptionVaultsData is read three times:
AdminInceptionVault.sol:139 AdminInceptionVault.sol:153 AdminInceptionVault.sol:156
  • _vaultConfig is read three times:
AdminInceptionVault.sol:145 AdminInceptionVault.sol:146 AdminInceptionVault.sol:158

scope: liquidatePartial()

  • _a is read three times:
AdminInceptionVault.sol:205 AdminInceptionVault.sol:226 AdminInceptionVault.sol:233
  • _adminInceptionVault is read twice:
AdminInceptionVault.sol:226 AdminInceptionVault.sol:235
  • _inceptionVaultsData is read three times:
AdminInceptionVault.sol:200 AdminInceptionVault.sol:203 AdminInceptionVault.sol:238
  • _inceptionPriceFeed is read twice:
AdminInceptionVault.sol:202 AdminInceptionVault.sol:237
  • _vaultConfig is read four times:
AdminInceptionVault.sol:205 AdminInceptionVault.sol:208 AdminInceptionVault.sol:217 AdminInceptionVault.sol:222

scope: _removeCollateralFromVault()

  • _inceptionVaultsData is read three times:
AdminInceptionVault.sol:284 AdminInceptionVault.sol:287 AdminInceptionVault.sol:294

scope: _addCollateralToVaultById()

  • _inceptionVaultsData is read twice:
AdminInceptionVault.sol:305 AdminInceptionVault.sol:306

scope: _refreshCumulativeRate()

  • _cumulativeRat is read three times:
AdminInceptionVault.sol:313 AdminInceptionVault.sol:313 AdminInceptionVault.sol:315

scope: _reduceVaultDebt()

  • _inceptionVaultsData is read twice:
AdminInceptionVault.sol:326 AdminInceptionVault.sol:330 AdminInceptionVault.sol:333

ChainlinkInceptionPriceFeed.sol

scope: getAssetPrice()

  • _eurOracle is read twice:
ChainlinkInceptionPriceFeed.sol:74 ChainlinkInceptionPriceFeed.sol:82

GenericMinerV2.sol

scope: _pendingMIMO()

  • _totalStakeWithBoost is read twice:
GenericMinerV2.sol:281 GenericMinerV2.sol:286

scope: _pendingPAR()

  • _totalStakeWithBoost is read twice:
GenericMinerV2.sol:295 GenericMinerV2.sol:300

scope: _getBoostMultiplier()

  • _boostConfig is read six times:
GenericMinerV2.sol:316 GenericMinerV2.sol:317 GenericMinerV2.sol:318 GenericMinerV2.sol:319 GenericMinerV2.sol:322 GenericMinerV2.sol:331

PARMinerV2.sol

scope: liquidate()

  • _par is read three times:
PARMinerV2.sol:118 PARMinerV2.sol:127 PARMinerV2.sol:128
  • _a is read twice:
PARMinerV2.sol:120 PARMinerV2.sol:122

scope: _increaseStake()

  • __accParAmountPerShare is read twice:
PARMinerV2.sol:261 PARMinerV2.sol:365

scope: _pendingMIMO()

  • _totalStakeWithBoost is read twice:
PARMinerV2.sol:376 PARMinerV2.sol:381

scope: _getBoostMultiplier()

  • _boostConfig is read six times:
PARMinerV2.sol:411 PARMinerV2.sol:412 PARMinerV2.sol:413 PARMinerV2.sol:414 PARMinerV2.sol:417 PARMinerV2.sol:426

BalancerV2LPOracle.sol

scope: latestRoundData()

  • pool is read twice:
BalancerV2LPOracle.sol:104 BalancerV2LPOracle.sol:117

SuperVault.sol

scope: executeOperation()

  • lendingPool is read twice:
SuperVault.sol:83 SuperVault.sol:97

scope: emptyVaultOperation()

  • a is read five times:
SuperVault.sol:198 SuperVault.sol:199 SuperVault.sol:202 SuperVault.sol:203 SuperVault.sol:205

scope: emptyVault()

  • a is read twice:
SuperVault.sol:233 SuperVault.sol:233

scope: withdrawFromVault()

  • a is read twice:
SuperVault.sol:245 SuperVault.sol:246

scope: borrowFromVault()

  • a is read three times:
SuperVault.sol:254 SuperVault.sol:255 SuperVault.sol:255

scope: depositToVault()

  • a is read twice:
SuperVault.sol:273 SuperVault.sol:275

scope: depositAndBorrowFromVault()

  • a is read four times:
SuperVault.sol:289 SuperVault.sol:291 SuperVault.sol:292 SuperVault.sol:292

scope: depositETHAndBorrowFromVault()

  • a is read three times:
SuperVault.sol:312 SuperVault.sol:313 SuperVault.sol:313

scope: leverageSwap()

  • a is read three times:
SuperVault.sol:326 SuperVault.sol:327 SuperVault.sol:328

scope: checkAndSendMIMO()

  • ga is read three times:
SuperVault.sol:369 SuperVault.sol:370 SuperVault.sol:370

TOOLS USED

Manual Analysis

MITIGATION

cache these storage variables in memory

Calldata instead of memory for RO function parameters

PROBLEM

If a reference type function parameter is read-only, it is cheaper in gas to use calldata instead of memory. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.

Try to use calldata as a data location because it will avoid copies and also makes sure that the data cannot be modified.

PROOF OF CONCEPT

Instances include:

InceptionVaultsCore.sol

scope: initialize()

InceptionVaultsCore.sol:42: VaultConfig memory vaultConfig

DemandMinerV2.sol

scope: initialize()

DemandMinerV2.sol:56: FeeConfig memory newFeeConfig

GenericMinerV2.sol

scope: setBoostConfig()

GenericMinerV2.sol:69: BoostConfig memory newBoostConfig

scope: _releaseRewards()

GenericMinerV2.sol:209: UserInfo memory _userInfo

scope: _updateBoost()

GenericMinerV2.sol:231: UserInfo memory _userInfo

PARMinerV2.sol

scope: setBoostConfig()

PARMinerV2.sol:70: BoostConfig memory newBoostConfig

scope: _releaseRewards()

PARMinerV2.sol:306: UserInfo memory _userInfo

scope: _updateBoost()

PARMinerV2.sol:331: UserInfo memory _userInfo

VotingMinerV2.sol

scope: releaseMIMO()

VotingMinerV2.sol:24: UserInfo memory _userInfo

scope: _syncStake()

VotingMinerV2.sol:67: UserInfo memory _userInfo

SuperVault.sol

scope: leverageOperation()

SuperVault.sol:105: bytes memory params

scope: rebalanceOperation()

SuperVault.sol:141: bytes memory params

scope: emptyVaultOperation()

SuperVault.sol:191: bytes memory params

scope: leverageSwap()

SuperVault.sol:321: bytes memory params

scope: aggregatorSwap()

SuperVault.sol:341: bytes memory dexTXData

scope: takeFlashLoan()

SuperVault.sol:357: bytes memory params

TOOLS USED

Manual Analysis

MITIGATION

Replace memory with calldata

Comparisons with zero for unsigned integers

IMPACT

>0 is less gas efficient than != 0 if you enable the optimizer at 10k AND you’re in a require statement. Detailed explanation with the opcodes here

PROOF OF CONCEPT

Instances include:

InceptionVaultsCore.sol

InceptionVaultsCore.sol:122

ChainlinkInceptionPriceFeed.sol

ChainlinkInceptionPriceFeed.sol:75 ChainlinkInceptionPriceFeed.sol:79

GenericMinerV2.sol

GenericMinerV2.sol:58 GenericMinerV2.sol:70 GenericMinerV2.sol:175 GenericMinerV2.sol:195

PARMinerV2.sol

PARMinerV2.sol:52 PARMinerV2.sol:71 PARMinerV2.sol:254 PARMinerV2.sol:284

GUniLPOracle.sol

GUniLPOracle.sol:112 GUniLPOracle.sol:112

TOOLS USED

Manual Analysis

MITIGATION

Replace > 0 with != 0

Comparison Operators

IMPACT

In the EVM, there is no opcode for >= or <=. When using greater than or equal, two operations are performed: > and =.

Using strict comparison operators hence saves gas

PROOF OF CONCEPT

Instances include:

InceptionVaultsCore.sol

InceptionVaultsCore.sol:138 InceptionVaultsCore.sol:179 InceptionVaultsCore.sol:219 InceptionVaultsCore.sol:226 InceptionVaultsCore.sol:285

GenericMinerV2.sol

GenericMinerV2.sol:58 GenericMinerV2.sol:58 GenericMinerV2.sol:70 GenericMinerV2.sol:70 GenericMinerV2.sol:197 GenericMinerV2.sol:331 GenericMinerV2.sol:331

PARMinerV2.sol

PARMinerV2.sol:52 PARMinerV2.sol:52 PARMinerV2.sol:71 PARMinerV2.sol:71 PARMinerV2.sol:286 PARMinerV2.sol:426 PARMinerV2.sol:426

TOOLS USED

Manual Analysis

MITIGATION

Replace <= with <, and >= with >. Do not forget to increment/decrement the compared variable

example:

-collateralValueToReceive >= collateralValue +collateralValueToReceive > collateralValue - 1;

However, if 1 is negligible compared to the value of the variable, we can omit the increment.

Constant should be used for storage variables that are not modified

IMPACT

Constant variables are replaced at compile time by their values. If a state variable is never modified, it should be specified as constant to save a SLOAD operation when it is read, saving 97 gas.

PROOF OF CONCEPT

Instances include:

BalancerV2LPOracle.sol

BalancerV2LPOracle.sol:18: uint256 public override version = 3;

GUniLPOracle.sol

GUniLPOracle.sol:16: uint256 public override version = 3;

TOOLS USED

Manual Analysis

MITIGATION

Add the constant modifier to these variables.

Constructor parameters should be avoided when possible

IMPACT

Constructor parameters are expensive. The contract deployment will be cheaper in gas if storage variables are hard coded instead of using constructor parameters. It can save approximately 670 gas per storage variable concerned upon deployment (~400 if the optimizer is enabled).

PROOF OF CONCEPT

Instances include:

DexAddressProvider.sol

DexAddressProvider.sol:15 _a = a;

InceptionVaultFactory.sol

InceptionVaultFactory.sol:54 _adminInceptionVaultBase = adminInceptionVaultBase; InceptionVaultFactory.sol:55 _inceptionVaultsCoreBase = inceptionVaultsCoreBase; InceptionVaultFactory.sol:56 _inceptionVaultsDataProviderBase = inceptionVaultsDataProviderBase; InceptionVaultFactory.sol:57 _a = addressProvider; InceptionVaultFactory.sol:58 _debtNotifier = debtNotifier; InceptionVaultFactory.sol:59 _weth = weth; InceptionVaultFactory.sol:60 _mimo = mimo;

DemandMinerV2.sol

DemandMinerV2.sol:34 _token = token; DemandMinerV2.sol:35 _feeCollector = feeCollector; DemandMinerV2.sol:36 _feeConfig = feeConfig;

GenericMinerV2.sol

GenericMinerV2.sol:56 _a = _addresses; GenericMinerV2.sol:59 _boostConfig = boostConfig;

PARMinerV2.sol

PARMinerV2.sol:53 _a = govAP; PARMinerV2.sol:54 _dexAP = dexAP; PARMinerV2.sol:55 _liquidateCallerReward = 200 ether; PARMinerV2.sol:60 _boostConfig = boostConfig;

SupplyMinerV2.sol

SupplyMinerV2.sol:20 _collateral = collateral;

BalancerV2LPOracle.sol

BalancerV2LPOracle.sol:39 vault = _vault; BalancerV2LPOracle.sol:40 poolId = _poolId; BalancerV2LPOracle.sol:44 decimals = _decimals; BalancerV2LPOracle.sol:45 description = _description; BalancerV2LPOracle.sol:46 pool = IBalancerPool(_pool); BalancerV2LPOracle.sol:47 oracleA = _oracleA; BalancerV2LPOracle.sol:48 oracleB = _oracleB;

GUniLPOracle.sol

GUniLPOracle.sol:39 decimals = _decimals; GUniLPOracle.sol:40 description = _description; GUniLPOracle.sol:41 pool = _pool; GUniLPOracle.sol:42 oracleA = _oracleA; GUniLPOracle.sol:43 oracleB = _oracleB;

SuperVaultFactory.sol

SuperVaultFactory.sol:20 base = _base;

TOOLS USED

Manual Analysis

MITIGATION

Hardcode these storage variables with their value instead of writing them during contract deployment with constructor parameters.

Custom Errors

IMPACT

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

PROOF OF CONCEPT

Instances include:

DexAddressProvider.sol

DexAddressProvider.sol:14: require(address(a) != address(0), "LM000"); DexAddressProvider.sol:38: require(_proxy != address(0), "LM000"); DexAddressProvider.sol:39: require(_router != address(0), "LM000");

InceptionVaultFactory.sol

InceptionVaultFactory.sol:47: require(adminInceptionVaultBase != address(0), "IV000"); InceptionVaultFactory.sol:48: require(inceptionVaultsCoreBase != address(0), "IV000"); InceptionVaultFactory.sol:49: require(inceptionVaultsDataProviderBase != address(0), "IV000"); InceptionVaultFactory.sol:50: require(address(addressProvider) != address(0), "IV000"); InceptionVaultFactory.sol:51: require(address(debtNotifier) != address(0), "IV000"); InceptionVaultFactory.sol:52: require(address(weth) != address(0), "IV000");; InceptionVaultFactory.sol:53: require(address(mimo) != address(0), "IV000"); InceptionVaultFactory.sol:74: require(address(_inceptionCollateral) != address(0), "IV000"); InceptionVaultFactory.sol:75: require(_inceptionVaultPriceFeed != address(0), "IV000"); InceptionVaultFactory.sol:85: require(address(_assetOracle) != address(0), "IV000"); InceptionVaultFactory.sol:89: require(address(_assetOracle) == address(0), "IV001"); InceptionVaultFactory.sol:130: require(_address != address(0), "IV000"); InceptionVaultFactory.sol:130: require(_priceFeedIds[_address] == 0, "IV002");

InceptionVaultFactory.sol

InceptionVaultFactory.sol:122 require(_amount > 0, "IV100"); InceptionVaultFactory.sol:138 require(_amount <= stablex.balanceOf(address(_adminInceptionVault)), "IV104"); InceptionVaultFactory.sol:160 require(isHealthy, "IV102"); InceptionVaultFactory.sol:204 require( !_a.liquidationManager().isHealthy(collateralValue, currentVaultDebt, _vaultConfig.liquidationRatio), "IV103" ); InceptionVaultFactory.sol:226 require(_a.stablex().balanceOf(address(_adminInceptionVault)) >= insuranceAmount, "IV104"); InceptionVaultFactory.sol:285 require(_amount <= v.collateralBalance, "IV101"); InceptionVaultFactory.sol:291 require( _a.liquidationManager().isHealthy( newCollateralValue, _inceptionVaultsData.vaults(_vaultId).baseDebt, _vaultConfig.minCollateralRatio ), "IV102" );

InceptionVaultDataProvider.sol

InceptionVaultDataProvider.sol:64: require(vaultExists(_vaultId), "IV105");

ChainlinkInceptionPriceFeed.sol

ChainlinkInceptionPriceFeed.sol:75: require(eurAnswer > 0, "EUR price data not valid"); ChainlinkInceptionPriceFeed.sol:76: require(block.timestamp - eurUpdatedAt < _PRICE_ORACLE_STALE_THRESHOLD, "EUR price data is stale"); ChainlinkInceptionPriceFeed.sol:79: require(answer > 0, "Price data not valid"); ChainlinkInceptionPriceFeed.sol:80: require(block.timestamp - assetUpdatedAt < _PRICE_ORACLE_STALE_THRESHOLD, "Price data is stale");

DemandMinerV2.sol

DemandMinerV2.sol:31: require(address(token) != address(0), "LM000"); DemandMinerV2.sol:32: require(address(token) != address(_addresses.mimo()), "LM001"); DemandMinerV2.sol:33: require(feeCollector != address(0), "LM000");

GenericMinerV2.sol

GenericMinerV2.sol:55: require(address(_addresses) != address(0), "LM000"); GenericMinerV2.sol:58: require(boostConfig.a >= 1 && boostConfig.d > 0 && boostConfig.maxBoost >= 1, "LM004"); GenericMinerV2.sol:70: require(newBoostConfig.a >= 1 && newBoostConfig.d > 0 && newBoostConfig.maxBoost >= 1, "LM004"); GenericMinerV2.sol:175: require(value > 0, "LM101"); GenericMinerV2.sol:195: require(value > 0, "LM101"); GenericMinerV2.sol:197: require(_userInfo.stake >= value, "LM102"); GenericMinerV2.sol:220: require(_a.mimo().transfer(_user, pendingMIMO), "LM100"); GenericMinerV2.sol:223: require(_par.transfer(_user, pendingPAR), "LM100"); GenericMinerV2.sol:331: require(multiplier >= 1e18 && multiplier <= _boostConfig.maxBoost, "LM103");

PARMinerV2.sol

PARMinerV2.sol:50: require(address(govAP) != address(0), "LM000"); PARMinerV2.sol:51: require(address(dexAP) != address(0), "LM000"); PARMinerV2.sol:52: require(boostConfig.a >= 1 && boostConfig.d > 0 && boostConfig.maxBoost >= 1, "LM004"); PARMinerV2.sol:71: require(boostConfig.a >= 1 && boostConfig.d > 0 && boostConfig.maxBoost >= 1, "LM004"); PARMinerV2.sol:128: require(_par.balanceOf(address(this)) > parBalanceBefore, "LM104");; PARMinerV2.sol:254: require(value > 0, "LM101"); PARMinerV2.sol:284: require(value > 0, "LM101"); PARMinerV2.sol:286: require(_userInfo.stake >= value, "LM102"); PARMinerV2.sol:320: require(_par.transfer(_user, pendingPAR), "LM100"); PARMinerV2.sol:323: require(_a.mimo().transfer(_user, pendingMIMO), "LM100"); PARMinerV2.sol:426: require(multiplier >= 1e18 && multiplier <= _boostConfig.maxBoost, "LM103");

SupplyMinerV2.sol

SupplyMinerV2.sol:50: require(address(collateral) != address(0), "LM000");

VotingMinerV2.sol

VotingMinerV2.sol:44: require(_a.mimo().transfer(_user, pendingMIMO), "LM100"); VotingMinerV2.sol:47: require(_par.transfer(_user, pendingPAR), "LM100");

BalancerV2LPOracle.sol

BalancerV2LPOracle.sol:35: require(address(_vault) != address(0), "C000"); BalancerV2LPOracle.sol:36: require(address(_oracleA) != address(0), "C000"); BalancerV2LPOracle.sol:37: require(address(_oracleB) != address(0), "C000"); BalancerV2LPOracle.sol:38: require(tokensNum == IBalancerVault.PoolSpecialization.TWO_TOKEN, "C001");

GUniLPOracle.sol

GUniLPOracle.sol:35: require(address(_pool) != address(0), "C000"); GUniLPOracle.sol:36: require(address(_oracleA) != address(0), "C000"); GUniLPOracle.sol:37: require(address(_oracleB) != address(0), "C000"); GUniLPOracle.sol:112: require(rA > 0 || rB > 0, "C100"); GUniLPOracle.sol:114: require(totalSupply >= 1e9, "C101");

SuperVault.sol

SuperVault.sol:56: require(address(_a) != address(0)); SuperVault.sol:57: require(address(_ga) != address(0)); SuperVault.sol:58: require(address(_lendingPool) != address(0)); SuperVault.sol:59: require(address(dexAP) != address(0)); SuperVault.sol:83: require(msg.sender == address(lendingPool), "SV002"); SuperVault.sol:109: require(token.balanceOf(address(this)) >= flashloanRepayAmount, "SV101"); SuperVault.sol:156: require(fromCollateral.balanceOf(address(this)) >= flashloanRepayAmount, "SV101"); SuperVault.sol:207: require(vaultCollateral.balanceOf(address(this)) >= flashloanRepayAmount, "SV101"); SuperVault.sol:233: require(IERC20(a.stablex()).transfer(msg.sender, IERC20(a.stablex()).balanceOf(address(this)))); SuperVault.sol:247: require(asset.transfer(msg.sender, amount)); SuperVault.sol:255: require(IERC20(a.stablex()).transfer(msg.sender, IERC20(a.stablex()).balanceOf(address(this)))); SuperVault.sol:264: require(token.transfer(msg.sender, token.balanceOf(address(this)))); SuperVault.sol:292: require(IERC20(a.stablex()).transfer(msg.sender, IERC20(a.stablex()).balanceOf(address(this)))); SuperVault.sol:313: require(IERC20(a.stablex()).transfer(msg.sender, IERC20(a.stablex()).balanceOf(address(this)))); SuperVault.sol:344: require(proxy != address(0) && router != address(0), "SV201"); SuperVault.sol:370: require(ga.mimo().transfer(msg.sender, ga.mimo().balanceOf(address(this))));

TOOLS USED

Manual Analysis

MITIGATION

Replace require and revert statements with custom errors.

For instance, in DexAddressProvider.sol:

Replace

require(_proxy_ != address(0), "LM000");

with

if (_proxy == address(0)) { revert ErrorNullAddress(_proxy); }

and define the custom error in the contract

error ErrorNullAddress(address _address);

Default value initialization

IMPACT

If a variable is not set/initialized, it is assumed to have the default value (0, false, 0x0 etc depending on the data type). Explicitly initializing it with its default value is an anti-pattern and wastes gas.

PROOF OF CONCEPT

Instances include:

InceptionVaultFactory.sol

InceptionVaultFactory.sol:218: uint256 insuranceAmount = 0;

TOOLS USED

Manual Analysis

MITIGATION

Remove explicit initialization for default values.

Event emitting of local variable

PROBLEM

When emitting an event, using a local variable instead of a storage variable saves gas.

PROOF OF CONCEPT

Instances include:

InceptionVaultFactory.sol

InceptionVaultFactory.sol:218: emit CumulativeRateUpdated(_timeElapsed, _cumulativeRate);

GenericMinerV2.sol

GenericMinerV2.sol:61: emit BoostConfigSet(_boostConfig); GenericMinerV2.sol:73: emit BoostConfigSet(_boostConfig);

PARMinerV2.sol

PARMinerV2.sol:74: emit BoostConfigSet(_boostConfig);

TOOLS USED

Manual Analysis

MITIGATION

When possible, emit the function parameter that is written in the storage variable instead of the storage variable itself to save gas. In cases where the storage variable is read multiple times in the function, it is recommended to cache it into memory, then passing these cached variable in the emit statement, as explained in the cache paragraph

Prefix increments

IMPACT

Prefix increments are cheaper than postfix increments.

PROOF OF CONCEPT

Instances include:

DexAddressProvider.sol

DexAddressProvider.sol:16: i++

AdminInceptionVault.sol

AdminInceptionVault.sol:108: i++

TOOLS USED

Manual Analysis

MITIGATION

change variable++ to ++variable.

Require instead of AND

IMPACT

Require statements including conditions with the && operator can be broken down in multiple require statements to save gas.

PROOF OF CONCEPT

Instances include:

GenericMinerV2.sol

GenericMinerV2.sol:58: require(boostConfig.a >= 1 && boostConfig.d > 0 && boostConfig.maxBoost >= 1, "LM004"); GenericMinerV2.sol:70: require(newBoostConfig.a >= 1 && newBoostConfig.d > 0 && newBoostConfig.maxBoost >= 1, "LM004"); GenericMinerV2.sol:331: require(multiplier >= 1e18 && multiplier <= _boostConfig.maxBoost, "LM103");

PARMinerV2.sol

PARMinerV2.sol:52: require(boostConfig.a >= 1 && boostConfig.d > 0 && boostConfig.maxBoost >= 1, "LM004"); PARMinerV2.sol:71: require(boostConfig.a >= 1 && boostConfig.d > 0 && boostConfig.maxBoost >= 1, "LM004"); PARMinerV2.sol:426: require(multiplier >= 1e18 && multiplier <= _boostConfig.maxBoost, "LM103");

SuperVault.sol

SuperVault.sol:344: require(proxy != address(0) && router != address(0), "SV201");

TOOLS USED

Manual Analysis

MITIGATION

Break down the single require statement in multiple require statements

e.g:

GenericMinerV2.sol:331: -require(multiplier >= 1e18 && multiplier <= _boostConfig.maxBoost, "LM103"); +require(multiplier >= 1e18) +require(multiplier <= _boostConfig.maxBoost, "LM103")

SafeMath not necessary everywhere

IMPACT

Since the version 0.8.0 update, overflow and underflow checks are performed automatically in Solidity. It is not necessary and costs additional gas to use the external library SafeMath to perform additions and subtractions

PROOF OF CONCEPT

Instances include:

InceptionVaultsCore.sol

InceptionVaultsCore.sol:217: uint256 collateralValueToReceive = _amount.add(_amount.wadMul(_vaultConfig.liquidationBonus)); InceptionVaultsCore.sol:222: uint256 discountedCollateralValue = collateralValue.wadDiv(_vaultConfig.liquidationBonus.add(WadRayMath.wad())); InceptionVaultsCore.sol:232: _reduceVaultDebt(_vaultId, repayAmount.add(insuranceAmount));

TOOLS USED

Manual Analysis

MITIGATION

Do not use this library for additions and subtractions

Tight Variable Packing

PROBLEM

Solidity contracts have contiguous 32 bytes (256 bits) slots used in storage. By arranging the variables, it is possible to minimize the number of slots used within a contract's storage and therefore reduce deployment costs.

string type variables are each of 20 bytes size (way less than 32 bytes). However, they here take up a whole 32 bytes slot (they are contiguous).

As bool type variables are of size 1 byte, there's a slot here that can get saved by moving them.

PROOF OF CONCEPT

Instances include:

IInceptionVaultFactory.sol

IInceptionVaultFactory.sol:16: struct InceptionVault { address owner; IAdminInceptionVault adminInceptionVault; IInceptionVaultsCore inceptionVaultsCore; IInceptionVaultsDataProvider inceptionVaultsDataProvider; IInceptionVaultPriceFeed inceptionVaultPriceFeed; bool isCustomPriceFeed; }

TOOLS USED

Manual Analysis

MITIGATION

Place isCustomPriceFeed after owner to save one storage slot

address owner; +bool isCustomPriceFeed; IAdminInceptionVault adminInceptionVault; IInceptionVaultsCore inceptionVaultsCore; IInceptionVaultsDataProvider inceptionVaultsDataProvider; IInceptionVaultPriceFeed inceptionVaultPriceFeed;

Unchecked arithmetic

IMPACT

The default "checked" behavior costs more gas when adding/diving/multiplying, because under-the-hood those checks are implemented as a series of opcodes that, prior to performing the actual arithmetic, check for under/overflow and revert if it is detected.

if it can statically be determined there is no possible way for your arithmetic to under/overflow (such as a condition in an if statement), surrounding the arithmetic in an unchecked block will save gas

PROOF OF CONCEPT

Instances include:

SuperVault.sol

SuperVault.sol:110: token.balanceOf(address(this)) is greater than flashloanRepayAmount (see condition one line above), underflow check unnecessary

TOOLS USED

Manual Analysis

MITIGATION

Place the arithmetic operations in an unchecked block

#0 - m19

2022-05-08T05:38:32Z

We appreciate this thorough report on how to optimize gas.

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter