Skip to main content

Bonding

Bonding sells a reserve asset to the treasury in exchange for DOHM, delivered after a vesting cliff. Each purchase mints a bond note — the sole claim on that payout. The note matures at a fixed block height; you then redeem it for the full payout.

Flow

  1. Pick a market and enter a deposit amount. The UI quotes your payout.
  2. Set a minimum payout (slippage protection — the signed transaction carries min_payout).
  3. Sign and broadcast. You receive the bond note (plus any refund) in the same transaction.
  4. Wait for the note's maturity height.
  5. Redeem the note: send it back, receive the full DOHM payout. The note is burned.

Pricing

Every deposit is first valued in frUSD (usd, base units), then priced by the market's mode. deposit_usd = amount × price / SCALE, where price depends on the asset (see Deposit valuation below).

Fixed-price market (fixed = frUSD per DOHM × 1e8):

payout = deposit_usd × 1e8 / fixed

At-floor market (prices exactly at the treasury floor — economically neutral):

payout = deposit_usd × 1e8 / floor

Curve market — a constant-product curve against virtual reserves v_in (frUSD units) and v_out (DOHM units). Spot price of the curve is 1e8 × v_in / v_out:

payout = deposit_usd × v_out / (v_in + deposit_usd)

After the purchase, the deposit joins the virtual reserve — v_in += deposit_usd — so the next bond is more expensive (demand feedback). When the market is idle, v_in decays: it halves every half_life blocks (linearly interpolated within a period), but never below min_v_in:

v_in(t) = max(v_in >> (elapsed / half_life) − partial, min_v_in)
partial = (v_in_shifted / 2) × (elapsed mod half_life) / half_life

min_v_in is the hard price floor of the curve; markets are created so 1e8 × min_v_in / v_out > treasury floor, i.e. a curve bond is always accretive. Markets may also enable tuning: every tune_interval blocks, v_in is re-targeted so remaining capacity sells out by the market's conclusion height, moved at most ±25% per tune and never below min_v_in.

Deposit valuation (price)

Asset typeHow it's valued
frUSD1:1, price = 1e8. No oracle.
Volatile asset (e.g. frBTC)Signed bond only. An off-chain signer attests a price band (see below). Your payout is sized at the band midpoint (min_usd + max_usd) / 2. The treasury independently reads the live AMM spot price and reverts unless spot ∈ [min_usd, max_usd] — an off-band price cannot execute.
LP tokenValued at the pool's manipulation-resistant per-unit markdown (below), inside a signed band, then priced on the market's curve or fixed price.

LP per-unit value (r0_usd, r1_usd = each pool leg's reserve valued at live spot):

value_per_lp = 2 × √(r0_usd × r1_usd) × 1e8 / total_lp
deposit_usd = value_per_lp × lp_amount / 1e8

The geometric mean √(r0·r1) is invariant under swaps in the pool, so a flash swap cannot inflate the LP's value.

The signed attestation (for integrators)

A volatile or LP bond requires an ed25519 attestation from the governance-set price signer. The signed message is 7 u128 words, little-endian, led by a domain-separation tag:

[ DOMAIN, asset_block, asset_tx, min_usd, max_usd, expiry_height, nonce ]

DOMAIN = 1 (DOMAIN_BOND_SIGNED) for a single-asset bond → (asset_block, asset_tx)
= 2 (DOMAIN_BOND_LP) for an LP bond → the pool's (block, tx)

The full attestation is 11 u128 words: these 7 fields plus the 64-byte signature packed big-endian across 4 words. The leading tag is mandatory — it makes the two message spaces cryptographically non-interchangeable, so a single-asset attestation cannot be replayed as an LP one (or vice versa). Omit it and verification fails. The signer, expiry, and single-use nonce are all checked on-chain before any state change.

The attested [min_usd, max_usd] band has no width cap in bonds. A wide band is bounded differently: the treasury independently re-reads live spot, requires spot ∈ [min_usd, max_usd], and enforces the accretion guard on its own value — so an over-wide band can only revert, never under-back. (The 2× width cap you may see referenced is a separate governance-set per-asset sanity band inside the valuation module, not this attestation.)

Execution order (what reverts, and in what order)

Once the deposit is priced, the bond runs these checks in order. A revert names the first one it hits:

  1. Capacity partial-fill. If your payout exceeds the market's remaining capacity, the fill is trimmed exactly to capacity — the needed deposit is recomputed with a ceiling inverse (never under-backing) and the unused deposit is refunded in the same transaction.

  2. Minimum payout. payout < min_payout → revert (your slippage floor).

  3. Accretion guard. The deposit must back the payout at the floor, or the transaction reverts:

    deposit_usd × 1e8 ≥ payout × floor

    Equality is an at-floor wash; anything above is surplus that becomes stakers' reward budget.

  4. Maximum payout. payout > max_payout → revert (per-purchase cap).

  5. Per-block cap. The sum of payouts in one Bitcoin block cannot exceed max_dohm_per_block.

A market past its conclusion height, or explicitly closed, accepts no new bonds (checked before pricing). Redeeming existing notes is unaffected.

Vesting — a cliff, not linear

claimable = payout if height ≥ vesting_end
= 0 otherwise

vesting_end = start + vesting_blocks

start is your purchase block (or a fixed market-wide start for presale-style markets). Nothing is claimable before maturity; everything is claimable at maturity. Two purchases at different heights are two separate notes with two maturities.

Redeeming

  • One note per transaction. Redeeming several matured notes takes several transactions, each with its own miner fee.
  • Redeem works even while the protocol is paused — an exit is never trapped.
  • The full payout mints on redeem; the note is burned. If you over-sent other assets on the same UTXO, they are passed back untouched.