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
| Function | Auth | Notes |
|---|---|---|
initialize(...) | once | Wires the registry, vaults, staking, token, treasury and arbiter |
set_router(router) / set_premium_vault(v) | arbiter, once | Without these the two router-backed proofs are dead |
challenge(challenger, cert_id, proof_type, victim, stake) -> challenge_id | challenger | Posts a bond of at least min_stake; opens or joins a 72h window |
close_window(cert_id) | permissionless | Settles every claim in a lapsed window together |
close_window_early(challenge_id) | arbiter only | Only against a claim the arbiter itself ruled false |
resolve_by_arbiter(challenge_id, fraud_proven, harm) | arbiter only | FakeSignature, and the harm figure that sizes a victim payout |
get_window, window_closes_at, is_settled, get_challenge | view |
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 opinionBoundExceeded 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.slash_allocation(cert_id, treasury, amount) — slashed capital goes only to the treasury, never to a named address.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.