Omniscia Euler Finance Audit

RiskManager Manual Review Findings

RiskManager Manual Review Findings

RMR-01M: Overprotective Security Mechanism

Description:

The RiskManager::checkVaultStatus function which is invoked as part of the Ethereum Vault Connector's execution flow after all interactions with the vault have concluded will ensure that the borrowCap has not been breached.

A problem that arises from the current security mechanism is the fact that the entire vault will be inoperable if the total borrows have naturally exceeded the permitted borrowCap. For example, a user with collateral but no debt will not be able to withdraw their collateral which is a restriction that should not be imposed.

Impact:

In the extreme circumstance that the borrowCap has been reached due to natural interest accrual, the vault will be inoperable in functions that do not otherwise deal with borrow balances when it shouldn't be so.

Example:

src/EVault/modules/RiskManager.sol
84/// @inheritdoc IRiskManager
85/// @dev See comment about re-entrancy for `checkAccountStatus`
86function checkVaultStatus() public virtual reentrantOK onlyEVCChecks returns (bytes4 magicValue) {
87 // Use the updating variant to make sure interest is accrued in storage before the interest rate update
88 VaultCache memory vaultCache = updateVault();
89 uint256 newInterestRate = computeInterestRate(vaultCache);
90
91 logVaultStatus(vaultCache, newInterestRate);
92
93 // We use the snapshot to check if the borrows or supply grew, and if so then we check the borrow and supply caps.
94 // If snapshot is initialized, then caps are configured.
95 // If caps are set in the middle of a batch, then snapshots represent the state of the vault at that time.
96 if (vaultCache.snapshotInitialized) {
97 vaultStorage.snapshotInitialized = vaultCache.snapshotInitialized = false;
98
99 Assets snapshotCash = snapshot.cash;
100 Assets snapshotBorrows = snapshot.borrows;
101
102 uint256 prevBorrows = snapshotBorrows.toUint();
103 uint256 borrows = vaultCache.totalBorrows.toAssetsUp().toUint();
104
105 if (borrows > vaultCache.borrowCap && borrows > prevBorrows) revert E_BorrowCapExceeded();
106
107 uint256 prevSupply = snapshotCash.toUint() + prevBorrows;
108 uint256 supply = totalAssetsInternal(vaultCache);
109
110 if (supply > vaultCache.supplyCap && supply > prevSupply) revert E_SupplyCapExceeded();
111
112 snapshot.reset();
113 }
114
115 magicValue = IEVCVault.checkVaultStatus.selector;
116}

Recommendation:

We advise the borrowCap limitation specifically to be applied solely for operations that actively increase the cap rather than for natural interest rate increases, ensuring that users can still interact with the vault albeit at a limited capacity when the borrowCap has been breached.

Alleviation (fb2dd77a6ff9b7f710edb48e7eb5437e0db4fc1a):

The Euler Finance team evaluated this exhibit and concluded that it is incorrect as the snapshot being evaluated will have reflected any borrow interest accrual in the prevBorrows variable, effectively causing the if clause to not trigger if the borrows were not increased as part of the overall user interaction.

As such, a user will continue to be able to withdraw even when the borrow has exceeded the cap rendering this exhibit invalid.