Turnstile: a market where AI agents pay per query
Deep DiveAI AgentsAgent Paymentsx402

Turnstile: a market where AI agents pay per query

Agents that find a data seller through ENS, read its price on chain, pay per query over x402 and spend inside a mandate they cannot widen. Overview of the nine-part series.

Ignacio Pastor Sanchez
October 14, 2026
10 min read

TL;DR

Turnstile is a market where a seller publishes a priced service at an ENS name, and a buyer's agent discovers it, reads the price from the chain, gets 402 Payment Required, pays per query in HBAR on Hedera or USDC on Arc, and gets an answer, inside a spending limit the agent itself can never raise.

Built solo in September 2026 for ETHOnline 2026 hackathon and won two prizes: Hedera's AI and agentic payments prize ($2,000) and second place in The Graph's best AI tooling category ($1,500).

This post is the overview. The nine deep dives, in reading order:

  1. x402 on Hedera: what a paid HTTP request is made of
  2. Gasless nanopayments on Arc, and the nonce that stays at 0
  3. One payment interface, four chains
  4. 197 ERC-8004 agents, and where their prices live
  5. The offer is the name: ENSv2 records and a key split the chain enforces
  6. Three listings per human: capping an open agent registry with World ID Selfie Check
  7. Spending mandates for agents: the key that spends can never raise its own limit
  8. Sell the answer, keep the method: Chainlink CRE Confidential Workflows
  9. Selling a liquidity verdict: The Graph, Uniswap QuoterV2, and the gap between TVL and depth

The problem: 197 agents, no standard price

AI agents now have a standard way to register themselves on chain, ERC-8004. I read every registration I could find across Base, Ethereum mainnet and Sepolia, 197 of them, and asked one question of each: can a buyer's program learn what this agent costs before it pays?

Two could, and neither through the standard, because the registration format has no price field. Mine publishes its price in an ENS record. One other agent, Capminal, quotes a price in the 402 its endpoint returns. 76 Virtuals agents list a number in the jobOfferings field of ACP, Virtuals' Agent Commerce Protocol, with no currency attached. Most of the rest set an x402Support flag, which says they take payment, with no working endpoint to ask. So agents can find each other, but they cannot buy anything from each other in a way a program can reason about.

Two more gaps sit behind that one.

  • A seller whose value is its analysis has to choose between publishing the method and asking buyers to take the answer on trust.
  • The usual way to let an agent pay is a private key in its environment, which can spend whatever sits behind it.

The Turnstile market page listing 197 real ERC-8004 registrations, with one price posted on chain, 97 agents marked askable and 99 with no price The market page counts only the prices it can read. It misses the Capminal quote and the Virtuals prices; Part 4 explains why.

What I built

Turnstile is a market where a seller publishes a priced service at an ENS name, and the resolver record is the offer: price, payment rails and endpoint, read live on-chain on every request. A buyer's agent finds the seller, reads the price, gets a 402 Payment Required, pays per query in HBAR on Hedera or USDC on Arc, and gets an answer.

To prove the whole loop, the market needed one seller worth paying. For Turnstile, it is the Liquidity Analyst at liquidity.turnstile.eth, and it answers one question about a Uniswap pool: is it safe to LP? It reads the pool's history from a Messari-standard subgraph on The Graph and quotes live trades against Uniswap's QuoterV2. It then returns ACCEPTABLE, CAUTION or AVOID, with a confidence score and the numbers behind each signal. Ranked by TVL, the top pool on our own subgraph was a fake USDT holding two trillion of itself and none of the real one, and it couldn't fill a $1,000 trade. That gap between what an index reports and what a trade can actually do is what the seller charges seven cents to find.

The buyer's organisation gives that agent a spending mandate, and the agent can never raise its own limit. Built it solo in September 2026 for ETHOnline 2026, and it is deployed at turnstile.moveseventyeight.com.

Architecture: discover, price, pay, answer

The request path has six steps.

flowchart TD
  A["01 Discover<br/>ERC-8004 · Substreams"] --> B["02 Read the offer<br/>ENSv2 resolver · turnstile:price"]
  B --> C["03 Ask<br/>MCP"]
  C --> D["04 402<br/>quote"]
  D --> E["05 Pay<br/>x402 on Hedera · USDC on Arc"]
  E --> F["06 Answer<br/>the result, not the method"]
  1. Discover. A Rust Substreams module normalises ERC-8004 agent registrations. The market and an MCP tool query the same discovery engine.
  2. Read the offer. The seller's ENSv2 subname, liquidity.turnstile.eth on Sepolia, carries its price and endpoint in standard ENSIP-25/26 records. Nothing is cached or hard-coded.
  3. Ask. A buyer's agent calls the seller through mcp-turnstile, a four-tool MCP server with a SKILL.md. It has no code path for any particular seller.
  4. 402. The paid service refuses unpaid requests with a quote.
  5. Pay. A PaymentRail seam with two implementations: x402 on Hedera settled through Blocky402, and Circle Gateway nanopayments in USDC on Arc. The same query settles identically over either.
  6. Answer. The seller's Liquidity Analyst answers one question, "is this pool safe to LP?", and returns the verdict, not the calibration behind it.

Turnstile architecture: the three key tiers, and the request path

The three key tiers

The design rests on one invariant: the key that spends can never raise its own limit. Authority is split across three tiers.

Tier Holds May authorise
Cold (a separate operator key) The seller's identity; owns the ENS name Rotating the hot key, changing the payout address, raising the price ceiling
Warm (Privy) The buyer organisation's wallet and mandate policy Funding the agent (depositFor, to that agent only, up to a cap, paying the gas), issuing a mandate, raising a cap (needs a quorum), adding an agent
Hot (Circle, Arc) The buyer agent's spending wallet Nothing. It spends inside the mandate and signs offchain only

Each tier is enforced where it lives:

  • On Sepolia, the resolver behind the seller's ENS name grants roles per record. The seller's own hot key, which updates its offer, may write only the price and endpoint records, so its setAddr call to change the payout address reverts with EACUnauthorizedAccountRoles. A Forge test forked from live Sepolia covers that revert.
  • In Privy, a 1-of-2 operations quorum owns the org wallet and a separate 2-of-2 board quorum owns the mandate policy, so raising a cap needs both board signatures and one alone gets a 401.
  • On Arc, the spending agent only signs EIP-3009 authorisations and Circle Gateway submits them, so after eleven payments the agent's nonce is still 0.

Key decisions and tradeoffs

1. The offer lives in the ENS record, not in the API. Price, rails and endpoint are standard ENSIP keys on the seller's name, read from Sepolia on every page load. Anyone can read an offer without trusting Turnstile, and the market cannot serve a stale price. The cost is a live RPC dependency: without SEPOLIA_RPC_URL the seller page shows nothing.

2. Split authority by role, and leave custody for later. The three tiers are separate: the chain and Privy decide which key can do what, so the hot key can't move money it doesn't control. Custody is the next step. The cold key is still an ordinary software key, not stored in a hardware wallet, so the split protects against a leaked hot key but not against a compromised operator machine. Making it cold in practice means generating it offline and signing on an air-gapped device. I planned that around Ledger's key ring, which my only device turned out not to support.

3. Rail-agnostic Settlement sits behind one PaymentRail interface. That keeps adding extra rails cheap and made "the same query settles on both rails" easy to implement. The rails still behave differently, because Hedera and Circle implement 402 payments differently: on Hedera, a facilitator settles each payment as it arrives, while Circle collects payments and settles them in batches.

4. Sell the answer, keep the method. The analyst's scoring runs in a Chainlink CRE confidential workflow. It is designed to run inside an enclave, with the seller's calibration held as a secret. What reaches chain is the rating, the confidence, which checks failed, and a hash of the evidence bundle. A buyer can check that the verdict commits to the exact bytes they paid for, without being able to reproduce it. The workflow runs in Chainlink's simulator, but deploying it to the oracle network needs two approvals Chainlink hadn't granted by the deadline: deploy access, and the Confidential Workflows private beta. So the verdict on Sepolia was written by my key, from the same scoring run outside the enclave. It shows the buyer's check works, but nothing has been attested yet.

What shipped, with receipts

Every item here links to a transaction, a live endpoint or a test. The project's EVIDENCE.md has one row per claim.

  • Live product: turnstile.moveseventyeight.com. The paid endpoint answers 402 Payment Required unpaid. The MCP URL the ENS record publishes serves the tools over HTTP/SSE.
  • Paid request on Hedera through Blocky402: 0.84 HBAR ($0.07), buyer gas zero (HashScan).
  • The same query over both rails in one run, same verdict (HashScan).
  • Nanopayments batched on Arc: six $0.0005 queries and one $0.07 query settled in one transaction (ArcScan), then a second batch (ArcScan).
  • Zero-gas spending agent: nonce 0 after eleven payments (ArcScan).
  • Hot key cannot move the payout: reverts on live Sepolia, pinned by a fork test.
  • Mandate approval that refuses: a cap raise fails with one Privy signature and passes with two. It is on camera in the demo video.
  • A World-verified seller: I ran World's Selfie Check for our seller from the World Sandbox App, through the site's /onboard page. The server verifies the proof with World's Developer Portal, keeps only the nullifier (a per-app pseudonym for the human), and stores it under the listing's ERC-8004 agent id, which is what the market reads. The market now lists our seller as verified. One human, one proof so far; the three-listing cap is covered by tests, not by more real proofs.
  • One mainnet payment: $0.07 in USDC on Base through the Bazantic gateway, paid from my own wallet to test the listing end to end (BaseScan).
  • The Graph: a Messari DEX AMM-conformant subgraph live on Studio, and an authored Substreams module for ERC-8004 registrations.
  • Bonus: A defect reported upstream: Uniswap's agent-tooling repo told agents to use an ethers v5 call that no longer exists. I filed the issue; a maintainer wrote and merged the fix the same day.

Sources and evidence