Skip to content

Repository files navigation

KAI — autonomous agent payments with WDK

KAI

KAI is a financial permission layer for autonomous AI agents, built for the Aleph WDK Track 1 hackathon. It gives an agent a testnet USD₮ wallet, deterministic spending controls, an auditable transaction trail, and the ability to discover and pay for x402-protected APIs.

Public repository: github.com/Klashka-Group/Aleph-hackathon.

What we built

KAI combines:

  • a WDK-backed EVM wallet initialized from a dedicated test seed;
  • USD₮ balance retrieval, transfers, confirmation polling, and transaction history;
  • a domain- and recipient-aware policy engine enforced before every transfer, with a full human-approval workflow (pending → approve/reject → on-chain execution) for payments above the approval threshold;
  • x402 HTTP 402 discovery, requirement parsing, payment verification, one-time request replay, and audit logging;
  • a Claude-driven autonomous agent loop wired to all of the above — the model calls tools that hit the real wallet, policy, and x402 modules, and transparently retries a paid x402 request without ever tracking a requirement ID or transaction hash itself;
  • demo endpoints for a small approved payment ($0.03) and a large policy-rejected payment ($0.80);
  • a Next.js dashboard and Supabase metadata/audit storage.

Wallet keys never go into Supabase. WDK owns the seed in the server process, while Supabase stores agents, tasks, policies, events, approvals, and transaction metadata.

Status: M0 (Foundation), M1 (WDK Wallet), M2 (Policy Engine & Guardrails), M3 (Agent Runtime), M4 (x402 & Payments), M5 (Dashboard), and M6 (End-to-End Integration) are complete. M7 (Demo & Submission) is in progress.

Project context and acceptance criteria are in INFO/CONTEXT.md, INFO/CHECKLIST.md, and INFO/GITHUB_ISSUES.md.

Why KAI

Technical challenges

  • Safe autonomy at the money boundary. An LLM is probabilistic, while a payment decision must be deterministic. KAI keeps policy enforcement outside the model: recipient and domain rules, per-payment limits, daily budgets, balances, and approval thresholds are re-evaluated immediately before funds can move.
  • A complete x402 payment state machine. The agent must recognize HTTP 402, validate and persist the payment requirement, pay the exact token/recipient/ amount/network, verify confirmation, and replay the original request once without allowing a proof to be reused.
  • Reliable on-chain execution through WDK. The implementation manages seed isolation, wallet lifecycle, six-decimal USD₮ conversion, RPC failures, balance caching, transaction broadcasting, confirmation polling, and durable audit updates without exposing keys to the browser or database.
  • Concurrency and hostile-input safety. A wallet-wide lock prevents nonce and balance races. Internal authentication, same-origin checks, rate limits, strict request validation, SSRF defenses, and fail-closed errors protect the agent's financial tools.
  • Human control without breaking autonomy. Payments above the configured threshold enter an immutable approval workflow; an approval re-checks the remaining budget before on-chain execution and cannot be double-executed.

Engineering depth

KAI connects a Claude tool-calling loop, deterministic policy engine, Tether WDK wallet, EVM transaction lifecycle, x402 protocol adapter, Supabase audit model, secured Next.js route handlers, and a supervision dashboard into one bounded workflow. The difficult parts are handled explicitly: decimal-safe amounts, idempotent approvals and retries, transaction finality, concurrent wallet access, partial failures, secret boundaries, and auditable state transitions.

Functional scope and completion path

The core product flow is implemented: an agent can encounter an x402 resource, evaluate policy, make a WDK-powered USD₮ payment or request human approval, verify it, retry the resource once, and expose the result and audit trail in the dashboard. M0–M5 are complete; M6 and M7 cover final end-to-end hardening and submission work. The remaining plan is concrete: run the full flow against the live configured services, finish automated integration coverage, validate the clean deployment environment, record the demo, and complete submission polish.

Innovation and pain points

Autonomous agents usually face an unsafe choice: they either cannot purchase resources without a person in every loop, or they receive wallet access with insufficient controls. KAI introduces a programmable financial permission layer between agent intent and wallet execution. It combines x402's machine- native paywall with WDK self-custody, deterministic budgets, selective human approval, and evidence-grade auditability. The model can decide what tool it needs, but it cannot decide to bypass the rules governing whether and how money moves.

Practicality and real-world applications

The design uses deployable components—Next.js, Supabase, WDK, EVM JSON-RPC, USD₮, and standard HTTP—so it can be integrated into existing agent products without a custom blockchain. Real-world uses include paying per request for market data, research, inference, storage, compute, licensed content, or other machine services while enforcing team budgets and recipient restrictions. Moving beyond the hackathon MVP mainly requires operational upgrades rather than a redesign: managed secret custody, durable background jobs, shared rate limiting/locks, production monitoring, and the target network's deployment and compliance controls.

WDK integration permalinks

These links are pinned to immutable commit hashes, so their line numbers do not move even as main continues to change.

Pinned to dc1eb83 (M0/M1/M4 — foundation, WDK wallet, x402):

Integration Direct permalink
WDK and EVM wallet-manager imports lib/wdk-client.ts lines 1–2
Seed and RPC validation lib/wdk-client.ts lines 39–59
WDK creation and registerWallet lib/wdk-client.ts lines 68–86
EVM account retrieval lib/wdk-client.ts lines 88–95
Key-material disposal lib/wdk-client.ts lines 97–104
USD₮ base-unit conversion lib/wdk-client.ts lines 106–123
Wallet connection and address lib/wallet.ts lines 94–113
WDK ERC-20 USD₮ balance retrieval lib/wallet.ts lines 115–153
Audited transaction history lib/wallet.ts lines 155–166
Policy check and WDK USD₮ transfer lib/wallet.ts lines 168–221
WDK confirmation and audit update lib/wallet.ts lines 222–264
Wallet/balance API app/api/wallet/route.ts lines 7–21
Transfer API app/api/wallet/transfer/route.ts lines 7–40
Transaction-history API app/api/wallet/transactions/route.ts lines 7–20
WDK CLI wallet creation/export wallet-config/README.md lines 29–61
Sepolia and USD₮ configuration wallet-config/README.md lines 63–98
MCP server setup (wdk-mcp) wallet-config/README.md lines 115–137

Pinned to f5717ff (M2 — policy engine, guardrails, approval workflow):

Integration Direct permalink
Domain- and recipient-aware policy evaluation lib/payment-policy.ts lines 1–61
Guardrails CRUD + daily-spend tracker lib/guardrails.ts lines 1–207
REQUIRES_APPROVAL creates a pending approval, not a rejection lib/wallet.ts lines 301–329
Post-approval on-chain execution lib/wallet.ts lines 351–387
Approve/reject workflow (double-click-safe) lib/approvals.ts lines 1–213

Pinned to 85fde8d (M3 — agent runtime):

Integration Direct permalink
Agent task loop (runAgentTask) lib/agent.ts lines 1–188
Transparent x402 pay-and-retry tool handler lib/agent-tools-handlers.ts lines 243–301

WDK packages and installed versions

Exact versions are taken from package-lock.json.

Package Version How KAI uses it
@tetherto/wdk 1.0.0-beta.16 Core in-process SDK: seed validation, wallet registration, accounts, transfers, balances, and confirmations
@tetherto/wdk-wallet-evm 1.0.0-beta.11 EVM wallet manager registered with WDK for Ethereum Sepolia
@tetherto/wdk-cli 1.0.0-beta.3 Creates, exports, unlocks, inspects, and funds the demo wallet; also bundles optional wdk-mcp tooling

See the declarations in package.json lines 21–23.

Network and token details

Setting Demo value
Chain Ethereum Sepolia testnet
Chain ID 11155111
WDK/CLI network sepolia
x402 CAIP-2 network eip155:11155111
Gas token Sepolia ETH
Payment token USD₮ / USDT, ERC-20, 6 decimals
USD₮ contract 0xd077A400968890Eacc75cdc901F0356c943e4fDb
Default RPC https://ethereum-sepolia-rpc.publicnode.com

We did not deploy a mock USD₮. The demo uses the Sepolia USDT registration exposed by WDK CLI. Verify it with:

npx wdk token info --network sepolia --token usdt --json

sepolia.drpc.org (a common default in other WDK examples) rejects Sepolia RPC calls on its free tier ("chain is not available on free plan") — verified directly against the live endpoint. publicnode's free public RPC works; swap providers if it ever rate-limits you.

This build uses a standard EOA through WalletManagerEvm and direct JSON-RPC. It does not use ERC-4337:

  • BUNDLER_URL: not used;
  • PAYMASTER_URL: not used;
  • bundler/paymaster contract addresses: not applicable.

Setup from a clean clone

1. Prerequisites

  • Git
  • Node.js 22.18.0 or newer
  • npm
  • A Supabase project
  • An Anthropic API key
  • Sepolia ETH and Sepolia USD₮ for the test wallet

Never use a seed phrase connected to real funds.

2. Clone and install exact dependencies

git clone https://github.com/Klashka-Group/Aleph-hackathon.git
cd Aleph-hackathon
npm ci

3. Create the local environment file

macOS/Linux:

cp .env.example .env.local

Windows PowerShell:

Copy-Item .env.example .env.local

Required configuration:

Variable Required Purpose
SUPABASE_URL Yes Supabase project URL
SUPABASE_SECRET_KEY Yes Server-only Supabase secret/service-role credential
ANTHROPIC_API_KEY Yes Claude API access (agent runtime)
WDK_WALLET_SEED Yes Dedicated test-only BIP-39 seed used by WDK
WALLET_ADDRESS Yes Address displayed and funded for the demo
NETWORK Yes sepolia
RPC_URL Yes Ethereum Sepolia JSON-RPC endpoint
USDT_CONTRACT_ADDRESS Yes Sepolia USD₮ contract shown above
USDT_DECIMALS Yes 6
INTERNAL_API_KEY Yes Server key sent as x-agentpay-internal-key to every private route (wallet, guardrails, approvals, agent, x402)
X402_PAYMENT_RECIPIENT x402 demo Testnet address receiving demo payments
X402_NETWORK x402 demo eip155:11155111
X402_ALLOW_PRIVATE_HOSTS Optional Set true only for local testing against non-public hosts; disables the x402 tool's SSRF guard
DEMO_MODE Optional Enables explicit local mock-payment confirmation when true
WDK_INDEXER_BASE_URL Optional Only for WDK CLI/indexer history
WDK_INDEXER_API_KEY Optional Indexer credential

Legacy Supabase names NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are also supported. Keep secrets only in the git-ignored .env.local file.

4. Create and fund the WDK test wallet

npm run wallet:create
npm run wallet:unlock
npx wdk wallet export --name agentpay-test
npm run wallet:address

Copy the exported seed into WDK_WALLET_SEED. Fund the reported address with Sepolia ETH for gas and Sepolia USD₮, then verify:

npx wdk get balance --network sepolia --wallet agentpay-test --json
npx wdk get balance --network sepolia --token usdt --wallet agentpay-test --json

See wallet-config/README.md for the complete, testnet-only wallet walkthrough.

5. Create the database

Run these files in the Supabase SQL editor, in order:

  1. supabase/schema.sql
  2. supabase/migrations/20260822203000_x402_support.sql
  3. Optional demo data: supabase/seed.sql

6. Run and verify

npm run dev

Open http://localhost:3000. Check the foundation at http://localhost:3000/api/health.

npm test
npm run typecheck
npm run lint
npm run build

Binance Spot Testnet demo

KAi can simulate Spot MARKET BUY and SELL operations using a dedicated Binance Spot Testnet account. It never accepts Binance Mainnet endpoints, withdrawals, margin, futures, or leverage. Configure the demo-only credentials in the git-ignored .env.local; never commit them. The full key setup, demo prompts, audit events, and x402/WDK separation are documented in docs/binance-testnet-demo.md.

API routes

All routes below are private server-side tools and require the x-agentpay-internal-key header. The deliberate exception is GET /api/paid-data: it represents the external merchant API that returns HTTP 402, so the agent's internal credential must not unlock it.

GET  /api/wallet?agentId=<uuid>                 # wallet metadata + cached USD₮ balance
GET  /api/wallet/transactions?agentId=<uuid>    # latest 50 audited transfers
POST /api/wallet/transfer                       # policy-checked USD₮ transfer

GET  /api/guardrails?agentId=<uuid>             # current guardrails + today's spending
PATCH /api/guardrails?agentId=<uuid>            # partial guardrail update

GET  /api/approvals?agentId=&status=            # list approvals (default status=PENDING)
POST /api/approvals/[id]                        # approve/reject and resume the paused task
POST /api/approvals/[id]/approve                # backward-compatible approval alias
POST /api/approvals/[id]/reject                 # backward-compatible rejection alias

POST /api/agent/task                            # create + synchronously run a task to completion/pause/failure
GET  /api/agent/status?agentId=<uuid>           # agent state, latest task, pending approvals, recent activity
GET  /api/events?agentId=<uuid>&after=<ISO>     # recent events or incremental polling updates

POST /api/x402-request                          # discovers and stores an upstream 402
POST /api/x402-retry                            # verifies payment and replays once
POST /api/x402-demo                             # resets/runs the demonstration scenario
POST /api/paid-data/reset                       # resets the simulated merchant
GET  /api/paid-data                             # public simulated paid merchant API

Example wallet calls:

curl "http://localhost:3000/api/wallet?agentId=AGENT_UUID" \
  -H "x-agentpay-internal-key: YOUR_INTERNAL_API_KEY"

curl -X POST "http://localhost:3000/api/wallet/transfer" \
  -H "content-type: application/json" \
  -H "x-agentpay-internal-key: YOUR_INTERNAL_API_KEY" \
  -d '{"agentId":"AGENT_UUID","recipient":"0x...","amount":"0.03","type":"x402_payment"}'

Example task call:

curl -X POST "http://localhost:3000/api/agent/task" \
  -H "content-type: application/json" \
  -H "x-agentpay-internal-key: YOUR_INTERNAL_API_KEY" \
  -d '{"agentId":"AGENT_UUID","title":"Research ETH market activity. Your task budget is $0.50."}'

Agent runtime

lib/agent.ts's runAgentTask(taskId) loads the task + agent, builds the system prompt (lib/system-prompt.ts) with the agent's real daily budget and approval threshold, then calls Claude with the 6 tools from lib/agent-tools.ts in a loop (50-iteration cap), dispatching each tool call via lib/agent-tools-handlers.ts to the real wallet/policy/x402 modules.

  • check_policy re-evaluates the domain-aware policy engine and logs policy_evaluation_started/policy_approved/policy_rejected/ policy_requires_approval events.
  • send_payment calls sendUsdtPayment — on AWAITING_APPROVAL it does not fail the task; it stops the loop cleanly (the agent's status was already flipped by lib/wallet.ts) and returns {outcome:"AWAITING_APPROVAL", ...}. The loop persists its messages and pending x402 requirements in the task's JSON checkpoint. POST /api/approvals/[id] records the immutable human decision, executes an approved transfer, restores that checkpoint, and resumes the same task without submitting the payment a second time.
  • make_http_request transparently retries a paid x402 request: it tracks pending unpaid requirements per URL in-memory for the task's lifetime. A matching send_payment is tagged as x402_payment, carries the paid API's domain into the second policy check, and stores its confirmed hash on that pending requirement. When the model calls the same URL again, the handler verifies and replays the original request via lib/x402/service.ts's retryX402Payment automatically. The model never sees a requirement ID or transaction hash; it just calls make_http_request again, exactly as lib/system-prompt.ts instructs.

Any unexpected failure anywhere in the loop (a Claude API error the bounded retry couldn't recover, a tool-glue bug, etc.) is caught by one outer try/catch that marks the task FAILED, the agent ERROR, and logs task_failed — a task can never end up stuck RUNNING with no audit trail.

M6 x402 end-to-end verification

npm test includes deterministic integration coverage for the complete 402 → policy approval → payment → verified replay → data sequence, including the event order consumed by the dashboard. It also exercises the real x402 parser/store/replay service against a simulated HTTP merchant boundary.

Run the live smoke test against a configured local or deployed instance to exercise the real LLM, Supabase, WDK test wallet, paid endpoint, and dashboard status API. The command fails if the task does not complete, any required activity event is missing or out of order, or the flow takes 60 seconds or longer. It performs a WDK balance preflight and requires at least 0.03 USD₮ plus enough Sepolia ETH for gas:

AGENTPAY_BASE_URL=http://localhost:3000 \
AGENTPAY_AGENT_ID=YOUR_AGENT_UUID \
INTERNAL_API_KEY=YOUR_INTERNAL_API_KEY \
npx tsx scripts/x402-live-smoke.mts

Dashboard

app/dashboard/* (Overview, Tasks, Wallet, Guardrails, Activity, Approvals) lets a non-technical user run and supervise the agent without touching the API directly or understanding crypto. Visit /dashboard after npm run dev.

Architecture note: the dashboard never calls the INTERNAL_API_KEY-gated API routes from the browser — doing so would require embedding that server-only secret in client JS. Instead:

  • All reads are React Server Components that import lib/wallet.ts, lib/guardrails.ts, lib/approvals.ts, and lib/dashboard-data.ts directly, in-process, with zero HTTP round-trip and zero exposed secrets.
  • Browser-started tasks use the session-cookie-gated POST /api/agent/task/stream route, which validates wallet ownership and streams NDJSON deltas to the chat without exposing INTERNAL_API_KEY.
  • Approving/rejecting a payment, updating guardrails, creating an agent, and saving encrypted per-agent LLM settings use authenticated Server Actions (lib/dashboard-actions.ts, "use server").
  • "Real-time" updates (#30) are a router.refresh() poll every 4s (components/dashboard/auto-refresh.tsx), re-running the Server Components for the current route without a full reload. Chosen over Supabase Realtime/SSE for simplicity — the tables have zero anon-role RLS policies by design (service-role only), and standing up a second real-time channel wasn't worth it for a few seconds of latency nobody notices in a demo. Agent text itself arrives immediately through the streaming task response.
  • Task execution is still request-bound; there is no durable background-job queue in this MVP, even though the chat renders progress incrementally.

The existing internal-key-gated API routes are untouched and still serve their original purpose: the agent runtime's own tool calls, x402 demo endpoints, and any external tooling.

Security notes

  • Use a dedicated wallet with testnet funds only. Never commit .env.local, seed phrases, private keys, Supabase secrets, or internal API keys.
  • WDK key material stays in server memory and is explicitly disposed through the wrapper; it is never persisted in Supabase.
  • Server-to-server financial, x402, guardrail, approval, and agent routes fail closed with a constant-time internal-key check. Browser-facing auth/task routes use signed wallet sessions plus ownership and same-origin checks. Sensitive routes limit JSON body size, reject unknown fields, and return no-store responses.
  • Transfers validate identifiers and monetary strings, then re-read recipient restrictions, policy limits (including domain allow/block lists), daily spending, and balance immediately before WDK is invoked. A missing or ambiguous policy denies payment.
  • A human's approval decision is immutable and distinct from execution outcome: rejecting a payment never touches the chain; approving one re-checks the daily budget (not recipient/domain/per-tx — those were the human's call) before broadcasting, and decideApproval is double-click-safe (an already-decided approval returns 409, not a second execution).
  • A process-wide wallet lock serializes transfers because all agent records share one physical WDK account, preventing concurrent nonce/balance races.
  • The x402 HTTP tool resolves target DNS and blocks credentials plus localhost, private, link-local, and multicast destinations to mitigate SSRF.
  • x402 retries bind a proof to the expected amount, token, recipient, and network; each confirmed payment can unlock only one replay.
  • Rate limiting is in-process for this hackathon runtime. A multi-instance production deployment should use a shared edge or datastore limiter.

WDK references: Core SDK documentation, EVM wallet documentation, and the WDK CLI guide.

License

Built for the Tether WDK Track 1 hackathon.

Releases

Packages

Contributors

Languages