Omniscia Moby Audit

FeeDistributor Manual Review Findings

FeeDistributor Manual Review Findings

FDR-01M: Inaccurate Distribution Mechanism

Description:

The RewardDistributor implementation will employ a reward-per-second mechanism that represents a truncated token value as the rewards distributed will be divided by the distributionPeriod.

Impact:

Amounts that are less than the distributionPeriod will slowly accumulate within the reward distributors as they will not be properly dispersed in the period they are meant to.

Example:

contracts/peripherals/FeeDistributor.sol
143function distributeOLPRewards() external onlyKeeper {
144 require(block.timestamp - lastOlpRewardsDistribution >= distributionPeriod, "not ready");
145
146 IERC20(weth).safeTransfer(sRewardDistributor, pendingOLPRewards[sRewardDistributor]);
147 IERC20(weth).safeTransfer(mRewardDistributor, pendingOLPRewards[mRewardDistributor]);
148 IERC20(weth).safeTransfer(lRewardDistributor, pendingOLPRewards[lRewardDistributor]);
149
150 uint256 sTokensPerInterval = pendingOLPRewards[sRewardDistributor] / distributionPeriod;
151 uint256 mTokensPerInterval = pendingOLPRewards[mRewardDistributor] / distributionPeriod;
152 uint256 lTokensPerInterval = pendingOLPRewards[lRewardDistributor] / distributionPeriod;
153
154 IRewardDistributor(sRewardDistributor).setTokensPerInterval(sTokensPerInterval);
155 IRewardDistributor(mRewardDistributor).setTokensPerInterval(mTokensPerInterval);
156 IRewardDistributor(lRewardDistributor).setTokensPerInterval(lTokensPerInterval);
157
158 pendingOLPRewards[sRewardDistributor] = 0;
159 pendingOLPRewards[mRewardDistributor] = 0;
160 pendingOLPRewards[lRewardDistributor] = 0;
161
162 lastOlpRewardsDistribution = block.timestamp;
163}

Recommendation:

We advise the code to either reduce each pendingOLPRewards entry by the value of xTokensPerInterval * distributionPeriod, or to transfer a value equal to xTokensPerInterval * distributionPeriod towards each xRewardDistributor.

Alleviation (b02fae335f62cc1f5f4236fb4d982ad16a32bd26):

The code was updated per our latter recommendation, evaluating the wholly divisible S/M/L amount that can be transferred to each reward distributor and utilizing it for subtracting their pendingOLPRewards entry.

FDR-02M: Unfair Fee Swaps

Description:

The FeeDistributor::distributeFee function will perform three separate swaps of the same trading pair independently, thereby causing the last swap to trade at an "unfair" disadvantage due to the preceding swaps' lowering the price of the AMM pair.

Impact:

The fees accumulated for the lVault (long position vault) will be less than the sVault (short position vault) due to an inadvertently unfair swap order.

Example:

contracts/peripherals/FeeDistributor.sol
101sVaultOutcome += _swap(vaults[0], _path, feeFromSVault);
102mVaultOutcome += _swap(vaults[1], _path, feeFromMVault);
103lVaultOutcome += _swap(vaults[2], _path, feeFromLVault);

Recommendation:

We advise a single swap to be performed and split among the sVaultOutcome, mVaultOutcome, and lVaultOutcome values as the mechanism will presently acquire more fees for the sVault than the lVault even for the same token amount.

Alleviation (b02fae335f62cc1f5f4236fb4d982ad16a32bd26):

The Moby team clarified that three independent swaps are performed to ensure that fees are captured independently per vault type, and we consider this approach to be sound.

As such, we consider the original exhibit nullified as it details desirable behaviour.