Search projects, hackathons, sponsors and tech

    ← All projects

    PayGuard

    An on-chain vault for AI agent payments: auto-pay small bills, ask for big ones, block the rest.

    Demo

    About

    An AI agent that can pay is an agent that can empty the account. PayGuard is the vault in between. The owner deposits funds into a per-owner PayGuardVault and gives an agent bounded authority instead of a key: a hard total budget, an automatic limit, an escalation limit, and a list of merchants and categories it may pay. The agent never holds funds. For each payment, the merchant signs an invoice, the agent signs an intent, and the vault evaluates both on-chain before anything moves: - ALLOW: small, in-policy bills settle on their own (demo: 0.50). - ESCALATE: a larger valid bill waits until the owner signs that exact payment (demo: 180). The signature binds merchant, amount and route, so it cannot be reused or redirected. - BLOCK: anything outside the rules (over budget, unknown merchant, duplicate invoice) never leaves the vault. No transaction is sent (demo: 500). The merchant always receives the exact invoiced amount. The owner can pay through three routes, each with its own vault and budget: a direct token transfer, a Uniswap v4 exact-output swap, or a 1inch Aqua/SwapVM swap. So an agent can pay in USDC while the owner's funds stay in another asset. The owner panel shows the remaining budget, the policy as read back from the vault, pending approvals, and activity that separates the policy's decision from what actually happened on-chain. The screen never decides anything. It shows what the vault decided. The relayer marks a payment paid only after a receipt exists. Built by the SuzuPay team (GustoDevelopment), which runs a live QR payment service with 100+ merchants in Fukuoka. PayGuard is the safety layer we want in front of those merchants as they start accepting agent payments.

    How it's made

    pnpm monorepo: Solidity contracts (Foundry), a Fastify API, a worker/relayer, PostgreSQL and a React 19 + Vite owner panel. Contract ABIs and EIP-712 types are generated once and shared, with drift tests so the frontend, backend and chain cannot disagree. Contracts. PayGuardVault (contracts/core-v4) holds owner funds and runs the whole decision on-chain: policy lookup, merchant/category bitmap, automatic and escalation limits, a hard budget, per-agent nonces and consumed-invoice tracking for replay defense. Invoices, intents and owner approvals are EIP-712 typed data. Owner approvals also accept ERC-1271 smart-wallet signatures. Settlement is atomic: if the route fails, the budget and state roll back. Foundry tests cover budgets, approvals, replays, negative evaluation, a malicious reentrant adapter, and invariants. Uniswap v4. PayGuardV4Adapter (contracts/core-v4/src/adapters) does an exact-output swap through the real PoolManager unlock/callback flow and settles all deltas. The vault caps the input it may spend, and the merchant gets exactly the invoiced output. The adapter is pinned to one pool and refuses hooks and non-standard fees, so a policy cannot be routed into an unexpected pool. 1inch Aqua / SwapVM. PayGuardAquaAdapter (contracts/aqua) is a taker-side adapter over AquaSwapVMRouter. It is bound to one maker and one SwapVM order, whose hash is computed at deployment, and uses SwapVM's exact-output max-input check. Its taker callback only accepts calls from the pinned router during its own settle(), and it leaves no allowance to Aqua after the call. Backend. The API handles SIWE sessions, policies, invoices, intents and approvals. The worker is a relayer with a transaction outbox and journal, crash recovery and a chain indexer that handles reorgs. It never treats HTTP 202 or a bare tx hash as success, only a verified receipt. The API and worker refuse to boot if an owner private key is present in the environment. Frontend. Every state comes from the API and the chain. Before the owner signs an approval, the panel recomputes the EIP-712 digest and checks chainId, verifying contract, signer, amount and recipient. Amounts are bigint strings throughout (6-decimal mUSDC and 18-decimal assets). Hacky bits. The API sends no CORS headers, so the panel runs behind a same-origin proxy (the Vite proxy in dev, a zero-dependency server.mjs in prod). A mock API lets us iterate on the UI without Postgres or Anvil. The demo runs on a local chain (31337) with mock tokens. No real funds move.