Skip to content

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

23 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WARD — Confidential Agent-Spending Treasury

Safe spending rails for AI agents on Solana.

What it is

WARD is a non-custodial treasury primitive built in Pinocchio on Solana. A single funded Vault governs many per-agent Cards — every payment is policy-checked on-chain before it settles. Confidential payments keep amounts and counterparties off the public chain, while the owner reconstructs the full ledger off-chain via a view key.

The problem

When AI agents spend money today, two things break at once:

  • No isolation — each agent holds its own wallet keys, so any bug, bad prompt, or rogue task can drain everything.
  • No privacy — every payment is public, leaking the buyer's identity, balance history, and counterparties to anyone watching the chain.

Architecture

Account structure

On-chain accounts, PDAs, and the instruction set.

flowchart TD
    subgraph PROGRAM["WARD program"]
        subgraph VAULT["Vault account — PDA: [b'vault', owner]"]
            V1["owner: Pubkey · admin_signer: Pubkey\npaused: bool · bump: u8"]
            SPL["SPL token account\nAuthority: vault PDA · asset: USDC"]
            V1 --> SPL
        end
        subgraph CARD["Card account — PDA: [b'card', vault, agent]"]
            C1["vault: Pubkey · agent_authority: Pubkey\ncap_per_tx: u64 · cap_per_day: u64\ndaily_spent: u64 · day_start_ts: i64\nexpiry: i64 · revoked: bool\nallowlist: [Pubkey;8] · bump: u8"]
        end
        VAULT -->|spawns| CARD
    end

    subgraph INSTR["Instructions"]
        I1["initialize_vault\nSigners: owner, admin"]
        I2["create_card\nSigner: owner"]
        I3["revoke_card\nSigner: owner"]
        I4["spend\nSigner: agent"]
        I5["private_spend\nSigner: agent + CPI"]
        I6["pause_vault\nSigner: owner"]
        I7["rotate_admin\nSigner: owner"]
    end

    PROGRAM --> INSTR
    INSTR --> TRUST["Trust boundary\nAgent authority signs on-chain\nLLM decision stays off-chain, keys never exposed to model"]
    TRUST --> VIEWKEY["View key\nDerived from owner keypair · decrypts sealed SpendEvent\npayloads off-chain · full ledger reconstruction"]
Loading

Spend lifecycle

All paths, error states, cap rollover, and settlement.

flowchart TD
    AGENT["Agent authority\nSigns spend instruction"] --> ROLLOVER
    ROLLOVER["Daily cap rollover\nclock_ts - day_start_ts >= 86400 -> reset daily_spent"] --> POLICY

    subgraph POLICY["On-chain policy check"]
        P1["amount <= cap_per_tx"]
        P2["daily_spent + amount <= cap_per_day"]
        P3["payee in allowlist · !expired · !revoked · !paused"]
        P4["card.vault == vault.key"]
    end

    POLICY -->|deny| ERRORS
    POLICY -->|approve: spend| SPL2
    POLICY -->|approve: private_spend| MB

    subgraph ERRORS["Errors"]
        E1["ExceedsTxCap · ExceedsDailyCap"]
        E2["PayeeNotAllowed · CardExpired"]
        E3["CardRevoked · VaultPaused"]
        E4["InvalidVault · Unauthorized"]
    end

    SPL2["CPI -> spl_token\ninvoke_signed with vault PDA seeds\nCleartext on-chain"]

    subgraph MB["CPI -> MagicBlock"]
        MB1["ephemeral-spl-token\nShuttle PDA delegated to MB validator"]
        MB2["Encrypted disbursement queued on validator"]
        MB1 --> MB2
        MB2 --> MB3["Sealed settlement\nAmount + payee encrypted"]
    end

    SPL2 --> EVENT
    MB3 --> EVENT
    EVENT["SpendEvent (sol_log_data)\nclient_ref_id · card · vault · amount (cleartext)\nsealed_payload: Option[u8;96] · timestamp"]
    EVENT --> VIEWKEY["Owner view key decrypts sealed_payload -> full ledger"]
Loading

Program accounts

Account PDA seeds Description
Vault ["vault", owner] Funded treasury, governed by owner + admin signer
Card ["card", vault, agent] Per-agent policy account

Card policies

Field Purpose
cap_per_tx Max amount allowed per transaction
cap_per_day Max daily spend, auto-resets every 86,400 seconds
allowlist Up to 8 approved payee addresses
expiry Unix timestamp after which the card stops working
revoked Kill switch — owner can flip at any time

Instructions

Instruction Signer Description
initialize_vault owner + admin Create and fund a vault
create_card owner Spawn a scoped card for an agent
spend agent Cleartext SPL transfer, policy-checked
private_spend agent Confidential transfer via MagicBlock — amount + payee sealed
revoke_card owner Instantly kill a card
pause_vault owner Pause all spending from the vault
rotate_admin owner + new admin Rotate the admin signer

Error types

ExceedsTxCap · ExceedsDailyCap · PayeeNotAllowed · CardExpired · CardRevoked · VaultPaused · InvalidVault · Unauthorized

Privacy: the view key

For private_spend, the client encrypts (payee pubkey + amount) with the owner keypair before building the transaction. The sealed 96-byte payload is stored in SpendEvent. The owner later runs decryptPayload against emitted events to reconstruct the full ledger — counterparties and amounts never touch the public chain.

Setup

Prerequisites

  • Rust + Cargo
  • Solana CLI
  • Node.js 18+

Build program

cargo build-sbf

Deploy

solana program deploy target/deploy/ward.so

Client

cd client
npm install
npm run build

Run devnet scripts

cd client
PROGRAM_ID=<your-program-id> npx ts-node scripts/devnet-admin.ts
PROGRAM_ID=<your-program-id> npx ts-node scripts/devnet-spend.ts
PROGRAM_ID=<your-program-id> MAGICBLOCK_PROGRAM_ID=<magicblock-id> npx ts-node scripts/devnet-private-spend.ts
PROGRAM_ID=<your-program-id> npx ts-node scripts/owner-console.ts

Program ID

Devnet: A6ak7ZnX3BioPejqTHGrfgQu9RYQ8mfHQJ3CDbxKn7Lf

Team

Built with

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages