Five checks. Two local, three against Arc, and a third verdict that matters.
What each one asks
- 01integrityLocalpass · fail
Does the body still hash to the id it claims?
Recomputes sha256 over the canonical body. Editing one character of a receipt fails here, which is why this check has no unknown: the answer never depends on anything outside the file.
- 02signatureLocalpass · fail · unknown
Did the policy signer sign these exact bytes?
ECDSA P-256 over the same canonical bytes the id covers. A receipt carrying a zk proof returns unknown: the in-browser verifier for those ships with v1.
- 03mandateArc mainnetpass · fail
Was that mandate commitment live at that epoch?
Reads MandateRegistry.mandateOf(account, epoch). Fails if nothing is registered, if the epoch was revoked, or if the commitment on chain differs from the one the receipt was checked against. The terms behind the commitment stay sealed either way.
- 04linkageArc mainnetpass · fail · unknown
Does the budget chain join to its predecessor?
With a predecessor in the bundle, the previous receipt's counter.next must equal this one's counter.prev — a gap means a receipt was dropped or reordered. With no predecessor, the starting commitment has to be anchored in AnchorRegistry; if it is not, the answer is unknown, not fail.
- 05settlementArc mainnetpass · fail · unknown
Did the transaction it points at succeed?
An eth_getTransactionReceipt for action.settledTx. A reverted transaction fails. A transaction that is not visible yet, or a receipt for a different chain than the verifier is pointed at, returns unknown.
Three verdicts
| pass | The check ran and the answer was yes. |
|---|---|
| fail | The check ran and the answer was no. This receipt is not what it claims. |
| unknown | The check could not decide. Never reported as a pass, and never as a fail — which would read as an accusation the evidence does not support. |
The report
Verification returns every verdict, not a boolean — the detail line on a failing check is usually the whole answer. Receipts are checked in the order given, so a bundle verifies its own linkage.
const report = await verifyReceipts(parsed.receipts, { chain }); report.status; // worst verdict across every receiptreport.receipts[0].status; // worst across this receipt's five checksreport.receipts[0].checks; // [{ id, status, detail }, ...] in CHECK_ORDER
Checking against fixed state
The RPC reader is one implementation of a small interface. Tests use the in-memory one, which answers from a state you supply.
// In tests, hand the verifier the chain state you want to check against.import { createMemoryChainReader } from "@arcveildev/sdk"; const chain = createMemoryChainReader({ mandates: [{ account, commitment, epoch: 1, revoked: false }], transactions: { [settledTx.toLowerCase()]: { status: "success" } }, anchors: [head],});
Or just try one
The verifier on this site runs exactly these checks against Arc mainnet. Load a sample, change one character, and watch which check catches you.