ZAP

Documentation

Everything you need to publish tools or build an agent that pays per call.

For Tool Providers

1. Connect your wallet

Go to /dashboard and connect your Solana wallet (Phantom, Solflare). Your tools are tied to your wallet address — only you can manage them.

2. Create a tool

Give it a name, point it to your API URL, set a price, and define the expected request/response format. ZAP wraps your API with x402 payment verification automatically.

// Your API stays exactly as-is. Example:
// GET https://api.yourapp.com/data

// ZAP creates a paid endpoint:
// POST /api/tool/your_tool_name

// Agents call your tool through ZAP:
// 1. Agent signs payment intent (Ed25519)
// 2. ZAP verifies signature + on-chain session
// 3. ZAP proxies request to your API
// 4. ZAP records the call + amount owed to you
// 5. You get paid in USDC at settlement

3. Get paid

Each call is tracked in a Ledger PDA on Solana, specific to your wallet. At settlement, USDC is transferred from the agent's escrow directly to your wallet. No intermediary.

4. Monitor

Your dashboard shows every call, earnings per tool, and agent activity — all filtered to your wallet. Other providers can't see your data.

For Agent Developers

1. Discover tools

Browse /explore or query the API:

GET /api/public/tools
GET /api/public/tools?q=price
GET /api/public/tools?tier=1

2. Open a session (3 on-chain txs)

The agent deposits USDC into an escrow and delegates the session to the Ephemeral Rollup. This is the only time the agent touches the blockchain.

// 1. init_session — deposits USDC into escrow PDA
await program.methods.initSession(new BN(500000)) // $0.50 USDC
  .accounts({ agent: wallet.publicKey })
  .rpc();

// 2. delegate_session — hand off to Ephemeral Rollup
await program.methods.delegateSession()
  .accounts({ payer: wallet.publicKey, pda: sessionPDA })
  .rpc();

// 3. deactivate_session — when done (can be called later)
await program.methods.deactivateSession()
  .accounts({ agent: wallet.publicKey })
  .rpc();

3. Call tools (no on-chain tx)

For each tool call, sign a payment intent with Ed25519. No transaction, no gas. The server verifies instantly and returns the result.

import nacl from "tweetnacl";
import bs58 from "bs58";

const intent = {
  session: "SESSION_PDA_ADDRESS",
  nonce: 1,              // increment per call
  amount: "1000",        // tool price in USDC base units
  resource: "get_price", // tool name
  timestamp: Math.floor(Date.now() / 1000),
};

const message = Buffer.from(JSON.stringify(intent));
const signature = nacl.sign.detached(message, keypair.secretKey);
const header = Buffer.from(JSON.stringify({
  intent,
  signature: Buffer.from(signature).toString("base64"),
  publicKey: bs58.encode(keypair.publicKey.toBytes()),
})).toString("base64");

const res = await fetch("/api/tool/get_price", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "payment-signature": header,
  },
  body: JSON.stringify({ mint: "So111...112" }),
});

const { result, meta } = await res.json();
// result = { found: true, priceUsd: 172.50 }
// meta.payment = { verified: true, onChain: true }

4. Call tools from different providers

One session, multiple providers. Each provider gets their own Ledger PDA tracking what they're owed. The agent doesn't need to know or care.

// Same session, different providers, different prices
await callTool("search_solana_token", { symbol: "SOL" }, "1000");  // Provider A
await callTool("chuck_norris", {}, "5000");                        // Provider B
await callTool("fear_greed_index", {}, "1000");                    // Provider C
await callTool("wallet_scan", { address: "..." }, "10000");        // Provider A

// On-chain ledgers created automatically:
// Ledger [session, Provider A] → $0.011 owed (2 calls)
// Ledger [session, Provider B] → $0.005 owed (1 call)
// Ledger [session, Provider C] → $0.001 owed (1 call)

5. What if the tool returns 402?

Missing or invalid payment signature returns 402 with the price info:

// 402 response:
{
  "error": "Payment required",
  "x402Version": 1,
  "price": "1000",
  "priceLabel": "$0.001"
}

How Settlement Works

ZAP uses Solana Ephemeral Rollups (MagicBlock) and a per-provider Ledger system for multi-provider settlement:

Agent opens session (2 txs)
├─ init_session: deposit USDC → escrow PDA
└─ delegate_session: hand off to Ephemeral Rollup
Agent makes N tool calls (0 txs)
├─ Each call: Ed25519 signature (instant, free)
├─ Server verifies on-chain session exists + active
├─ Server records call on ER (background, ~130ms)
├─ Per-provider Ledger PDA tracks amount owed
└─ Agent gets result in <12ms
Agent closes session (1 tx)
└─ deactivate_session
Settlement (1 tx per provider)
├─ settle_provider: escrow USDC → Provider A
├─ settle_provider: escrow USDC → Provider B
└─ settle_provider: escrow USDC → Provider C
Cleanup
├─ refund: unspent USDC → back to agent
├─ close_ledger × N: recover rent
└─ close_session: recover rent
100 calls, 10 providers = 3 agent txs + 10 settlement txs
vs. x402 standard = 100 txs

Security Model

Agent authorization
Only the session agent can record calls. Enforced on-chain with signer constraint.
Deposit is real
init_session performs an actual USDC transfer to escrow. No self-reported amounts.
Spending limit
total_spent can never exceed deposit. Checked on every record_call.
Provider token validation
Settlement verifies the token account owner matches the provider AND the mint is USDC.
No double settlement
Each ledger has a settled flag. Cannot settle twice.
Refund protection
Refund only works after all providers are settled. Agent can't withdraw before paying.
Force-undelegate auth
Only the session agent can force-undelegate. Prevents griefing.
Rent recovery
close_ledger and close_session return rent to the agent. Both require proper authorization.