Platform: Code4rena
Start Date: 21/10/2021
Pot Size: $80,000 ETH
Total HM: 28
Participants: 15
Period: 7 days
Judge: ghoulsol
Total Solo HM: 19
Id: 42
League: ETH
Rank: 8/15
Findings: 3
Award: $4,572.30
🌟 Selected for report: 8
🚀 Solo Findings: 0
pauliax
function claimRewardAsMochi in ReferralFeePoolV0 will produce a runtime exception because the length of the path is 2 but it tries to assign 3 entries: address[] memory path = new address; path[0] = address(usdm); path[1] = uniswapRouter.WETH(); path[2] = address(engine.mochi()); So users will not be able to claim their rewards.
#0 - r2moon
2021-10-29T12:56:32Z
duplicated with https://github.com/code-423n4/2021-10-mochi-findings/issues/97
🌟 Selected for report: pauliax
pauliax
Using the unchecked keyword to avoid redundant arithmetic underflow/overflow checks to save gas when an underflow/overflow cannot happen. We can apply the unchecked keyword in the following lines of code since there are checks before that ensure the arithmetic operations would not cause an integer underflow or overflow.
numerator: auction.startedAt + DURATION > block.number ? auction.startedAt + DURATION - block.number : 0
or
return type(uint256).max - totalSupply();
Consider using 'unchecked' where it is safe to do so.
#0 - r2moon
2021-10-29T12:49:03Z
duplicated with https://github.com/code-423n4/2021-10-mochi-findings/issues/75
#1 - ghoul-sol
2021-11-02T15:06:26Z
I don't think this is a duplicate
pauliax
Cache duplicate access of storage variables when they do not change between these interactions. For example, here: usdm.approve(address(uniswapRouter), reward[msg.sender]); uniswapRouter.swapExactTokensForTokens( reward[msg.sender], ... );
this duplicate storage read can be eliminated: uint userReward = reward[msg.sender]; usdm.approve(address(uniswapRouter), userReward); uniswapRouter.swapExactTokensForTokens( userReward, ... );
There are places where these duplicate accesses are all over the place so I expect huge gas costs there, e.g. modifier checkClaimable and function vest access vesting[recipient] 5 times, function accrueDebt accesses details mapping 8 times, and so on.
Please revisit such places and consider caching values that do not change to improve gas usage.
#0 - r2moon
2021-10-29T12:48:23Z
🌟 Selected for report: pauliax
pauliax
The check could be inclusive <= here to save some gas when debts = _amount (also 'unchecked' keyword can be used):
if (debts < _amount) { // safe gaurd to some underflows debts = 0; } else { debts -= _amount; }
#0 - r2moon
2021-10-29T12:45:30Z
duplicated with https://github.com/code-423n4/2021-10-mochi-findings/issues/75
#1 - ghoul-sol
2021-11-02T15:05:25Z
I don't think it's a duplicate
🌟 Selected for report: pauliax
0.0768 ETH - $319.56
pauliax
This not only loses some precision (cuz of multiplication and division) but also consumes more gas: // send Mochi to vMochi Vault mochi.transfer( address(engine.vMochi()), (mochiBalance * vMochiRatio) / 1e18 ); // send Mochi to veCRV Holders mochi.transfer( crvVoterRewardPool, (mochiBalance * (1e18 - vMochiRatio)) / 1e18 );
Proposed improvement: // send Mochi to vMochi Vault uint toVault = (mochiBalance * vMochiRatio) / 1e18; mochi.transfer( address(engine.vMochi()), toVault ); // send Mochi to veCRV Holders mochi.transfer( crvVoterRewardPool, mochiBalance - toVault; );
This way you the whole mochiBalance will be transferred and it will cost less to do that as fewer math operations are performed.
🌟 Selected for report: pauliax
pauliax
Cache results of duplicate external calls when the results between them do not change. E.g. here engine.mochi() is called 2 times: path[2] = address(engine.mochi()); ... engine.mochi().transfer( msg.sender, engine.mochi().balanceOf(address(this)) );
can be refactored to: IMochi mochi = engine.mochi(); path[2] = address(mochi); ... mochi.transfer( msg.sender, mochi.balanceOf(address(this)) );
🌟 Selected for report: pauliax
0.0768 ETH - $319.56
pauliax
contract USDM does not need to import IERC3156FlashLender again as it was already imported in IUSDM. import "../interfaces/IERC3156FlashLender.sol";
contract DutchAuctionLiquidator makes no use of these imports: import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; import "@mochifi/library/contracts/BeaconProxyDeployer.sol";
Consider reviewing all the unused imports and removing them to reduce the size of the contract and thus save some deployment gas.
pauliax
There are some validations that could be enforced by smart contracts, e.g.:
function vest in VestedRewardPool should require that _recipient is not empty to prevent accidentally locked funds.
functions changevMochiRatio and changeTreasuryRatio in FeePoolV0 should require that _ratio is <= 1e18 (100%).
function addReward in NoMochiReferralFeePool and ReferralFeePoolV0 should require that _recipient is not empty to prevent accidentally locked funds.
Consider enforcing proposed validations. Also, review other functions that may contain such missing validations.
#0 - r2moon
2021-10-29T12:34:44Z
duplicated with https://github.com/code-423n4/2021-10-mochi-findings/issues/106
🌟 Selected for report: pauliax
0.0768 ETH - $319.56
pauliax
Duplicate math operations can be cached to reduce gas usage, e.g. no need to calculate updatedFee / 2 twice here: operationShare += updatedFee / 2; veCRVShare += updatedFee / 2;
or here: mochi.transfer(msg.sender, _amount / 2); mochi.transfer(address(vMochi), _amount / 2);
Eliminate useless duplicate calculations.
🌟 Selected for report: pauliax
0.0768 ETH - $319.56
pauliax
function flashLoan does not necesseraly need this check: require(_token == address(this), "!supported"); as it will be later checked again in function flashFee.
Consider removing it to reduce gas usage.
🌟 Selected for report: pauliax
0.0768 ETH - $319.56
pauliax
Gas efficiency can be achieved by tightly packing the struct. Struct variables are stored in 32 bytes each so you can group smaller types to occupy less storage. For example, startedAt or boughtAt in Auction struct hold block.number so realistically this does not need uint256 and you can consider storing it in lower type. You can read more here: https://fravoll.github.io/solidity-patterns/tight_variable_packing.html or in the official documentation: https://docs.soliditylang.org/en/v0.4.21/miscellaneous.html
Search for an optimal size and order of structs to reduce gas usage.