Omniscia Euler Finance Audit

YieldAggregatorVault Manual Review Findings

YieldAggregatorVault Manual Review Findings

YAV-01M: Capital-Inefficient Limitation Implementations of Allocation System

Description:

The YieldAggregatorVaultModule::_rebalance function implementation is presently inefficient when dealing with strategy cap and deposit limitations as they are applied after the allocation point proportion has been evaluated.

This results in underutilized capital, as any capital that was meant for a strategy based on its allocation points but cannot be deposited to it due to its cap and / or deposit limit will remain at rest.

Impact:

This finding represents a capital efficiency recommendation rather than a vulnerability and has thus been designated with an unknown severity rating.

Example:

src/module/YieldAggregatorVault.sol
703uint256 totalAllocationPointsCache = $.totalAllocationPoints;
704uint256 totalAssetsAllocatableCache = _totalAssetsAllocatable();
705uint256 targetAllocation =
706 totalAssetsAllocatableCache * strategyData.allocationPoints / totalAllocationPointsCache;
707
708uint120 capAmount = uint120(strategyData.cap.resolve());
709// capAmount will be max uint256 if no cap is set
710if (targetAllocation > capAmount) targetAllocation = capAmount;

Recommendation:

We advise this approach to be potentially revised, and unutilized capital to optionally carry over to another strategy whereby the relevant caps might not have been achieved.

While this issue can be circumvented with a reconfiguration of the proportions of each strategy, it would remain inefficient as each new deposit and withdrawal would require a re-adjustment of the allocations if one strategy has a higher practical cap limit than the other.

Alleviation:

The Euler Finance team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase.

YAV-02M: Unconditional Disbursement of Performance Fee

Description:

The current YieldAggregatorVaultModule::_accruePerformanceFee function will attempt to mint shares to the fee recipient regardless of whether the maximum share limit has been achieved by the vault.

Such a state will cause the vault to enter a locked state whereby withdrawals become impossible unless unblocked by administrative action that would set the performance fee / recipient to 0.

Impact:

The likelihood of this event is considered low, and due to its capability to be unblocked via administrative action it has been considered an informational vulnerability.

Example:

src/module/YieldAggregatorVault.sol
666function _accruePerformanceFee(uint256 _yield) private {
667 YieldAggregatorStorage storage $ = Storage._getYieldAggregatorStorage();
668
669 address cachedFeeRecipient = $.feeRecipient;
670 uint96 cachedPerformanceFee = $.performanceFee;
671
672 if (cachedFeeRecipient == address(0) || cachedPerformanceFee == 0) return;
673
674 (uint256 feeAssets, uint256 feeShares) = _applyPerformanceFee(_yield, cachedPerformanceFee);
675
676 if (feeShares != 0) {
677 // Move feeAssets from gulpable amount to totalAssetsDeposited to not dilute other depositors.
678 $.totalAssetsDeposited += feeAssets;
679
680 _mint(cachedFeeRecipient, feeShares);
681 }
682
683 emit Events.AccruePerformanceFee(cachedFeeRecipient, _yield, feeShares);
684}

Recommendation:

We advise the performance fee accrual system to mint the relevant feeShares only when it is possible to do so, ignoring the accrued fees otherwise.

Alleviation:

The Euler Finance team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase.

YAV-03M: Incorrect Implementations of EIP-4626 Maximum Restrictions

Description:

The view related functions around maximum mint and deposit amounts as per the EIP-4626 standard are presently implemented incorrectly as they indicate no limit is set.

These indications directly contradict the YieldAggregatorVaultModule::_update function implementation which imposes a cap on mint operations based on the ERC20VotesUpgradeable dependency.

Impact:

The YieldAggregatorVaultModule implements deposit limit related functions of the EIP-4626 standard incorrectly, indicating that no limit exists whilst one is enforced during every share mint operation.

Example:

src/module/YieldAggregatorVault.sol
533function _update(address _from, address _to, uint256 _value)
534 internal
535 override (ERC20VotesUpgradeable, ERC20Upgradeable)
536{
537 /// call `_update()` on ERC20Upgradeable
538 ERC20Upgradeable._update(_from, _to, _value);
539
540 /// ERC20VotesUpgradeable `_update()`
541 if (_from == address(0)) {
542 uint256 supply = _totalSupply();
543 uint256 cap = _maxSupply();
544 require(supply <= cap, Errors.ERC20ExceededSafeSupply(supply, cap));
545 }

Recommendation:

We advise the relevant functions to yield proper limitations for would-be depositors to the YieldAggregatorVaultModule, ensuring proper conformity to the EIP-4626 standard.

Alleviation:

The EIP-4626 maximum restrictions have been corrected per our recommendation with a slight deviation, yielding the maximum value possible instead of 0 whenever an attempt to measure the maximum precisely reverts.

We consider that the updated measurement mechanism is in-line with the EIP-4626 standard and that the revert based deviation can be safely acknowledged, rendering this exhibit to be ultimately alleviated.

YAV-04M: Improper Evaluation of Vault Assets

Description:

The current YieldAggregatorVaultModule::_totalAssets function will yield all the assets that the system has presently deposited as well as any pending interest that has yet to be reflected.

The current asset evaluation mechanism is insecure as it does not account all the pending interest of the vault which is readily available to it, thereby permitting depositors to consistently undervalue the vault's position.

Impact:

The present YieldAggregatorVaultModule::_totalAssets function implementation will not take into account the full pending interest distributed via the vault's gulp mechanism, thereby permitting new deposits to capture the pending interest incorrectly.

We consider the likelihood of this event to be continual (i.e. high) and the impact to be on average low with certain high-yield cases causing significant damage to existing depositors.

Example:

src/module/YieldAggregatorVault.sol
771function _totalAssets() private view returns (uint256) {
772 YieldAggregatorStorage storage $ = Storage._getYieldAggregatorStorage();
773
774 return $.totalAssetsDeposited + _interestAccruedFromCache($.interestLeft);
775}

Recommendation:

We advise all inflow-related interactions (including performance fee accruals) to evaluate the total assets inclusive of the full pending interest and all outflow-related operations to continue with the same mechanism as presently implemented, ensuring that deposits consistently use the true value of the vault whilst withdrawals use the available value of the vault.

Alleviation:

The Euler Finance team evaluated this exhibit and while they confirm its validity, they have opted to acknowledge it due to time constraints.

In detail, they envision the arbitraged profit in the scenario described to be minimal and gradually smeared therefore rendering it a risky attack to execute whilst also acting as a natural deposit incentivization mechanism.

Based on the fact that the Euler Finance team is fully aware of the exhibit's ramifications and considers the present economic incentives introduced by it desirable, we consider this exhibit to be safely acknowledged.

YAV-05M: Improper Restriction of Harvests

Description:

As outlined in the technical documentation of the Euler Earn system, an edge case exists whereby loss socialization may be front-run and thus allow a contributor of the vault to avoid its effect.

We do not believe that such a scenario should be permitted as EIP-4626 vaults are expected to fluctuate in value and the current system permits any negative fluctuations to be arbitraged, effectively permitting users to interact with vaults in a risk-free manner.

Impact:

The current harvest cooldown system will continue to be in effect even if a negative yield is meant to be harvested, permitting depositors to front-run the negative yield's realization and affecting any user who did not withdraw in time.

While this vulnerability is known to the Euler Finance team, we consider it to be a non-acknowledgeable one and its impact was assessed as medium based on the combination of a medium likelihood (negative fluctuation during an in-cooldown period) and medium impact (avoidance of loss socialization by directly affecting other users' deposits).

Example:

src/module/YieldAggregatorVault.sol
585function _harvest(bool _isHarvestCoolDownCheckOn, bool _isOnlyCashReserveWithdraw) private returns (bool) {
586 YieldAggregatorStorage storage $ = Storage._getYieldAggregatorStorage();
587
588 if (
589 (_isHarvestCoolDownCheckOn && ($.lastHarvestTimestamp + Constants.HARVEST_COOLDOWN >= block.timestamp))
590 && _isOnlyCashReserveWithdraw
591 ) {
592 return false;
593 }

Recommendation:

After discussing with the Euler Finance team, we concluded that the optimal approach to addressing this exhibit would be to introduce the strict evaluation of harvests optionally via a configurational flag of the YieldAggregator.

Specifically, a YieldAggregator deployment should be able to be configured to harvest all strategies if a negative yield event occurs ignoring the harvest cooldown.

Alleviation:

After extensive discussions with the Euler Finance team, we concluded that an acknowledgement of the exhibit is appropriate given that the vault can already be configured to execute synchronization operations on each interaction and this configuration can be selectively applied on chains / vaults that the Euler Finance team considers it imperative to do so.