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: 32/69
Findings: 2
Award: $118.83
🌟 Selected for report: 0
🚀 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
69.6581 USDC - $69.66
The pragma version used are:
pragma solidity 0.8.10; pragma solidity ^0.8.4; pragma solidity >=0.8.4;
Note that mixing pragma are not recommended.
The minimum required version must be 0.8.15; otherwise, contracts will be affected by the following important bug fixes:
abi.encodeCall
in place of fixed bytes arguments.calldatasize()
in all cases.bytes
arrays.Apart from these, there are several minor bug fixes and improvements.
It's possible to lose the ownership under specific circumstances.
Because an human error it's possible to set a new invalid owner. When you want to change the owner's address it's better to propose a new owner, and then accept this ownership with the new wallet.
Affected source code:
The execute
method of the MIMOProxy
contract contains the following condition to prevent a malicious call from altering the original owner.
if (owner_ != owner) { revert CustomErrors.OWNER_CHANGED(owner_, owner); }
However, via the permissions system, it is easily bypassed by adding an execute permission with setPermission
and calling it later with the desired selector.
if (owner != msg.sender) { bytes4 selector; assembly { selector := calldataload(data.offset) } if (!_permissions[msg.sender][target][selector]) { revert CustomErrors.EXECUTION_NOT_AUTHORIZED(owner, msg.sender, target, selector); } }
So the security of the proxy can be compromised in the same way.
Affected source code:
The following methods have a lack checks if the received argument is an address, it's good practice in order to reduce human error to check that the address specified in the constructor or initialize is different than address(0)
.
Affected source code:
The following method names are not descriptive, consider changing the name.
function a() external view returns (IAddressProvider);
:
The method _aggregatorSwap
does not returns the output of the call, it could be interesting in the future to have this information, for debugging or for other purposes.
function _aggregatorSwap( IERC20 token, uint256 amount, SwapData calldata swapData - ) internal { + ) internal returns (bytes memory response){ (address proxy, address router) = dexAP.getDex(swapData.dexIndex); require(proxy != address(0), Errors.INVALID_AGGREGATOR); require(router != address(0), Errors.INVALID_AGGREGATOR); token.safeIncreaseAllowance(proxy, amount); - (bool success, bytes memory response) = router.call(swapData.dexTxData); + (bool success, response) = router.call(swapData.dexTxData);
Affected source code:
🌟 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
index
TransferOwnership
is not an event that need to be filtered, These indexes can be removed:
immutable
It's possible to avoid storage access a save gas using immutable
keyword for the following variables:
It's also better to remove the initial values, because they will be set during the constructor.
Affected source code:
++i
costs less gas compared to i++
or i += 1
++i
costs less gas compared to i++
or i += 1
for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration). This statement is true even with the optimizer enabled.
i++
increments i
and returns the initial value of i
. Which means:
uint i = 1; i++; // == 1 but i == 2
But ++i
returns the actual incremented value:
uint i = 1; ++i; // == 2 and i == 2 too, so no need for a temporary variable
In the first case, the compiler has to create a temporary variable (when used) for returning 1
instead of 2
I suggest using ++i
instead of i++
to increment the value of an uint variable. Same thing for --i
and i--
Affected source code:
It's cheaper to store the length of the array inside a local variable and iterate over it.
Affected source code:
If a variable is not set/initialized, the default value is assumed (0, false
, 0x0 ... depending on the data type). You are simply wasting gas if you directly initialize it with its default value.
Affected source code:
Some conditions can be removed to remove gas because they are always met or it is impossible for them to do so.
function deployFor(address owner) public override returns (IMIMOProxy proxy) { IMIMOProxy currentProxy = _currentProxies[owner]; // Do not deploy if the proxy already exists and the owner is the same. - if (address(currentProxy) != address(0) && currentProxy.owner() == owner) { + if (address(currentProxy) != address(0)) { revert CustomErrors.PROXY_ALREADY_EXISTS(owner); } // Deploy the proxy via the factory. proxy = factory.deployFor(owner); // Set or override the current proxy for the owner. _currentProxies[owner] = IMIMOProxy(proxy); }
Affected source code:
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Source Custom Errors in Solidity:
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.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
Reduce the size of error messages (Long revert Strings)
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition is met.
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
In this case, the statements are short, so it is recommended to use custom errors.
Affected source code: