Bound
Contracts

ChallengeManager

Proves a short reserve on-chain and settles it in a single transaction.

CAYEGPIHNDIEONWNKRF2UPTO32SXGFTLBQ2K4RPN2LCIGOOZYLYYYIHY

This is where Bound stops being a registry and starts being an enforcement mechanism.

Functions

FunctionAuthNotes
initialize(...)onceWires the registry, vaults, staking, token, treasury and arbiter
set_router(router) / set_premium_vault(v)arbiter, onceWithout these the two router-backed proofs are dead
challenge(challenger, cert_id, proof_type, victim, stake) -> challenge_idchallengerPosts a bond of at least min_stake; opens or joins a 72h window
close_window(cert_id)permissionlessSettles every claim in a lapsed window together
close_window_early(challenge_id)arbiter onlyOnly against a claim the arbiter itself ruled false
resolve_by_arbiter(challenge_id, fraud_proven, harm)arbiter onlyFakeSignature, and the harm figure that sizes a victim payout
get_window, window_closes_at, is_settled, get_challengeview

resolve no longer exists. v1 settled the first claim to arrive and foreclosed every honest one behind it; close_window replaced it, and that removal is the largest ABI break between v1 and v2.

The trustless path

Three of the four proof types are decided by arithmetic over state the contracts already hold. InsufficientReserve is two cross-contract reads and a comparison:

let claimed: i128 = Registry.get_cert_reserve(cert_id);      // what the cert claims
let actual:  i128 = ReserveVault.get_balance(cert_id);       // what that cert holds
let fraud = actual < claimed;                                // math, not opinion

BoundExceeded compares PaymentRouter.spent(cert_id) against the certified bound, and ExpiredCertificate reads the router's record of payments settling after expiry. Both are trustless to prove and settle in hygiene mode — the certificate dies, the challenger takes a flat bounty, and the auditor is not slashed. See the settlement waterfall for why.

Note the cert_id argument on get_balance. In v1 there was none, the vault kept one pooled balance, and any deposit backed every certificate — which defeated this proof entirely. That is defect L5, and closing it is why v2 is a redeploy.

No oracle, no vote, no privileged caller. Anyone can invoke it, and the answer does not depend on who did.

Settlement

v1 settled inside the filing transaction and split the auditor's whole stake 80/20 between a challenger-named victim and the challenger. Both halves of that are gone. The arbitrary-recipient slash(auditor, recipient, amount) was the vulnerability — a colluding pair could name themselves — and it was removed rather than guarded.

v2 runs one waterfall for every proof type, when the claim window closes:

payable = min(harm, reserve + allocation) — harm is assessed by the arbiter, or computed by the predicate.
The victim is paid from the operator's own reserve only.
The challenger takes a fee of 10% of harm, also from the reserve.
slash_allocation(cert_id, treasury, amount) — slashed capital goes only to the treasury, never to a named address.
The allocation is retired; the unslashed remainder returns to the auditor's free stake.
Registry.invalidate(cert_id), and the bond is returned.

With harm == 0 this is hygiene mode: the certificate is killed, the challenger takes a flat bounty, and the reserve and the auditor's allocation are untouched. BoundExceeded and ExpiredCertificate always land here, because both are manufacturable by the operator for the price of gas — slashing an auditor on a counter the operator controls would mean nobody would ever audit.

InsufficientReserve pays no victim compensation either: it proves a covenant broke, not that a named person was harmed. Naming and sizing a victim always needs the arbiter.

A claim ruled false forfeits its bond. A claim the operator cures during the window has its bond returned in full — it was true when filed, and that is what the bond is staked on.

Full rules, and what each of these cost: the settlement waterfall.

The arbiter path, and why it is separate

FakeSignature cannot be settled by arithmetic — a forged attestation leaves no on-chain trace to read. It routes to resolve_by_arbiter, a named party, which also supplies the harm figure for any victim payout.

In v1 this path also covered BoundExceeded. The PaymentRouter closed that, which was the point of building it.

That remaining assumption is real and deliberately not disguised as a trustless one. See Trust model.

On this page