An on-chain marketplace where AI agents pay USDC for legacy APIs via x402, and where independent providers resell that infrastructure with reputation staked on-chain.
AgentGate is a two-sided marketplace for AI agents and API infrastructure providers. Agents discover providers on-chain by reputation, price, or latency, and pay per call in USDC — no API keys, no signups, no subscriptions. Providers stake USDC to publish their services, earn 95% of every call, and accumulate on-chain reputation that compounds over time. The gateway sits between them as a thin router that handles x402 settlement, request forwarding, and attestation logging — it holds no API keys and no funds in flight.
Deployed on Kite Chain testnet (chain ID 2368), settled via the Pieverse x402 facilitator, paid in USDC (transferWithAuthorization / EIP-3009).
Live demo: agent-gate.vercel.app
- Quick start
- Agent Flow
- Provider Flow
- How to be a provider
- Architecture Diagram
- Demo and examples
- Why this isn't just "yet another x402 demo"
- Running tests
- Smart contracts
- x402 details
- Repository layout
- Adding a new provider
- Deployment notes
- AgentGate V2 roadmap
- Acknowledgements
- Node 20+,
pnpm - Foundry (
forge,cast) - A Kite Chain testnet wallet funded with KITE for gas (faucet)
git clone https://github.com/Antrikshgwal/Agent-gate.git
cd Agent-gate
cp .env.example .env # fill in keys + addresses (or use the testnet defaults)
make install # installs every workspace
make demo # boots gateway + 3 providers + frontend in one terminalmake demo starts (with prefixed logs):
| Process | Port |
|---|---|
| gateway | :3000 |
| OpenWeather primary | :4001 |
| OpenWeather budget | :4002 |
| CoinGecko | :4003 |
| frontend | :3001 |
Visit http://localhost:3001 and click around. Ctrl+C kills the whole group.
cd sdk && npx tsx examples/weather.tsYou'll get JSON weather data plus three tx hashes (payment.transaction, distribution_tx_hash, attestation_tx_hash) — all settled on Kite testnet.
An agent that wants to call a paid API doesn't sign up, doesn't hold an API key, and doesn't pre-fund anything. It discovers a provider on-chain, pays per call in USDC via x402, and the call returns with cryptographic proof of payment.
Every successful call increments the agent's reputation score and the provider's successfulCalls; every failure increments failedCalls. Reputation is computed live on-chain:
reputation = 700 · successRate + 200 · ageBonus + 100 · volumeBonus
A provider is anyone who owns a Kite Chain wallet, has an upstream API they want to resell, and is willing to stake 100 USDC for credibility. The flow is symmetric to the agent flow — you publish, you serve, you get paid.
The provider's server is the only place the upstream API key lives. The gateway speaks one shared wire format (POST /v1/call) to every provider — adding a new upstream is roughly 80 lines of Express.
Three steps. Total time: under 10 minutes once you have a wallet funded.
1. Fork the template.
cp -r providers/template providers/my-service
cd providers/my-serviceEdit src/server.ts: set SERVICE_NAME, list the methods you support in SUPPORTED_METHODS, and call your upstream API from the dispatcher. Set AGENTGATE_GATEWAY_SECRET and any upstream API keys via env vars.
2. Deploy your server.
Any Node host works — Railway, Render, Fly.io, your own box. The gateway just needs an HTTPS URL it can POST /v1/call to.
3. Register on-chain.
Open /register, connect your provider wallet, fill in name + endpoint + price + stake. Submit. The gateway starts routing the moment registerService is mined.
You can also call registerService directly via cast — the UI is a convenience, not a gate.
The gateway is intentionally thin: it does not hold API keys, does not custody funds, and does not arbitrate disputes. Everything material — discovery, payment, reputation — lives in the four contracts on the right.
| Surface | URL |
|---|---|
| Frontend | agent-gate.vercel.app |
| Gateway API | gateway-production-1f1c.up.railway.app |
| Block explorer | testnet.kitescan.ai |
/— landing with live ticker of recent paid calls/services— every registered provider with price, uptime, stake, and which onefindProviderwould pick under each strategy/services/[id]— single-provider detail with success/failure mix/playground— fire a paid call as a demo agent and watch the 5-step pipeline run live (discover → quote → sign → settle → attest)/agents— agent launchpad with JSON-LD discovery feed/agents/[did]— single-agent dashboard: reputation breakdown + attestation history/register— provider registration form (wallet-connected)⌘K— command palette with quick presets (Weather in London / Tokyo, BTC + ETH price)
import { AgentGateClient, findProvider } from "@agentgate/sdk";
const svc = await findProvider({ name: "OpenWeather", strategy: "best_reputation" });
const client = new AgentGateClient({ privateKey, gatewayUrl });
const res = await client.call(svc.id, "get_current_weather", { city: "London" });
console.log(res.data, res.payment.transaction);Most x402 examples are a single server gating one endpoint. AgentGate adds the three pieces that make a marketplace:
-
On-chain discovery + reputation.
ServiceRegistryis a real directory. Every successful or failed call updatestotalCalls / successfulCallsfor the provider andreputationScorefor the agent. The SDK ships afindProvider({ name, strategy })helper so agents pick bycheapest,best_reputation, orfirst_match— and the/servicespage mirrors that exact logic so you can see which one wins at a glance. -
Payment splitting in a contract, not in the gateway.
payToon the 402 quote is thePaymentSplitter. The gateway settles into the splitter, then callsdistribute(serviceId, amount), which reads the provider out ofServiceRegistryand fans out 95% / 5%. No off-chain bookkeeping, no gateway holding funds in flight. -
Provider HTTP contract that any operator can implement in ~80 lines.
POST /v1/callwith{ method, params },X-AgentGate-Authshared secret, returns{ success, data, error }. The gateway speaks one wire format to every provider; the gateway code does not change when you add a new upstream API.
cd contracts
forge test -vvCoverage is heaviest on PaymentSplitter (rounding, drain-to-zero invariants) and AttestationLogger (only-owner gates, double-attestation prevention).
make typecheck # runs tsc --noEmit across gateway, sdk, frontend, providersThere are no JS unit tests — the SDK is exercised end-to-end by make seed and make demo, which hit live testnet contracts.
All four are pure-Solidity, no upgradability, no proxies. Foundry tests live in contracts/test/.
registerService(name, endpoint, schemaHash, pricePerCall, stake, SLA)— pulls USDC stake viatransferFrom.MIN_STAKE = 100 USDC.updateEndpoint(id, newEndpoint)— provider-only, lets ops rotate the backend without re-staking.getAllServices()— frontend / SDK discovery feed.updateServiceStats(id, success)— only callable byAttestationLogger.
registerAgent(did)— DID is abytes32derived from the agent's Kite Passport identity. Starts at neutral reputation (500/1000).- Reputation = 700·successRate + 200·age + 100·volume, recomputed on every attestation.
- The only contract the gateway needs
onlyOwneraccess to. Each call writes a single attestation and updates both registries. - This is what makes reputation tamper-evident: the gateway can't fake a successful call, only log what actually happened.
distribute(serviceId, amount)— gateway-owned; reads the provider out ofServiceRegistry, transfers 95% to them and 5% to the protocol treasury. Rounding favors the protocol so the splitter is always drained to zero per call (verified inPaymentSplitter.t.sol).sweep()— owner-only escape hatch for residual dust.
| Contract | Address |
|---|---|
ServiceRegistry |
0xF1b879E314e05ED8d6381AaF30793f2B204C39B2 |
AgentRegistry |
0x29527d55D2Fe6d777378920581A9cc2F76eEe6ba |
AttestationLogger |
0xBf8319a66b4079D07f1230a7222882b6218c565b |
PaymentSplitter |
0x77C268964E38CeB6b654D36eB4aBedE82B615f4b |
USDC (MockUSDC) |
0x0309764915AFC7a2a7CDd1E64c58a57c1F1705E3 |
| Gateway wallet | 0xe5A044f3d61D2381bded585AC357be9d9e8aD564 |
We implement x402 v2 against Pieverse (https://facilitator.pieverse.io).
- Network is CAIP-2:
eip155:2368(Pieverse's expected format). - Quote envelope shape:
{ x402Version, paymentPayload: { x402Version, payload, accepted }, paymentRequirements }—acceptedmirrorspaymentRequirements. The shape was reverse-engineered from Pieverse's Python lib; bothgateway/src/x402/andsdk/src/agree on it. - Asset is the deployed MockUSDC. EIP-3009
transferWithAuthorizationis signed by the agent and submitted by Pieverse — the gateway never holds the agent's signature beyond settlement. payToon every 402 quote is the PaymentSplitter address, not the gateway. The gateway has no claim on funds in flight.
agent-gate/
├── contracts/ Foundry workspace
│ ├── src/ ServiceRegistry, AgentRegistry, AttestationLogger, PaymentSplitter
│ ├── test/ forge tests (10+ for splitter alone)
│ ├── script/Deploy.s.sol deploys all four with correct wiring
│ └── deployments/2368.json addresses for Kite testnet
├── gateway/ Express + ethers.js
│ ├── src/routes/call.ts 402 → settle → forward → split → attest
│ ├── src/blockchain/ on-chain reads + write helpers
│ └── src/x402/facilitator.ts Pieverse client
├── providers/
│ ├── template/ 60-line forkable skeleton
│ ├── openweather/ reference provider (paid upstream)
│ ├── coingecko/ reference provider (free upstream)
│ └── README.md gateway↔provider RPC contract
├── sdk/ TypeScript client
│ ├── src/client.ts AgentGateClient.call (x402 round-trip)
│ ├── src/discovery.ts findProvider({ name, strategy })
│ ├── src/sign.ts EIP-3009 signing helpers
│ └── examples/weather.ts end-to-end demo call
├── frontend/ Next.js 14 App Router
│ ├── app/page.tsx landing
│ ├── app/services/ browse + strategy-aware selection UI
│ ├── app/playground/ live demo agent runner
│ ├── app/register/ on-chain provider registration form
│ ├── app/agents/[did]/ agent dashboards
│ └── lib/discovery.ts mirrors sdk/src/discovery.ts exactly
├── Makefile install / build / typecheck / demo / seed
├── DEPLOY.md Vercel + Railway deployment runbook
└── .env single source of truth for addresses + keys
This is the path you'd take in production. The gateway code does not change.
cp -r providers/template providers/my-service
cd providers/my-service
# 1. Edit src/server.ts: rename SERVICE_NAME, add methods to the dispatcher,
# call your upstream API.
# 2. Set MY_UPSTREAM_API_KEY-style env vars + AGENTGATE_GATEWAY_SECRET.
pnpm install && pnpm dev
# 3. Deploy publicly (Railway, Render, Fly — any Node host).
# 4. Go to /register, connect provider wallet, fill in
# name / endpoint / pricePerCall / stake / SLA. Submit.The gateway starts routing the moment registerService is mined.
Full step-by-step runbook in DEPLOY.md. Short version:
| Component | Host | Notes |
|---|---|---|
| Frontend (Next.js) | Vercel | vercel --prod from frontend/. All NEXT_PUBLIC_* vars must be set before build (Next.js inlines them at compile time). |
| Gateway + 3 providers | Railway | One project, four services. Each service has its own root directory. Use railway up <path> --path-as-root --service <name> since Railpack analyzes from the upload root. |
| Contracts | Kite Chain testnet | forge script script/Deploy.s.sol --broadcast. Addresses land in contracts/deployments/2368.json. |
A few hard-learned gotchas:
- Next.js +
process.env. Dynamic property access (process.env[name]) does not get inlined by the Next.js compiler — only literal property access (process.env.NEXT_PUBLIC_FOO) does. The frontend'slib/env.tsis structured around this. useSearchParamsin App Router needs a Suspense boundary or the production build will fail at the prerender step.- Railway monorepo deploys ignore
source.rootDirectorywhen you userailway upfrom the linked project root. Eithercdinto the sub-app first or pass--path-as-root. - Provider endpoints are on-chain. If you redeploy a provider to a new URL, the provider wallet must call
ServiceRegistry.updateEndpoint(id, newUrl)— the gateway reads endpoints live from chain, not from config.
The current build is a working v1. The next slice tightens trust and broadens what the marketplace can support:
- Per-provider rotating secrets fetched from a KMS at gateway startup, replacing the shared
AGENTGATE_GATEWAY_SECRET. - SLA oracle + automatic slashing.
ServiceRegistry.slashStakeis implemented but gated on owner; v2 wires it to an off-chain SLA monitor that watches latency + uptime and slashes on threshold breach. - Agent passport integration. Today
AGENT_DIDis configured manually; v2 reads it from the local Kite Passport CLI daemon via the agent's passkey. - Real USDC on mainnet. MockUSDC is a stand-in because Kite testnet doesn't ship a 6-decimal USDC. Production swap is a one-line
.envchange once the canonical address ships. - Subscription / streaming payments. x402 is per-call; some upstreams (LLM streaming, video) want per-second metering. Either x402.5 or a sidecar payment channel.
- Provider operator-vs-treasury split. Splitter owner is currently the gateway wallet; v2 separates "who runs the gateway" from "who owns the 5% protocol fee."
- Kite AI for the chain + Passport SDK
- Pieverse for the x402 v2 facilitator (and for shipping the Python reference that let us reverse-engineer the envelope shape)
- OpenZeppelin for the contract primitives
- shadcn / Tailwind / Next.js for the frontend stack
- Railway + Vercel for the hosting

