Protocol yield generation

Overview

This document describes how the protocol generates yield from user deposits, how that yield is handled based on strategy type, and under what conditions it is sent to the L1SenderV2 contract for cross-chain reward minting.

Yield generation overview

When users stake tokens into deposit pools, those tokens may be used in different yield strategies, depending on the pool configuration. The protocol currently supports the following strategies:

  • AAVE - token is deposited into Aave to generate yield.

  • NO_YIELD - token is not used in any external protocol. This is typically used for stETH.

  • NONE - no yield logic is applied (only for private buckets without real stake).

Strategy-Specific Deposit Behavior

When a user calls supply()

For AAVE Strategy:

  • The user transfers the underlying token to the Distributor.

  • The Distributor supplies the token to the Aave protocol via AaveIPool.supply().

  • Yield is later measured by checking the aToken balance of the Distributor.

For NO_YIELD Strategy (e.g., stETH):

  • The user transfers stETH to the Distributor.

  • It is not deposited anywhere; yield is not actively generated.

  • Instead, the stETH is held and passively earns staking rewards (as per Lido).

Reward Distribution Timing and Constraints

Rewards are distributed using distributeRewards() which performs the following:

  1. Fetch emitted MOR - calls RewardPool.getPeriodRewards() using the last distribution timestamp.

  2. Check if distribution is due: public pools must respect minRewardsDistributePeriod (e.g., once per day).

    • This is enforced because stETH yield is not readable on-chain per second or block. It's accrued slowly, and daily intervals allow consistent accounting.

  3. Update Prices:

    • For each deposit pool in the reward pool, the price of the deposit token is fetched from Chainlink (via ChainLinkDataConsumer).

    • This allows yield across tokens (USDC, WETH, etc.) to be normalized to a dollar value.

  4. Calculate Yield Per Pool. See MOR distribution. Step #1

  5. Calculate Pool Shares. See MOR distribution. Step #1

  6. Update State. See MOR distribution. Step #1

Transferring Yield to L1SenderV2

The protocol does not retain accrued yield within the Distributor contract. Instead, yield is extracted and forwarded to L1SenderV2 selectively — only for the specific DepositPool that triggers the operation.

At the end of a yield cycle (e.g., daily), when any function (e.g., supply, withdraw, or withdrawYield) is called on a given deposit pool, the Distributor contract:

  • Calls distributeRewards to calculate and assign MOR rewards based on relative yield of all deposit pools in the same reward pool.

  • Then calls _withdrawYield only for the DepositPool involved in the transaction, transferring its actual yield to L1SenderV2.

Yield is transferred using the following logic:

  • For AAVE:

    • Calls AaveIPool.withdraw() to redeem aTokens.

    • Transfers the withdrawn tokens to L1SenderV2.

  • For NO_YIELD:

    • Transfers the excess tokens directly (if any) to l1SeL1SenderV2nder.

Once the tokens are with L1SenderV2, they can be:

  • Bridged to Arbitrum.

  • Used for minting MOR rewards for users via sendMintMessage().

Notes

  • stETH pools use NO_YIELD because stETH natively accrues rewards via rebasing.

  • The yield on stETH cannot be measured at each block — hence daily distribution is preferred.

  • Price-based yield normalization ensures fair distribution even when token values vary.

Last updated

Was this helpful?