Haythem Sellami & Mick de Graaf.
- Strategy
- Deposits & Withdraws
- Rebalance
- Harvest
- Loss Deduction & Socialisation
- Gulping & Smearing Period
The goal of implementing Euler Earn is to provide to provide a passive yield for users, and to manage the risk on their behalf. The source of yield are the strategies that the Euler Earn vault allocates funds into.
A strategy is any ERC4626 compliant contract, can be added and removed by the address that has the STRATEGY_OPERATOR
role.
Removing a strategy that has an allocated amount greater than 0 is not possible, therefore an adjustment in its allocation points to 0, and then a rebalance trigger is required to remove such a strategy.
The strategy with address(0)
is reserved for the CASH_RESERVE
strategy, which acts as the first source of liquidity for withdrawals. The amount of funds set aside for the cash reserve
is based on the number of allocation points set for it.
When adding a strategy, the STRATEGY_OPERATOR
needs to set allocation points
for the given strategy, which define the amount of assets to be allocated into that strategy via rebalancing.
For the cash reserve
strategy, the allocation points amount is defined at the deployment time.
An address that has the GUARDIAN
role can adjust the allocation points for an already added strategy. For the cash reserve
strategy, and allocation points amount of 0 is not allowed.
Although the allocation points define the amount of funds to allocate in a strategy, the GUARDIAN
can still set a cap on a strategy allocated amount. By default, the cap for all the strategies is set to 0.
It is not possible to set a cap on the amount reserved for the cash reserve
strategy.
In the scenario where the euler earn vault can not interact with a strategy that has some allocated amount, the GUARDIAN
can set the strategy status as Emergency
. This will basically allow the rebalance
and harvest
functionalities to execute as expected without actually removing that strategy from the withdrawal queue array.
Once the strategy is set as Emergency
, the totalAllocationPoints
and the totalAllocated
are decreased by the strategy allocation points
and the strategy allocated amount
, which leads to counting that allocated amount as a loss, and deducting it following the loss deduction mechanism
.
The GUARDIAN
can revert back the strategy to active anytime, once that's done, the acutal Euler Earn assets amount in that strategy will be calculated using strategy.previewRedeem()
, and assigned back along with the strategy allocation points, respectively, to the totalAllocated
and totalAllocationPoints
.
The strategy allocated amount will be immediatly available for gulping
.
Strategies addresses are stored in the Withdrawal Queue
array based on their order of insertion.
The withdrawal queue order is mainly used during the withdraw
and harvest
operations.
An address that has the WITHDRAWAL_QUEUE_MANAGER
role can re-order the withdrawal queue array. It is expected to have the strategies with the most allocated amounts in the beginning of the array, for more gas-efficient withdraws.
When the assets get deposited into the euler earn by the user, those funds do not get allocated immediatly to the strategies, instead we just increase the totalAssetsDeposited
. The funds in the euler earn get allocated into the strategies through the operation called Rebalancing
.
For withdraws, if the amount of asset to withdraw can not be covered by the funds in the cash reserve
, the euler earn will loop through the withdrawal queue array
, and withdraw from the strategies one by one till the requested amount to withdraw is filled. If for some reason the funds can not be taken from the strategies, the withdraw operation will fail with NotEnoughAssets
error.
The withdraw operation does trigger a harvest operation
if the HARVEST_COOLDOWN
period has passed.
A rebalance
operation can be initiated by any address that has the Rebalancer
role, by calling the rebalance()
function, giving an array of strategies addresses to rebalance.
During rebalance, the vault calculate the startegy target allocation amount total assets allocatable * startegy allocation points / total allocation points
and compare that with the strategy current allocated amount. If that target allocation is greater, the euler earn will try to hit that target amount by depositing more into the strategy, if it's less, a withdrawal from that strategy will occur to reach that target amount.
For both cases, the amount to rebalance is restricted by a few factors...For the deposit, it includes:
- The amount of cash available, which is any surpluss in the cash reserve compared to its target allocation.
- The max deposit for that specific strategy.
For the withdraw case:
- The max amount withdrawable from that specific strategy.
Once funds are allocated, the euler earn vault has no awarness for the yield generated from those strategies, that's why a harvesting operation is needed.
The harvest()
function can be initiated by any address, and it does follow the withdrawal queue array order in its loop. The harvest operation aggregates all the positive and negative yield accrued from the strategies, and account only for the net amount. The yield is calculated by comparing the current allocated amount in the strategy and the euler earn assets in that strategy if the redemption of all the shares were to occur, using the strategy.previewRedeem()
call.
In the case of having a positive net yield amount, the performance fee
, if applicable, will be taken. The positive yield amount will not be instantly added to totalAssetsDeposited
but will be only added to the totalAllocated
amount, and this will make the yield available for gulping
.
In the case of a negative net yield amount, the euler earn has a loss deduction mechanism
implemented.
A HARVEST_COOLDOWN
period is set to 1 day
which only allows the harvest execution during a withdraw()
call if the last executed harvest was at least 1 day before. A direct call to harvest()
ignores the cooldown period check.
An address that has the role EULER_EARN_MANAGER
can set the fee recipient address and the performance fee. The MAX_PERFORMANCE_FEE
is set to 50%.
During the harvest
operation, if the net yield amount is positive and the performance fee is set, then that fee is accrued from the net yield amount, gets converted to shares
amount and minted to the feeRecipient
.
The euler earn vault can suffer losses in two scenarios:
- During a
harvest
operation, if the harvested net yield amount is negative. - If a circuit-breaker for a strategy is activated by marking its status as
Emergency
, using thetoggleStrategyEmergencyStatus()
function.
For those scenarios, the loss deduction mechanism is implemented. The euler earn vault will first deduct the loss amount from any past-harvest-yield that remains to be distributed as interest. If the loss is bigger than that, the remaining amount will be socialised amongst all current depositors in the Euler Earn vault. Although opportunistic depositors could frontrun loss socialisation by withdrawing earlier, that would only work if harvest
is not triggered during the withdrawal because the HARVEST_COOLDOWN
period hasn't pass yet.
As mentionned in the harvest section, the harvested positive yield is not immediately distributed to depositors, but it does get added to the totalAllocated
amount, therefore it becomes available to gulp()
. Gulping is the mechanism which distributes it to share holders in the vault over a "smeared" two week period. Accrued interest is added to the totalAssetsDeposited
of the Euler Earn vault, adjusting the exchange rate accordingly. On deposit and redeem accrued interest is added to the totalDeposited variable which tracks all deposits in the vault in a donation attack resistent manner.
On gulp, any interest which has not been distributed, is smeared for an additional two weeks. In theory this means that interest could be smeared indefinitely by continiously calling gulp, in practice it is expected that the interest will keep accruing, negating any negative side effects which may come from the smearing mechanism.
The gulping mechanism implemented here is the same one that was implemented in the EulerSavingsRate
contract.