Omniscia Morpho Labs Audit

SupplyHarvestVault Code Style Findings

SupplyHarvestVault Code Style Findings

SHV-01C: Ineffectual Usage of Safe Arithmetics

Description:

The linked mathematical operation is guaranteed to be performed safely by surrounding conditionals evaluated in either require checks or if-else constructs.

Example:

src/aave-v3/SupplyHarvestVault.sol
145rewardsFee = rewardsAmount.percentMul(harvestingFeeMem);
146rewardsFees[i] = rewardsFee;
147rewardsAmount -= rewardsFee;

Recommendation:

Given that safe arithmetics are toggled on by default in pragma versions of 0.8.X, we advise the linked statement to be wrapped in an unchecked code block thereby optimizing its execution cost.

Alleviation:

The Morpho team has wrapped the referenced statement as well as the accumulation of fees in an unchecked code block. We assume that the Morpho team has validated the addition accumulation cannot overflow and as such mark this exhibit as addressed.

SHV-02C: Inefficient Reward Fee Supply Workflow

Description:

The reward fee is transferred on each iteration for the exact same asset in the current implementation which is inefficient.

Example:

src/aave-v3/SupplyHarvestVault.sol
102function harvest()
103 external
104 returns (
105 address[] memory rewardTokens,
106 uint256[] memory rewardsAmounts,
107 uint256[] memory rewardsFees
108 )
109{
110 address poolTokenMem = poolToken;
111
112 {
113 address[] memory poolTokens = new address[](1);
114 poolTokens[0] = poolTokenMem;
115 (rewardTokens, rewardsAmounts) = morpho.claimRewards(poolTokens, false);
116 }
117
118 address assetMem = asset();
119 ISwapper swapperMem = swapper;
120 uint16 harvestingFeeMem = harvestingFee;
121 uint256 nbRewardTokens = rewardTokens.length;
122 uint256 toSupply;
123 rewardsFees = new uint256[](nbRewardTokens);
124
125 for (uint256 i; i < nbRewardTokens; ) {
126 uint256 rewardsAmount = rewardsAmounts[i];
127
128 if (rewardsAmount > 0) {
129 ERC20 rewardToken = ERC20(rewardTokens[i]);
130
131 // Note: Uniswap pairs are considered to have enough market depth.
132 // The amount swapped is considered low enough to avoid relying on any oracle.
133 if (assetMem != address(rewardToken)) {
134 rewardToken.safeTransfer(address(swapperMem), rewardsAmount);
135 rewardsAmount = swapperMem.executeSwap(
136 address(rewardToken),
137 rewardsAmount,
138 assetMem,
139 address(this)
140 );
141 }
142
143 uint256 rewardsFee;
144 if (harvestingFeeMem > 0) {
145 rewardsFee = rewardsAmount.percentMul(harvestingFeeMem);
146 rewardsFees[i] = rewardsFee;
147 rewardsAmount -= rewardsFee;
148 ERC20(assetMem).safeTransfer(msg.sender, rewardsFee);
149 }
150
151 rewardsAmounts[i] = rewardsAmount;
152 toSupply += rewardsAmount;
153
154 emit Harvested(msg.sender, address(rewardToken), rewardsAmount, rewardsFee);
155 }
156
157 unchecked {
158 ++i;
159 }
160 }
161
162 morpho.supply(poolTokenMem, address(this), toSupply);
163}

Recommendation:

We advise the total reward fee to be accumulated similarly to the toSupply variable and a single transfer to be performed at the end of the function's execution.

Alleviation:

The fees are now accumulated instead and transferred in a single action at the end of the function's execution thereby optimizing the gas cost of the function significantly.