Atomic ZK-Proof-Gated Settlement for x402 Agent Payments: A Measured Reference Design

TL;DR

x402 lets an agent pay for a resource-server call, but it never binds payment to correctness of execution — a provider can take payment and never deliver, or deliver something other than what was promised. I’ve built and measured a reference design (ZkInferenceEscrow) that closes this gap: payment for an AI inference call settles atomically, in one transaction, only when an on-chain ZK proof verifies the call was executed by a specific, pinned circuit. As a structural side effect, the x402 facilitator becomes optional rather than trusted. This post lays out the design, is upfront about what’s genuinely novel versus known technique, and shares real gas/economics numbers from a Base Sepolia deployment rather than paper estimates.


The gap

x402 (HTTP 402-based agent payments, now under the x402 Foundation) solves payment authorization well, but correctness of the paid-for work is out of scope by design: a client sends money, a server sends something back, and nothing on-chain ties the two together. A dishonest or buggy provider can take payment and withhold the response, or return output that doesn’t match what it promised, and the client has no recourse besides reputation and off-chain trust.

This isn’t a new observation — it’s the classical fair exchange problem (Pagnia & Gärtner, 1999, show it’s impossible without a trusted third party), applied to a specific modern setting: HTTP-native agent payments. The lineage here runs through Zero-Knowledge Contingent Payments (Maxwell’s “pay-to-sudoku”, 2016, hardened against setup-subversion attacks by Campanelli, Gennaro, Goldfeder & Nizzardo, CCS 2017) and FairSwap (Dziembowski, Eckey, Faust, CCS 2018) — neither of which targeted HTTP-native agent payments or ML workloads specifically.

I’m not the first to flag this gap for x402 specifically. There’s at least one recent academic proposal that attacks the same atomicity problem using TEEs plus adaptor signatures, and a couple of industry blog posts sketching “payment settles only when computation is proven” at a conceptual level. None of these, as far as I can find, ship a concrete, x402-scheme-conformant, ERC-8004-integrated reference design with measured costs. That’s the gap this fills.

The design, briefly

The core mechanism is a proof-gated escrow, registered as an x402 payment scheme:

  1. Client sends a request; provider responds with a 402 quote binding a specific model circuit (identified by its verifying key) and a salted commitment to the input.

  2. Client signs an EIP-3009 payment authorization whose nonce is derived from all request parameters — this is the anti-tamper anchor; a relayer can’t alter any field without invalidating the signature.

  3. Provider computes the result and returns it immediately over HTTP (optimistic delivery — the client sees an answer in under a second).

  4. In parallel, the provider generates a ZK proof (EZKL/Halo2) that the pinned circuit, run on the committed input, produces the delivered output.

  5. One on-chain transaction verifies the proof, releases payment, and publishes the output in calldata — payment and output-availability are the same state transition. No proof, no payment; no payment without provable output.

  6. If no valid proof lands by the deadline, the client recovers funds permissionlessly.

What’s actually new here, and what isn’t

Being explicit about this, because I’d rather have this conversation now than in the comments:

Not new: the fair-exchange-via-ZK pattern itself (ZKCP, FairSwap), Halo2/EZKL as a proving stack, x402’s payment layer, ERC-8004 as a validation registry.

A real composition, not just glue: binding this specifically into an x402 payment scheme (rather than an external escrow the client has to know to use) and into ERC-8004’s Validation Registry (so a model’s track record accumulates against an immutable circuit identity, not a mutable API endpoint) is, to my knowledge, not published anywhere as a concrete spec — only as blog-level concept posts.

A genuinely counter-intuitive finding: the naive assumption is that ZK verification gas dominates the per-request cost, so the obvious optimization is proof aggregation. Measuring the actual breakdown shows that escrow-opening (the EIP-3009 pull + state writes), not verification, is the dominant line item once you batch verification — meaning channel-style funding, not proof aggregation, is the first-order lever. This reorders the “obvious” optimization roadmap.

A free structural side effect: because only the contract can authorize payment release, the x402 facilitator degrades from trusted settlement executor to optional, unprivileged relayer — decentralizing the facilitator wasn’t a design goal, it falls out of removing the need to trust anyone with settlement.

Measured, not estimated

Everything below is from real transactions (Base Sepolia), not simulation:

  • Solo settlement (open + settle, small-circuit model, K5 salted input commitment): ~970k gas total, both operations individually within 2% of local Anvil measurements.

  • With channel-based funding + K=8 batched proof verification + a challenge-close mechanism for safe early channel exit: ~84% gas reduction versus solo settlement — down from the naive aggregation-only estimate that undercounted the escrow-opening cost.

  • L1 data-availability fee (OP-Stack/Fjord formula) is a small fraction of total cost at current Base gas prices (~3-4%), but scales to become the dominant term under L1 fee-spike conditions — worth modeling explicitly rather than assuming it’s negligible.

Circuit size is the real constraint on this approach today: proving cost puts a practical ceiling around low-tens-of-millions of parameters on commodity hardware, which is the right regime for scoring/classification/compliance-check models, not frontier LLMs. That’s a documented limitation, not a hidden one — the verifier is pluggable so this improves as faster provers (recursive SNARK aggregation, GKR-based approaches) mature, without changing the settlement semantics above.

Open questions I’d genuinely like input on

  1. VK-to-model-identity binding. The proof shows a pinned circuit executed correctly — it doesn’t by itself prove that circuit is the model the provider advertises. I’m using an independent-reproduction attestation pattern (third parties recompile the artifact bundle and attest the resulting VK matches) rather than trying to solve this cryptographically. Is there a cleaner primitive for “this VK corresponds to this claimed model” that doesn’t require trusting the attester set?

  2. Batched-circuit privacy. Once you batch K requests into one proof for cost amortization, does anything about the batch (timing, position, which requests get proven vs. skipped) leak information a single-request design wouldn’t? I have a design for output encryption that composes with batching without breaking atomicity, but haven’t seen this specific composition (proof-carrying settlement + batching + in-circuit output encryption) discussed elsewhere and would like to know if I’m missing prior art.

  3. Has anyone else already shipped a scheme-conformant reference implementation of this pattern for x402 that I should be citing/comparing against instead of re-deriving?

Happy to go deeper on any part of this — architecture, the gas breakdown methodology, or the attestation design — in the comments.

1 Like

Update since posting: the design is now tested end-to-end on Base Sepolia testnet with real transactions — measured proof generation (2–22s depending on model size), real on-chain settlement cost (~$0.018/request live, dropping to fractions of a cent with aggregation), and a working self-attestation flow binding a deployed verifying key to a registry record.

I’m now on the model-provenance layer specifically: proving a deployed circuit’s verifying key actually corresponds to published model weights, rather than trusting self-attestation. Published a small, independently-verifiable bundle for a real deployed circuit — reproducible bit-exact against the on-chain VK hash, verified both natively and in Docker:

Still looking for a small number (3-5) of independent people/orgs willing to be named reproducers for the production deployment. Happy to answer questions on the design or the provenance approach.

1 Like

Update 2 — the VK-to-model-identity question from OP, with a number attached

Open question 1 in the OP asked whether there’s a cleaner primitive for binding
a VK to a claimed model. Partial answer, from having had to solve it.

There are two hashes here, and I had been conflating them in my own writeup.
keccak256(vk.key) is what the on-chain passport record is keyed on and what
external reproducers can produce. keccak256(stripCborMetadata(eth_getCode(verifier)))
is what a client can derive from chain state alone without trusting the
provider. Different values, different hash spaces, and each is the only one a
particular party can obtain unaided: an attester who never deploys can produce
the first; a wallet with no model weights can produce the second.

The useful part is the link between them, and how cheap it is. Going
vk.key -> Halo2Verifier.sol -> compiled -> deployed bytecode measured at 0.28s
wall-clock and under 1GB peak RSS on an M4 Pro. It consumes exactly three files
— vk.key, settings.json, srs.bin — and ezkl.create_evm_verifier has no ONNX or
weights parameter at all, so the step is structurally weight-free. Compare
against ~15-100s and 7-8GB for the keygen that produces vk.key in the first
place.

That asymmetry has a consequence I haven’t seen stated: for a closed-weight
model, the second link is the only one an outside party can ever verify.
Reproducing vk.key requires the weights by definition. So a provenance scheme
for commercial models can offer deployment integrity — this bytecode is what
that VK compiles to — but not lineage, and shouldn’t claim otherwise. Whatever
covers the gap has to be behavioural (benchmark attestation against a committed
eval set through the same pinned circuit), not reproductive.

Two phrasings in my Aug 29 update need correcting in light of the above. I
wrote that the bundle was “reproducible bit-exact against the on-chain VK hash,
verified both natively and in Docker,” and that the self-attestation flow binds
“a deployed verifying key to a registry record.” Both were ambiguous between
the two hashes: the registry record is keyed on keccak256(vk.key), and carries
no verifier address at all. The bytecode hash had never been published, so
nobody could check the stronger reading either way — and for a period my own
deployed batch verifier had in fact been generated from an earlier vk.key state
than the one the bundle publishes, which is how I found any of this. That was
fixed on Sept 8 by regenerating and redeploying from the canonical key, with a
fail-closed freshness check so it can’t recur silently. Both hashes are now
published, with a script that reproduces the second link:

Also in that repo, in case it’s useful to anyone doing signed attestations: the
exact byte construction for verifying both proof formats the reproducers used —
a detached JWS with b64: false over RFC 8785 canonical JSON, and EIP-191 over
the same canonical bytes with signer authority resolved through a published
manifest. The near-miss variants that silently canonicalize to different bytes
are documented too, because I hit several of them.

The reproducer ask from that update is closed — three independent
reproductions, all signed. The second link has none yet, and it’s the cheap one
(0.28s, no weights, three files); if anyone wants to be first, the script is in
the repo.

Nothing here is new cryptography — the mechanism is two existing SDK calls.
What was missing was writing down which identifier is canonical and why.

My heads crack of this can you help me how to get easy way

Short version: normally when you pay an API for an AI answer, you have to trust that they ran the model they said they ran. This makes the payment conditional on a cryptographic proof that the exact published model ran on your exact input — no proof, no payment, and you get your money back automatically if the proof never arrives.

The catch is that proving is slow and only affordable for small models today, which the post is fairly blunt about.