Proof for anything you can photograph: AI verdicts that move real money on-chain.
Aivy Checkout turns a phone photo into a cryptographic receipt that moves real money.
THE PROBLEM. Every deposit dispute on Earth is two people, two photos and zero proof. The host says damaged, the tenant says lies, and the money sits frozen for weeks. An AI can judge evidence perfectly, but it cannot walk into the apartment. And a photo on its own proves nothing, because anyone can send an old one.
HOW IT WORKS. Three properties have to hold at once, and each one is enforced somewhere different.
A verified unique human captures the evidence. World ID gates entry and the nullifier is the sybil key: one human, one live checkout, with no documents and no wallet-per-person assumption. The assurance tier sets the economic terms, so Device, Selfie and Orb map to different escrow caps. Verification is not a login here. It decides how much money the platform will hold for you.
The photo cannot be old, reused or generated. When the checkout opens, the contract commits keccak256(liveness challenge) on-chain, for example "lay a yellow pen across the seat of the chair", before the photo exists. A verdict is only accepted if its nonce hash matches that commitment, so an image that already existed cannot satisfy a challenge nobody had published. Anti-replay lives in the contract, not in a prompt. Optional geo-locks and time-locks prove where and when.
The payout is unilateral. The AI signs an itemized verdict, the escrow ecrecovers that signature on-chain, and when the final required item passes it transfers the deposit IN THE SAME CALL. There is no "host approves payout" step that can be withheld. If items fail or the deadline passes, the host recovers the funds through timeout resolution.
SPONSOR STACK, ALL LIVE. There are no mocks in the money path.
Hedera. CheckoutEscrow (Solidity, Foundry) is an itemized state machine with ecrecover-verified verdicts, registrar-gated creation, and unilateral auto-release of native HBAR. It is an AI agent moving value autonomously. Every checkout writes a full lifecycle audit trail to HCS: checkout_created, nonces_committed, verdict_signed for each item, then escrow_released, each one consensus-timestamped and publicly ordered. Item descriptions travel as keccak hashes and geo coordinates are rounded before sealing, so the record proves the area and the order of events without publishing your doorstep.
0G. Evidence blobs go to 0G Storage with public roots printed on the receipt. The verdict itself runs on 0G Compute mainnet (qwen/qwen3-vl-30b-a3b-instruct, TeeML) through the broker SDK, with on-chain provider acknowledgement, a funded ledger, signed request headers, and per-response signature verification. Whether that signature verified is written into the receipt and sealed to HCS, so the claim is checkable rather than asserted. If the provider errors or times out, a conventional model decides instead, and the receipt records which brain actually ruled.
World. World ID 4.0 with Selfie Check, used as economic policy rather than authentication. The credential tier decides the escrow cap.
THE AGENT ECONOMY. The verifier is an economically separate actor with its own wallet. The platform pays it 0.1 HBAR per verdict, on PASS or FAIL alike, so its income cannot bias its judgement. External agents can buy a signed inspection through an x402 endpoint: an HTTP 402 challenge, a real HBAR transfer verified against the public mirror node, then a signed verdict. One worker, one balance sheet, two customer types.
BREADTH. One engine drives six templates: rental checkout, expense receipt, vehicle return, scooter return, delivery handover and retail shelf audit, plus a build-your-own designer, because the contract never knew it was about apartments. The expense receipt template shifts the subject from an object's condition to a document's authenticity using the same machinery.
STATUS. Live at https://checkout.aivylabs.xyz and as a Telegram Mini App at https://t.me/aivycheckout_bot. Escrow 0x83B55906c6359c3f43Bf95cb8Cdef4455DB68226 on Hedera testnet, receipts on HCS topic 0.0.9736741, 22 Foundry tests and 30 offchain tests green. Completed cases are replayed on the landing page from the HCS topic itself rather than from a database, so anyone holding the topic id can audit the history independently.
STACK. Solidity with Foundry for the escrow. A Node 20 backend with zero web framework, just node:http, bundled by esbuild into a single file and run under pm2 on a small VPS behind nginx. React and Vite for the mini app, using IDKit for World ID and a plain camera input. ethers v6 everywhere on the client and server. The whole thing is one process and one static bundle, which is why a redeploy is a 30 second operation.
THE CORE IDEA IN CODE. ItemVerdict is a struct mirrored byte for byte in Solidity and TypeScript. The relayer signs personal_sign(keccak256(abi.encode(fields))) and CheckoutEscrow ecrecovers it on-chain, so the AI's judgement is a signature the contract checks rather than an API call it trusts. A parity script signs in TS and recovers in Solidity to prove the two never drift. When the last required item passes, the same function that verified the signature transfers the deposit, so there is no separate payout path anyone can block.
HEDERA, AND FOUR THINGS THAT COST US REAL TIME.
Value units. Inside Hedera's EVM msg.value arrives in tinybar (8 decimals), while the JSON-RPC relay takes tx value in weibar (18 decimals). The stored deposit has to be tinybar-scaled or the equality check reverts. Same number, two decimal worlds, one silent revert.
Nonce handling. The relay rejects future-nonce transactions instead of queueing them the way geth does, so everything must be sent strictly in order. Opening a checkout was five sequential transactions at roughly five seconds each, which is 21 seconds of dead air. We measured it on the mirror node, then reordered: create and fund, respond to the human, and commit the liveness nonces in the background, awaited later at the first evidence upload. The contract permits commitNonce in both Created and Funded status, so this is legal by design rather than a trick. Perceived wait dropped to about 10 seconds.
Account existence. On Hedera a transfer to an address with no account reverts, which would trap a deposit until timeout. Payout targets are checked against the mirror node before they are accepted.
HCS chunking. Receipts run past the 1 KB message cap because signatures are long, so the mirror node returns them as numbered chunks. Our first read produced pure garbage until we reassembled by chunk_info in order and skipped any receipt whose chunks straddle a page boundary.
0G, AND THE HACKIEST FIND OF THE WEEKEND.
Storage. Use @0gfoundation/0g-storage-ts-sdk. The older @0glabs/0g-ts-sdk 0.3.x targets a retired flow contract and its submit reverts on Galileo, which cost an hour before we spotted it. More importantly, 0G finalization is not on the critical path: a segment can sit in "available, but not finalized yet" for a minute, and storeEvidence ran before the vision call, so an item stalled until nginx gave up at 120 seconds. Since the keccak hash that goes on-chain is identical whether the bytes live on 0G or on disk, we gave the upload a budget (default 12s), served the local content-addressed copy when it expired, and let the upload finish in the background. Evidence in that state reports its backend as "0g-pending" rather than claiming either "0g" or "local", because both would be false.
Compute. The verdict runs on 0G Compute mainnet, model qwen/qwen3-vl-30b-a3b-instruct, through the broker SDK: fund a ledger, acknowledge the provider on-chain, get signed request headers, POST an OpenAI-shaped body with the evidence image, then verify the response signature. Two things are worth knowing. First, the ledger minimum is 3 OG, which is 30 days of faucet at 0.1 per day, so plan the funding before the hackathon. Second, and this is the hacky one: processResponse verifies the signature by chat id, but the id in the completion body is not the key. The provider files the signature under the zg-res-key response header. Passing body.id returns "chat_id_not_found" and verification silently degrades to unverified. We only found it by dumping every response header and trying each candidate.
We also learned that prompt shape matters more than model size for this task. Asking the model to state what it sees before judging, and to judge presence rather than exact position, turned a false rejection of a palm covering half the frame into a correct pass, while a control photo with the required object missing still correctly failed.
WORLD. Migrated from IDKit v2 to World ID 4.0 with an rp_id and server-signed request context. One portal gotcha for anyone following: the Incognito Actions UI is gone from the developer portal, and the World ID tab only offers the 4.0 upgrade. Actions are created through POST /api/v2/create-action/{app_id} with a team API key, and max_verifications must be set to 0 or a person can only ever verify once, which quietly breaks repeat demos. We use the credential tier as economic policy: Device, Selfie and Orb map to different escrow caps.
THE MOBILE BUG THAT LOOKED LIKE AN AI BUG. Items kept failing at the venue and the verdicts looked wrong. They were not. iPhones hand the browser HEIC, the client shipped those bytes raw, the server wrote them to a file named .jpg, and the vision API returned a 400 for an unsupported image before the model ever ran. Three layers fixed it: the file input names the formats it accepts so iOS transcodes, every capture is re-encoded to JPEG through a canvas (which also drops a 4 MB photo to about 600 KB, worth a lot on venue wifi), and an unknown format now fails with a message the person holding the phone can act on.
THE ARCHIVE IS THE CHAIN. Completed cases on the landing page are not read from a database. They are replayed from the HCS topic through the mirror node, reassembled from chunks, filtered to terminal receipts. The topic now carries the whole lifecycle, so an early version of this rendered checkout_created and verdict_signed events as empty cases until we required an outcome field.
X402. POST /api/x402/inspect returns an HTTP 402 challenge with x402-shaped accepts. The agent then makes a real HBAR transfer and retries with the transaction id, which we verify against the public mirror node for recipient, amount, age under 10 minutes, and single use, before running the verdict and signing it with the same key the escrow trusts.
SECURITY WORK. createCheckout is registrar-gated. Without it anyone could squat an unused checkoutId, register as host with a deadline a second away, and take the deposit through resolveTimeout. Checkout ids come from a CSPRNG rather than a counter, item text is sanitised before it reaches a prompt, and the relayer key and personhood mode both throw at boot rather than falling back to a permissive default. Coverage is 22 Foundry tests plus 30 offchain tests, and an end-to-end script runs the whole loop on a local anvil chain including the rejection and timeout paths.

