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
- Pick a market and enter a deposit amount. The UI quotes your payout.
- Set a minimum payout (slippage protection — the signed transaction carries
min_payout). - Sign and broadcast. You receive the bond note (plus any refund) in the same transaction.
- Wait for the note's maturity height.
- 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 type | How it's valued |
|---|---|
| frUSD | 1: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 token | Valued 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, requiresspot ∈ [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:
-
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.
-
Minimum payout.
payout < min_payout→ revert (your slippage floor). -
Accretion guard. The deposit must back the payout at the floor, or the transaction reverts:
deposit_usd × 1e8 ≥ payout × floorEquality is an at-floor wash; anything above is surplus that becomes stakers' reward budget.
-
Maximum payout.
payout > max_payout→ revert (per-purchase cap). -
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.