Closed sandybradley closed 1 year ago
We could implement something like the following, however I recall being informed that the initial share vault will be a multisig. With this in mind, we could not use the following logic to get the balance of the protocol fees and rewards unless we use SafeModules or something similar.
function finalizeUpdateMevEthShareVault() external onlyAdmin {
// --snip--
(uint128 fees, uint128 rewards) = MevEthShareVault(payable(mevEthShareVault)).protocolBalance();
if (fees != 0 || rewards != 0) {
revert MevEthErrors.NonZeroVaultBalance();
}
// --snip--
}
Is the plan to still use a multisig as the initial share vault? If so, what are your thoughts on how to approach this? @ControlCplusControlV @sandybradley
@0xKitsune that is still the plan, but we could always default to the .balance
of the multisig if the call fails using a sort of try catch maybe?
Second remediation point is covered in https://github.com/manifoldfinance/mevETH2/pull/157 where grantRewards
is not restricted to only current share-vault.
@0xKitsune that is still the plan, but we could always default to the
.balance
of the multisig if the call fails using a sort of try catch maybe?
Issue with using balance
for multisig is that its assuming all the eth balance is from staking rewards. Surely there would at least be some excess for paying gas.
After talking with @ControlCplusControlV I opened #165 as a first pass at this.
With the PRed approach, the function to finalize the new MevEthShareVault
takes a bool
signifying if the previous share vault is a multisig or not.
In the case that the previous share vault is not a mutisig, it will require that the protocol rewards and balance are 0 before upgrading the module. If the previous vault is a multisig, then this condition is not checked and the multisig is responsible for forwarding the necessary balances.
As mentioned before, when the share vault is a multisig, we could check that the balance of the address is 0, which would require the multisig to migrate fees to another address upon upgrading, but it would also ensure that funds have been migrated before finalization.
@sandybradley @ControlCplusControlV, let me know your thoughts and we can iterate from here.
GRANTING REMAINING REWARDS AND FEES SHOULD BE ENFORCED BY CODE WHEN UPDATING
SEVERITY: High
PATH: MevEth.sol:finalizeUpdateMevEthShareVault():L253-274
https://github.com/manifoldfinance/mevETH2/blob/63edde66d91c263b919fe9c21e128a382219880e/src/MevEth.sol#L253-L274
DESCRIPTION
The function
finalizeUpdateMevEthShareVault
is used to update the share vault contract. In the function, when updatingmevEthShareVault = pendingMevEthShareVault
on line 269,there is no check to see if
protocolBalance.rewards
andprotocolBalance.fees
equal zero.If the new
mevEthShareVault
overwrites these variables, there will be a permanent imbalance when the operators trackingRewardPayment
events calculateprotocolFeesOwed
, leading to protocol malfunction and potential loss of user funds.In addition, granting any remaining rewards from the existing share vault as suggested in MevEth.sol line 264 is not enough. Consider the following scenarios:
Scenario 1:
commitUpdateMevEthShareVault()
is calledaddress(this).balance = 0
,protocolBalance.rewards = 0
,protocolBalance.fees = 0
.receive
is called (an arbitrary number of times) andRewardPayment
is emitted, nowaddress(this).balance > 0
, rewards and fees are still zero.mevEthShareVault
update is finalised viafinalizeUpdateMevEthShareVault()
address(this).balance = 0
, however operators are calculatingprotocolFeesOwed
based on lastRewardPayment
emitted.logRewards
will always revert on line 102 becauseprotocolFeesOwed > uint128(rewardsEarned)
. Eventually enough payments will accumulate andlogRewards
won’t revert, however as soon as that happens it is expected that operators will callpayRewards()
, quickly putting the contract in debt again.Scenario 2:
In the case rewards and fees were not granted before the update.
REMEDIATION
finalizeUpdateMevEthShareVault()
to ensure rewards and fees equal zero before settingmevEthShareVault = pendingMevEthShareVault