For AI Agents
Private, gasless SOL payments for autonomous agents on Solana. Your agent shields SOL once, then pays anyone privately. The recipient needs no SOL and never sees the sender.
Why agents need this
- Every SOL transfer is public. If your agent pays for compute, APIs, or data, anyone can read its spending.
- Visible transactions leak strategy and let others front run.
- Agents act for people. Their payments should not expose the user’s activity.
ZeroK is the privacy primitive your agent plugs into. It is not an agent itself. It is an SDK and an MCP server that your agent, app, or CLI uses.
Prerequisites
- A Solana keypair JSON, your existing
~/.config/solana/id.json, or a new one fromsolana-keygen new. - That wallet holds the SOL you want to shield, plus a little for the network fee. The recipient needs nothing, because spending is gasless. Deposit any amount, from 0.01 SOL.
That is it. No RPC key required to start. For production volume, point it at your own RPC (set HELIUS_API_KEY or ZEROK_RPC).
Option A: MCP server (recommended for AI agents)
Give any MCP capable agent (Claude Desktop, Claude Code, Cursor, or your own) seven tools: zerok_address, zerok_balance, zerok_deposit, zerok_preview, zerok_send, zerok_consolidate, zerok_recover. Full parity with the SDK. Your agent decides amounts and recipients, and ZeroK hides all the cryptography. zerok_recover reattaches to your funds from chain on a new machine or after a restart. Nothing to clone, npx fetches it, and your keypair stays on your machine (read locally, never transmitted).
Connect it.
Claude Code:
claude mcp add zerok -e SOLANA_KEYPAIR=~/.config/solana/id.json -e ZEROK_NETWORK=devnet -- npx -y zerok-mcpClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"zerok": {
"command": "npx",
"args": ["-y", "zerok-mcp"],
"env": { "SOLANA_KEYPAIR": "~/.config/solana/id.json", "ZEROK_NETWORK": "devnet" }
}
}
}Before you point it at real money
The examples above use devnet on purpose, and the server defaults to devnet for the same reason.
Switch ZEROK_NETWORK to mainnet-beta only when you mean it, because an agent spending on mainnet
cannot undo it.
Two things protect you, and both are worth knowing about rather than discovering later:
- The money tools are marked as destructive, so a well behaved MCP client asks before running them.
- A per session spend cap is enforced by the server,
1by default. Raise or lower it withZEROK_SESSION_CAP_SOL. It bounds what a confused or manipulated agent can move in one session.
The network, the RPC, the relay and the note location are read from the environment only. A prompt cannot talk the server into a different network or a different relay, because those are not part of any tool’s input.
Use it. Just ask your agent:
“Shield 1 SOL with ZeroK, then send 0.3 privately to
<address>.”
On a fresh machine, ask it to recover first: “Recover my ZeroK balance.” Same wallet, same funds.
Option B: SDK (for code, apps, custom agents)
npm install zerok-agentconst { Keypair } = require('@solana/web3.js');
const { ZeroKV2 } = require('zerok-agent');
const zk = new ZeroKV2({
network: 'mainnet-beta',
asset: 'USDC', // or 'SOL'
wallet: Keypair.fromSecretKey(/* agent key */),
});
await zk.recover(); // reattach to your funds from chain (new machine or restart)
await zk.deposit(24.9); // shield any amount, split onto the ladder for you
await zk.preview(20, 'RecipientAddress'); // what it will cost, before you commit
await zk.send(20, 'RecipientAddress'); // private and gasless for the recipient
await zk.sendMax('RecipientAddress'); // drain the balance in one call
await zk.balance(); // { balanceSol, notes, pendingSol, pendingNotes }
zk.address(); // your public keyOne instance handles one asset. Set asset: 'SOL' for native SOL, or 'USDC' for stablecoin
payments, and everything below behaves the same way.
| Method | What it does |
|---|---|
deposit(amount) | Shield any amount. It is split onto the denomination ladder for you |
preview(amount, recipient) | Quote the payment before sending, including the cost |
send(amount, recipient) | Pay anyone. The recipient needs no SOL |
sendMax(recipient) | Send everything you hold |
consolidate(recipient) | Combine many small notes into fewer |
recover() | Rebuild your balance from chain |
balance() | Your private balance, { balanceSol, notes, pendingSol, pendingNotes } |
address() | Your base58 public key |
The legacy V1 agent is still available at require('zerok-agent/v1') if you have older code.
Deposit any amount, send any amount. SOL moves on a 0.01 grid and USDC on a 0.1 grid, and send() handles the rest, including awkward amounts and the private change that comes back to you. You never manage any of it.
Your funds are safe across restarts
The secrets that let you spend are saved for you and encrypted to your wallet, so the same wallet recovers the same funds on any machine or tool, whether that is the web app, the CLI, the SDK, or the MCP. There is nothing to back up beyond your keypair.
How it works in 30 seconds
- Deposit. Your SOL or USDC joins one shared, shielded vault for that asset, and you get private notes back.
- Send. A zero knowledge proof shows you own a note without revealing which one. A relayer submits it and pays the gas, so the recipient never needs SOL. If they have no token account yet, the relayer creates it for them.
- Privacy. Payments leave in fixed denominations identical to everyone else’s. The withdrawal is public. The link back to your deposit is not. Its strength comes from how many notes share the pool, so it grows as the pool does. A nullifier makes each note spendable exactly once.
Facts
- Program:
HVcTokFF4rwvcU7sC7GjS317CSf7QDgfCvW7edijKS2v(Solana mainnet, serves both assets) - One shielded vault per asset. Deposit any amount of SOL or USDC, spend in fixed denominations.
- Withdrawals are gasless for the recipient, funded by the relayer out of a small per transaction
protocol fee that
preview()shows you before you send. - Non custodial. Your key never leaves your machine.
- Machine readable summary: llms.txt