Sign in

Protocol

Settlement is a pure function of what was posted

Anyone can call settle. Nobody decides a breach. These are the formulas, the states, the measurement and the parameters, read from the contracts on this page.

§01

Model

per epoch e of service s  (onchain aggregates only, voided windows removed)
  allowed  = floor((1 - availTarget) * probes)
  fails    = failed slots  (availability, freshness, deviation, tool stability)
  excess   = max(0, fails - allowed)
  fAvail   = min(capAvail, excess * bpsPerExcessProbe)
  fLat     = latBpsPerBucket * #{hours with p95 bin > latTarget}
  f        = min(maxSlashPerEpoch, fAvail + fLat)

per leg L in {USDG, $PBI}:   S_L = f * assets_L(end of e)
  the operator's junior pays min(S_L, junior_L) first; the rest comes from
  senior backers pro-rata (the share price falls)
  S_USDG -> compensation(s, e)          S_$PBI -> burned (0x...dEaD)

compensation: customers C = payers of USDG to s.payTo during e (Transfer logs)
  comp_c   = min(compMultiple * paid_c,  S_USDG * paid_c / sum(paid))
  leftover = S_USDG - sum(comp_c)  -> reserve
  posted as one Merkle root of (customer, amount, paid); claimed with a proof

premium per second:  p = premiumBps * faceUSDG / year
  reserve <- protocolPremiumBps * p
  leg USDG <- legSplit * rest      leg $PBI <- (1 - legSplit) * rest
  within a leg: pro-rata to assets, junior and senior alike

§02

State machines

Service

WatchedClaimedOpenActiveBreachedLapsedRetirednext epoch clean

Epoch

ProbingClosedSettledClaimable

Open becomes Active when the first backer arrives; premium streams from then on. Lapsed means the premium balance ran out: backers may leave at the end of the current epoch. Retired returns capital after the final settlement.

§03

Measurement

The prober set

One prober today, ours, on one machine in Europe. A second is added through the timelock with addProber; with several, a slot fails only when a majority of those that reported on it say so.

The latest post

Window2026-10-07 11:20 UTC
Block82,438,549
Probes, failed250, 1
Transaction0xe647...7f8b

Anchor rule: each record carries the hash of the latest block seen before its probe, and its window lands onchain within 30 min of closing. Outage rule: when 50% or more of the probed services fail most of a window, the window is voided for everyone. 115 windows posted, 28,650 probes counted, 0 windows voided.

§04

Settlement and compensation

Canary, epoch 14, worked through with its own numbers

probes = 60, failed = 0, target = 99%
allowed = floor(1% * 60) = 0
excess  = 0
f       = 0%   (0 x 10 bps, capped)
slashed = 0.000000 USDG to compensation, 0.0000 $PBI burned

See the customers paid on the service page.

§05

Invariants

I1cashUSDG held by BondBook is at least every leg, premium balance, earned premium, crystallised exit and the reserve.
I2capNo epoch slashes more than maxSlashPerEpoch of a leg's assets at that epoch's close.
I3first lossIf a senior backer loses anything in an epoch, the junior tranche of that leg is zero after it.
I4orderEpochs settle once and in order, from aggregates posted before epoch end plus postDeadline.
I5no dodgeAn exit never executes before every epoch it overlapped is settled.
I6bounded compensationcomp_c is at most compMultiple times paid_c, and the sum of comp_c is at most S_USDG.
I7ownerThe owner can never move bond capital, premium balances or compensation. Its withdrawals reach the reserve and free ETH only.

A stateful fuzzer checks them after every call: 128 runs of depth 64, so 8,192 random calls per invariant, on every build. Lost by customers without compensation to date: 0.000000 USDG committed and not yet claimed, 0.000000 USDG awaiting its customer list.

§06

Parameters

ParameterNowBounds in codeMeaning
probeInterval1 min60 s to 10 minprobe cadence per service
postEvery10 min5 to 60 minone onchain post per window
postDeadline30 minup to 6 hlatest a window can be posted
outageVoid50%30% to 90%share of failing services that voids a window
bpsPerExcessProbe10 bps1 to 100 bpsslash per failed probe beyond the allowance
capAvail25%up to 50%availability part of an epoch slash
latBpsPerBucket50 bps0 to 500 bpsper hour over the latency target
maxSlashPerEpoch30%1% to 50%ceiling per epoch per leg
compMultiple3x1x to 10xcompensation ceiling against what a customer paid
minOperatorBps20%5% to 100%junior tranche floor per leg
minPremiumRunway7 d1 to 90 dpremium pre-funded at issue
protocolPremiumBps10%0% to 25%share of premium to the reserve
settleDelay2 h0 to 24 hwait after an epoch ends before it settles

Read live from getParams() on BondBook and ProbeLog. Owner: a TimelockController with a 48 h minimum delay.

Timelock queue

Nothing queued.

§07

$PBI

Token

not bound yet

the $PBI legs open when it is bound

Burned by slashes

0.00

$PBI sent to 0x...dEaD by settlement

ETH accumulator

0.000000

ETH credited to $PBI backing; 0.000000 ETH unallocated

A $PBI leg earns its share of the service's premium in USDG, and its pro-rata share of the Pons creator-fee ETH if the token's creator fees are pointed at BondBook (harvest() pulls them). A slash on the leg burns the operator's junior $PBI first, then backers'.

§08

Interface

FunctionWhoWhat
ProbeLog.post(window, anchorBlock, anchorHash, root, aggs[])proberone window of aggregates, a Merkle root and a block anchor
ProbeLog.register(kind, endpointHash, uri)registraradds a watched service
ProbeLog.setOperator(id, operator, payTo)registrarrecords a verified claim
ProbeLog.epochStats(id, from, to, latBin)anyone (view)probes, failed slots, hours over the latency target
BondBook.issue(params)claimed operatoropens a bond with junior capital and premium
BondBook.back(bond, leg, amount)anyonesenior shares in a leg, up to its cap
BondBook.requestExit / withdrawExitbackerleaves after the next settlement
BondBook.settle(bond)anyonesettles the next epoch by formula
BondBook.claimPremium(bond, leg)backerUSDG premium, and ETH on the $PBI leg
BondBook.isBonded(service, minCapital, mask)anyone (view)for agents, spending policies and hooks
Compensation.postRoot(bond, epoch, root, total)publisherthe customers of a slashed epoch
Compensation.claim(bond, epoch, account, amount, paid, proof)anyonealways pays the account
BondBook.setParams / ProbeLog.addProbertimelock (48 h)parameters inside their bounds, the prober set
BondBook.bindToken(token)binder, onceopens the $PBI legs
cast call 0xe43B9179c9FB05072860bcf64778041f30Eb5292 "getParams()((uint16,uint16,uint16,uint16,uint16,uint16,uint32,uint16,uint32))" \
  --rpc-url https://rpc.mainnet.chain.robinhood.com
cast call 0xe43B9179c9FB05072860bcf64778041f30Eb5292 "isBonded(uint256,uint256,uint256)(bool)" 1 100000 1 --rpc-url ...
cast call 0x091A79A686fa96005243628adA4cA40C69Ebe387 "epochStats(uint256,uint256,uint256,uint8)(uint256,uint256,uint256)" 1 <from> <to> 255 --rpc-url ...
cast send 0xe43B9179c9FB05072860bcf64778041f30Eb5292 "settle(uint256)" <bondId> --rpc-url ... --private-key ...

Sources are verified on Blockscout. Security lists who holds each role.