Auditor’s Guide

For nerds, auditors, and the professionally suspicious. The deep version of how it works. Everything here is checkable; where it isn’t yet, it says so. Live addresses and backing status: trust & verification.

1 · What Is On-Chain, Exactly

One Scrypto component (guild-marketplace-escrow, Radix mainnet) enforces a six-state task lifecycle. The app (Next.js + Postgres) is a mirror and a convenience — the ledger is the source of truth, and a reconciler heals the database from chain events, never the reverse.

Open ──claim──▶ Claimed ──submit──▶ Submitted ──approve──▶ Released   (terminal)
  │                │                    │
  cancel        expire/cancel        dispute ──resolve/auto──▶ Released | Refunded (terminal)
  ▼                ▼
Refunded (terminal)
TransitionCallerAuthTime gate
create_taskposter— (funds reward + insurance atomically)
claim_taskworkerworker badge Proof + claim bond; asserts worker != poster — an honest-mistake guard, not a self-dealing defence: poster is a caller-supplied parameter of create_task, so a decoy poster address bypasses it for the cost of gassets submit deadline (7d human / 1d agent)
expire_claimanyonePUBLICafter submit deadline
submit_taskworkerclaim-receipt burn (one-shot) + evidence hash committed
approve_and_releasepostertask-receipt Proof (resource + local-id match). NOT burned: under pull the task receipt is a PERSISTENT entitlement key, retired separately via burn_task_receipt once both lanes are zerocredits per-party entitlements inside the component; moves nothing to the caller
raise_disputeposter or workerreceipt/badge Proof + evidence hash
resolve_disputearbiterarbiter badgeruling: PayWorker / RefundPoster / Split
auto_resolve_disputeanyonePUBLICafter dispute window (72h deployed)
withdraw_workerworkerworker (or agent) badge Proof — asserts the badge is the SAME non-fungible local id that claimed the taskdeposits BOTH lanes (reward + bond) into task.worker_account, PINNED AT CLAIM — the caller cannot name the destination
withdraw_posterpostertask-receipt Proof (persistent, not one-shot)deposits BOTH lanes into task.poster, PINNED AT create_task — the caller cannot name the destination
burn_task_receiptpostertask-receipt Bucket (this one IS burned)TWO guards, both required: both poster entitlement lanes read zero, AND the task is terminal (Released | Refunded) — the second exists because on an Open task both lanes are legitimately zero, and burning there would strand the whole funded reward

Auth pattern worth auditing, and settlement is the part to read twice — it is two-phase. approve_and_release and the dispute rulings move no money outward: they drain the escrow vaults into per-party entitlements held inside the component. Only withdraw_worker / withdraw_poster pay out, and each deposits from inside the component into an account pinned earlier — the worker's at claim_task, the poster's at create_task — because deposit_both_lanes calls try_deposit_or_abort on that stored address. The caller never names the destination, so a public method being callable by anyone moves no value to whoever calls it. That pin is the property worth attacking: the worker badge and the task receipt are both transferable bearer instruments (neither resource calls withdraw_roles!, so both inherit the SDK default withdrawer=AllowAll), so stealing one lets you CALL the withdrawal — and the money still lands in the pinned account. Public-mint governs forging a credential, transferability governs stealing one, and the payee pin is what makes the second one not pay. On receipts, the two differ: the claim receipt is one-shot and burned at submit_task, but the task receipt is NOT burned at approval — under pull it is a persistent entitlement key presented as a Proof, retired explicitly via burn_task_receipt once both poster lanes read zero and the task is terminal. Recurring acts use Proofs; safety releases (expire_claim, auto_resolve_dispute) are PUBLIC and time-gated. One consequence worth auditing: the payee is coerced to an Account at withdrawal, so a component (a funding pool, say) cannot yet be a payee — push_entitlement is designed for that and is not in the deployed blueprint.

2 · The Trust Claims, and How to Check Each One

1. “The platform cannot move escrowed funds.
There is no platform key with authority over task vaults. The owner badge can: manage the accepted-token whitelist, freeze a whitelisted token, withdraw forfeited claim bonds. It cannot touch reward or insurance vaults.
Check: Read the blueprint's auth roles (source publication — see the trust page) and the method table above; try it on a fork.
2. “Nobody can be stranded.
Claimed tasks expire publicly; disputes auto-resolve publicly after the window; (vNext2) submitted work auto-releases after the review window. The winner finalizes from their own wallet — our keeper is watch-only by decision: it alerts humans and sends no transactions on the money path.
Check: The keeper's watch-only decision is documented in-code (scripts/keeper.mjs header); observe that auto_resolve_dispute and expire_claim carry no badge requirement.
3. “The terms you saw are the terms that settle.
Title, description, and structured terms (acceptance criteria, deadlines, revisions) are canonicalized and SHA-256 committed into the task at funding (work_brief_hash); submission evidence is likewise hashed. Disputes are judged against the committed brief — chat doesn't count unless it amended the brief.
Check: Recompute the canonical form (frozen v1/v2 formats in src/lib/escrow-utils.ts) against the on-chain hash via the Gateway.
4. “The mirror can't lie for long.
App state is advanced only by verified on-chain events (single confirm path), and a keyless reconciler replays chain events on a cron.
Check: src/lib/escrow-confirm.ts — one writer, event-verified, idempotent ledger (UNIQUE(taskId, txType)).

3 · Economics (Deployed → Planned)

Deployed today

Insurance min 5% of reward; claim bond 10% of reward (floor 76.45 XRD, capped on-chain); dispute window 72h, default SplitEvenly; arbiter fee 0 (cap 10%). Ground truth per component address — see trust & verification.

Planned at vNext2 (decided 2026-06-12)

Insurance becomes optional dispute coverage (~10–15% suggested, refunded if unused; no coverage ⇒ no dispute path — pure optimistic mode); review window with auto-release on poster silence (default 3d); SplitEvenly on dispute abandonment + reputation marks; mutually-signed splits (settle_by_agreement). One migration, everything bundled.

Two corrections (Phase-0 sitting, 2026-08-06). This paragraph read “heartbeat removed in favor of deadline + mutual extension”. The second half was never true: heartbeat is removed outright — one claim deadline, fixed at claim time, no paid extensions and no extension method at all. And auto-release is not being built: submit_task validates nothing, so a timed release would be a faucet. settle_by_agreement is deferred. This is now the deployed reality — the live component has no heartbeat leg.

4 · Honest Gaps (the Register)

ElementStatusBest we have
Per-criterion enforced payoutsNo shipped precedent, anywhereChecklist-as-evidence routes to: full release / revision / mutual split / insured arbitration
Co-funder voting on acceptanceEvery attempt died or went unusedCurator-pattern pools (named acceptor, self-claim refunds, escrow-level timers protect the worker)
Subjective quality judgmentUnsolvable in generalCommitted brief + insured human arbiter + (planned) AI advisory opinion
Agent work verificationStandards in flux industry-wideOn-demand PR checks against the committed brief, plus the claim bond and escrow deadlines; the claiming badge is pinned to the task and must be re-presented to submit. But the badge is a public mint that identifies no one, and the recall-revocable agent badge this model assumes has never been issued — so no human currently answers for an agent.

If you have a better mechanism for any of these: post it as a task. That is not a slogan; it is the product working on itself.

5 · Identity and Backing (Pseudonymous, With Receipts)

The operator is pseudonymous (bigdev / @bigdevxrd) with a verifiable on-chain and GitHub track record. In lieu of doxxing: the escrow blueprint source will be published with reproducible-build verification; the bug bounty is funded through the Guild’s own escrow (on-chain, visible); a timelocked commitment bond is planned. Status of each: trust & verification. For tasks/projects over $50k USD, trustee-verified identity is offered — a named third party attests the operator’s identity and standing without public disclosure.

6 · Known Limits — Read Before Relying

  • Blueprint upgrades are migrations (new component + env swap), not in-place — config is immutable per instantiation by design. Review the parameter sheet per component address.
  • Wallet-side MFA/multisig UX is not yet on Radix mainnet; arbiter-council M-of-N is enforced at the method-auth layer when activated.
  • The app is closed-source during beta; the money-path blueprint is the part being opened.
  • This is experimental software on mainnet. The honest-gaps register above is live, not historical.