ERC-8416: Epoch-Based Fixed-Rate Vault

This standard defines specs for Fixed-Period (epoch) and Fixed-Term vaults.
A vault may have one epoch (i.e., stop accruing after maturity) or multiple sequential fixed-rate epochs, which increases the vault’s lifetime and composability.
By extending both ERC4626 and ERC7540 flexibly, the standard supports a variety of sync/async schemas.

Example diagram

Note: Implementations have the flexibility to freely define epochs’ intervals and their overlaps.

Interface

interface IERC8416 {
  event EpochAdded(
    uint256 indexed epochId,
    uint256 rate,
    uint256 maxDeposits,
    uint256 start,
    uint256 entryWindowEnd,
    uint256 accrualStart,
    uint256 accrualEnd,
    uint256 exitWindowStart,
    uint256 end
  );

  event Terminated();

  function epochId() external view returns (uint256 id);

  function rate(uint256 id) external view returns (uint256 rate);

  function maxEpochDeposits(uint256 id) external view returns (uint256 maxAssets);

  function epochPeriod(uint256 id) external view returns (uint256 start, uint256 end);

  function entryWindow(uint256 id) external view returns (uint256 start, uint256 end);

  function accrualInterval(uint256 id) external view returns (uint256 start, uint256 end);

  function exitWindow(uint256 id) external view returns (uint256 start, uint256 end);

  function currentRate() external view returns (uint256 rate);

  function isTerminated() external view returns (bool terminated);
}

/// @dev Implemented together with `IERC7540Deposit` by asynchronous-deposit vaults.
interface IERC8416AsyncDeposit is IERC8416 {
  function maxRequestDeposit(address owner) external view returns (uint256 maxAssets);
}

/// @dev Implemented together with `IERC7540Redeem` by asynchronous-redeem vaults.
interface IERC8416AsyncRedeem is IERC8416 {
  function maxRequestRedeem(address owner) external view returns (uint256 maxShares);
}

ERCs comparison

ERC-4626 ERC-7540 ERC-5095 ERC-8416
Rate Floating Floating Implied by PT discount Fixed per epoch
Maturity None None One per token One per epoch, rolling
Share token One One One per maturity One across all epochs
Entry / exit Any time Any time (request) Exit after maturity only Entry/exit windows; exit always open after termination
Sync / async Sync Async Sync Sync or async, per leg
4626-compatible — Partially No Yes when sync; 7540 when async

In one line: ERC-8416 brings 5095’s fixed rate and maturity to the 4626/7540 interfaces, while keeping a single fungible share token for every term.

Reference implementation

Full specification

Any question or comment is welcome.

6 Likes

Before depositing, I’d want to clearly see what happens when the period ends: does earning stop, does the deposit continue into another period, and when can I withdraw? Having that explained alongside the rate would make the choice much easier to understand.

3 Likes

Some notes:

  1. (Re: Motivation section) ERC4626 doesn’t describe a single mechanism for calculating the exchange rate, just that the exchange rate in practice should follow the average depositor’s share price, in a cheaper computation vs. preview* workflows, which are allowed to be more bespoke/involve heavier computations (such as the inclusion of time-based terms). The purpose is to provide as much of an apples-to-apples comparison as possible between the many varieties of ERC4626 vaults, despite different mechanisms and types of yield.
  2. Having the exit window hug the end of an epoch may be incorrect, I would think in practice you might want to open the exit window at the same time as the entry window, because you may want to close it earlier than the end of the epoch to ensure enough time exists to process both entries and exits (subscriptions and redemptions), e.g. if both windows close at 6pm during a daily epoch, but the epoch completes settlement and posts at midnight (allowing enough time to perform the aggregated settlement computations between 6pm and midnight in order to post them at midnight)
  3. I think that the terminated state should be left completely out of this ERC, as it seems very orthogonal to the concept of epoch processing. There are other ways to block subscriptions into a tokenized fund that is winding down (perhaps another great ERC4626 extension proposal)
  4. We used share price conversions instead of rates because it is easier to extrapolate the rate based on discrete changes in price, instead or trying to derive the price changes from the rate. Having a rate oracle may be a worthwhile feature, but could also be proposed as an alternative extension of ERC4626. In my opinion, it would be cleaner to have epochs track discrete changes in share price conversions instead or tracking a normalized yield rate. You can easily compute yield rate over time from this data off-chain, or update an interest oracle as a separate feature if the contract requires it

As someone who has also looked into tokenized fund architectures, I think this is a great start!

5 Likes

Would deriving the rate from share price changes lose the distinction between the agreed rate and actual performance?

I’m thinking about an RWA vault with a fixed rate for the epoch. If an underlying loan takes a loss then the ending share price might fall short of the scheduled accrual. The price history would show the outcome but would not tell a depositor what rate had been agreed at entry.

Would you put those agreed terms in the separate rate extension you mentioned? Or do you see a way to preserve that distinction using share price conversions alone?

2 Likes

Thanks!
does the earning stop? Yes, at maturity. The share price increases during the accrual interval, outside of it (e.g., between epochs) it stays the same.
does the deposit continue into another period? Yes, unless you exit. By default, the vault rolls over to the next epoch or it terminates and the shareholders withdraw their funds.
when can I withdraw? During the exit window. When an epoch is created, it must define an immutable exit window where redemptions (sync) or redeem requests (async) can be made.

3 Likes

Thanks!

  1. I agree, the ERC4626 can express fixed rate but it needs to be extended to be able to express epoch-based maturities instead of a single term.
  2. Entry and Exit windows may overlap and both can have the same duration as the epoch itself. For redemptions settlement, it can be done between epochs (limbo period) or during the next epoch.
  3. OK, we’ll think about that, the idea is to cover the scenario where no more epochs will exist and ensure solvency and users’ access to their funds, because if no more epochs are created, users can’t withdraw (i.e., there’s no more exit window). Do you see any scenario where the termination process defined by the standard couldn’t fit well?
  4. The rate param is a simple way for callers to check the advertised rate for the epoch. Without it, the actual rate can only be known after the accrual period ends; it can’t be known beforehand.
3 Likes

Thanks!
I think it’s worth having both advertised/target and actual (derived from share price) rates.

3 Likes

Thanks, that clears it up. The automatic rollover is something I’d want highlighted before depositing. A reminder before the withdrawal window closes would also help users avoid staying in for another period unintentionally.

4 Likes
  1. What happens if the next epoch hasn’t been created before the current epochs exit window closes?
  2. What happens if a future epoch is created in advance but market changes significantly before it starts?
  3. For asynchronous deposits, what happens if I request a deposit during one epoch but it isn’t fulfilled until the next epoch has started? Would my deposit enter the epoch I originally requested, or the new epoch and its potentially different rate?

Some questions, but love the idea!

3 Likes

While not quite identical, and we never followed up enough given the wider fixed-rate space died in 2023, we (Yield, Swivel, Sense, Pendle, etc.,) did work on a similar standard / ERC-5095:

It doesn’t answer any of the open questions nor address exactly the intent of this EIP but I figured I would add the context to the discussion.

2 Likes

@DomCrogan

  1. Contract enforces that exit window is within epoch period. Exit epoch does not overlap with next epoch
  2. Epoch rate and some config can be update before start of epoch.
  3. No. User can cancel deposit request if not fulfilled. If next epoch already started but keeper has not fulfilled any deposit request, user still has chance to cancel the deposit request.
2 Likes

Thanks Julian for your input, I’ve update the description adding a comparison table that also considers the ERC-5095.

1 Like

Does cancellation alone protect a depositor when a pending request carries into the next epoch? What happens if the keeper fulfills it at a lower rate before the cancellation transaction lands?

Should the request commit to a specific epoch and rate terms so that fulfillment under different terms requires explicit consent? Otherwise the depositor’s protection seems to depend on transaction ordering.

2 Likes

I think the cancellation will work in most cases, the request parameters are limited by the ERC-7540, and the standard is meant to be minimal and leaves implementation details to the implementers. That said, one vault may implement requests in a way that they can only be fulfilled inside the epoch where they were created (or if the next epoch’s rate >= the previous).
Do you think we should define this behavior in the standard’s spec?

Thanks. I’d define the outcome, not the mechanism: a pending request MUST NOT be fulfilled at a lower rate than the one advertised when it was made, unless the controller consents. How a vault enforces this (same-epoch only, rate >= previous, etc.) can stay up to the implementer.

Epoch binding alone isn’t enough, since an epoch’s rate can still be updated before it starts. No new ERC-7540 parameters are needed. epochId() and rate(id) already exist, and an optional view returning a request’s bound epoch would let integrators check its terms.