ChallengeManager
Proves a short reserve on-chain and settles it in a single transaction.
CANDUKOYQIMZDK4MUWHN6MKJI5ORY5R4BPBEQIOQQSINJYITMY47UNZH
This is where Bound stops being a registry and starts being an enforcement mechanism.
Functions
| Function | Auth | Notes |
|---|---|---|
initialize(registry, auditor_staking, reserve_vault, fee_escrow, token, arbiter, min_stake) | once | |
challenge(challenger, cert_id, proof_type, victim, stake) -> challenge_id | challenger | Posts a bond of at least min_stake |
resolve(challenge_id) | permissionless | The trustless path — InsufficientReserve only |
resolve_by_arbiter(challenge_id, fraud_proven) | arbiter only | Subjective types (BoundExceeded, FakeSignature) |
The trustless path
resolve for 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(); // what is really locked
let fraud = actual < claimed; // math, not opinionNo oracle, no vote, no privileged caller. Anyone can invoke it, and the answer does not depend on who did.
Settlement, in one transaction
On fraud == true:
slash(auditor, victim, 80%) — the victim is compensated first.slash(auditor, challenger, 20%) — reward = stake / 5, paid for catching it.release_to_victim(victim, balance) — drain whatever reserve remains.Registry.invalidate(cert_id) — the certificate is dead.On fraud == false, the challenger forfeits their bond, which stays in the contract.
That is the cost that keeps challenges honest.
The arbiter path, and why it is separate
BoundExceeded and FakeSignature cannot be settled by arithmetic — they require
judging what happened off-chain. Those route to resolve_by_arbiter, a named party.
That is a real trust assumption and it is deliberately not disguised as a trustless one. See Trust model.