Skip to main content

Rebasing

A rebase mints new DOHM to stakers, capped by what the treasury's backing can cover. It runs at most once per epoch (a fixed number of Bitcoin blocks) and is permissionless — there is no keeper. Every stake, unstake, and (plain) bond pokes it, so any user interaction distributes a due epoch. If nobody interacts, no epoch is lost: missed epochs are caught up on the next interaction (many at a time).

When a rebase is due

epochs_due = ⌊ (height − last_rebase_height) / epoch_length ⌋ (0 → clean no-op)

Each call advances last_rebase_height by epochs × epoch_length, so a longer backlog drains over successive calls.

The catch-up cap is path-dependent

How many missed epochs one call can clear depends on which valuation path it takes (see How reserves are valued below):

  • Cheap path (backing cache is fresh) — up to 1024 epochs per call. Its only per-epoch cost is in-contract arithmetic, so one interaction drains almost any quiet-period backlog.
  • True-up path (cache stale or dirty, full re-value required) — up to 3 epochs per call, because that call also runs a whole-reserve spot walk and must fit the per-transaction fuel budget. A deeper backlog clears over repeated calls.

This is the answer to "why didn't my whole backlog clear in one rebase?" — you were on the true-up path, capped at 3.

The reward, per epoch

Three quantities, all from treasury accounting (reserve_usd = the USD value of all treasury reserves; tracked = cumulative DOHM minted; pending = DOHM reserved for unredeemed bond notes; floor = frUSD per DOHM × 1e8):

1. Backing ratio (bps, 10 000 = exactly 1×-backed):

backed_dohm = ⌊ reserve_usd × 1e8 / floor ⌋
backing_ratio_bps = ⌊ backed_dohm × 10 000 / (tracked + pending) ⌋

2. Effective rate (ppm per epoch) — zero at or below 1× backing, ramping linearly to the policy maximum at the target ratio:

rate_eff = 0 if ratio ≤ 10 000
= rate_max if ratio ≥ target
= rate_max × (ratio − 10 000) / (target − 10 000) otherwise

rate_max is governance-set and hard-capped at 100 000 ppm = 10% per epoch.

3. Reward — the emission, capped by accounted excess so a rebase can never mint past backing:

excess = backed_dohm − (tracked + pending)
reward = min( ⌊ staked × rate_eff / 1e6 ⌋ , excess )

The reward is minted to the staking contract, which raises the index (see Staking). When several epochs are caught up in one call, each epoch's reward compounds — tracked and staked grow by the prior epoch's reward before the next is computed — and the total is delivered in one distribution.

What this means for you

  • Rewards can be zero. At ≤1× backing the rate is 0; with no excess the cap is 0. There is no guaranteed yield.
  • Rewards arrive with activity. A due epoch pays out when anyone stakes, unstakes, bonds, or calls rebase directly — not at a wall-clock instant.
  • A skipped epoch still advances. If staking is paused or the reserve is momentarily unpriceable, the epoch advances without a reward rather than blocking the protocol.
  • There is no caller bounty — calling rebase yourself costs a miner fee and pays you nothing directly.

Estimating yield

There is no fixed APY. To estimate one from the current parameters: the index grows by roughly rate_eff (as a fraction) each epoch, so compounded over a year:

epochs_per_year ≈ blocks_per_year / epoch_length (≈ 52,560 blocks/year at ~10-min blocks)
APY ≈ (1 + rate_eff)^epochs_per_year − 1

This is an upper bound: it assumes the rate holds and the reward is never excess-capped. Both rate_eff and the backing ratio move, so treat any APY figure as a live estimate, not a promise. (The Calculator does this for you.)

How the treasury values its reserves for rebase

Rebase reads a cached backing value (updated whenever a bond deposits value) — the cheap path. It falls back to a full live re-value of the whole reserve (frUSD 1:1; volatile assets at AMM spot within their band; LP by the √k markdown) when either of two things is true:

  • The cache is stale — older than the staleness window (blocks since the last full re-value).
  • The cache is dirty — an emergency reserve write-down (governance DecreaseReserveBal) flags it, forcing a true-up on the very next rebase even if the cache is time-fresh. The write-down deliberately skips re-valuation to stay usable when a reserve is unpriceable, so the next rebase must re-anchor.

Either way, a reserve that cannot be priced makes the rebase skip its reward — it never mints against a bad number.