sclera · monad mainnet · immutable

Trust stops being a badge. It becomes a price.

An agent locks USDC against a floor on its own future claim-truth rate. Stay above the floor and the stake comes back. Fall below it and a signed verdict takes it. There is no admin function and no override anywhere in that path.

every bond we have posted · 3

Read from Sclera on every load. State is the contract's, not an index's, because a window matures when a block number passes and emits nothing.

0.02 USDC is at stake across these right now. A released bond reads zero because the money went back, which is why the state is shown beside the amount rather than the amount alone.

one complete bond, on mainnet

Posted, held for its full window, released. Not a testnet screenshot and not a plan.

floor committed to
8000 bps 80% of decided claims must resolve true
window
216,000 blocks about a day at 400ms
minimum sample
5 below this nothing can slash

Verified by where the money went rather than by an event we emitted. While the bond was open the contract held 20,000 units and the agent was down by exactly that; on release both reversed. The bond now reads state 3, Released, with its amount zeroed and a second release reverts WrongState().

# the contract holds nothing now, because it gave it back
cast call 0x754704Bc059F8C67012fEd69BC8A327a5aafb603 'balanceOf(address)(uint256)' \
  0x57aF4e4B482Ab1bb4f9d1aeb5206258a7Def0eaf --rpc-url https://rpc.monad.xyz    # 0

# and the bond is settled rather than open
cast call 0x57aF4e4B482Ab1bb4f9d1aeb5206258a7Def0eaf 'bonds(uint256)' 10252 --rpc-url https://rpc.monad.xyz
# state 3 == Released, amount 0

What the release does not prove: the window ran with no verdict submitted, so this demonstrates the timelock, the state machine and the return path. It does not show that a measurement fed the outcome. release is callable by anyone on purpose, because the money can only go to the agent that posted it, which keeps a bond from being stranded by an operator who has lost access to the gas token.

how a bond is decided

The thing that slashes it is a measurement, not an opinion.

Post

The agent picks its own floor, window and minimum sample, then locks the tokens. Only the agent can: the contract reads ownerOf from the ERC-8004 registry and refuses anyone else.

Measure

Claim-truth rate over a block range, served at /api/sla/<agentId>. A pure function of the id and the range, so independent nodes computing it agree.

Sign

A verdict is signed by an attestor key derived from a passkey. The contract recovers it and compares against one address fixed at deploy.

Settle

Above the floor, the stake returns when the window matures and anyone may trigger it. Below it, the bond is forfeit in the same transaction that decides.

why the forwarder is not trusted

The one design decision that keeps this from being a fund-loss bug.

A Chainlink CRE report reaches a consumer through a forwarder. We read the deployed forwarder's source before trusting it: typeAndVersion() returns MockKeystoneForwarder 1.0.0 and its report function is permissionless by design. A contract gating on msg.sender == forwarder would be gating on nothingand this contract holds bonds.

So authorisation travels with the verdict instead. The attestor signs it, the contract recovers the signer and requires it equals 0x358c591aD7D60aC36bC223472B41518A7FA7b44A, fixed at deploy with no setter. The forwarder is a pipe. Anyone may push a report through it and exactly one signature makes it act.

That attestor is a passkey-derived key that owns nothing and can only sign verdicts. How the four keys separate →

what this does not prove yet

No bond has been slashed and we declined to fake one. Two things block it independently. The attestor key is derived from a passkey, so nothing in the repository can author a slashing verdict at all. That is the key separation working rather than a gap. And agent 10252's real claim-truth sample is empty, because our own record asserts no payment claims, so an honest verdict carries a sample of zero and the contract refuses it before computing a rate. Manufacturing a slash would mean signing numbers that are not a measurement or publishing false payment claims about our own agent. Both are the thing this project exists to detect, so the absence stays and the slashing path rests on the contract test suite and fork tests against real mainnet state.

The review that preceded this deployment found a real one and is published with the exploit rather than summarised: postBond originally never checked who owned the agent, so anyone could bond anyone and a stranger could have created an unslashable bond that made an agent read as staked. A badge that costs one atomic unit to fake is the thing this project exists to attack, so the fix is enforced by the registry and mutation-tested. How a verdict is decided →