
Mr HamlinThe agent economy just got its wallet layer. Today MoonPay launched the Open Wallet Standard (OWS) —...
The agent economy just got its wallet layer.
Today MoonPay launched the Open Wallet Standard (OWS) — an open-source protocol that gives AI agents a single, encrypted vault for storing keys and signing transactions across every major blockchain. One seed phrase, one signing interface, eight chain families. Keys never leave your machine.
The coalition backing it reads like a who's who: PayPal, OKX, Ripple, Solana Foundation, Ethereum Foundation, Base, Polygon, Arbitrum, Circle, Virtuals, LayerZero, and more — 21 founding organizations in total.
But here's the part that caught my attention: OWS explicitly names x402 as the payment protocol it was built to serve.
I build Spraay — an x402 payment gateway with 76+ paid endpoints across 13 chains. So I spent the morning digging into the OWS spec, installing the SDK, and testing how well it plugs into our stack.
Short answer: it fits like a glove.
Right now, if you're building an AI agent that pays for things — API calls, compute, data — you're probably doing something like this:
PRIVATE_KEY=0xabc123... # sitting in .env
Your agent reads that key at startup, holds it in memory, and passes it to whatever signing library you're using. If you're on EVM, you're using ethers.js. Solana? Different library, different key format. Bitcoin? Yet another one.
Three private keys. Three surfaces where they can leak — through logs, crash dumps, or the LLM context window itself.
OWS replaces all of that with:
const { createWallet, signTypedData } = require("@open-wallet-standard/core");
const wallet = createWallet("agent-treasury");
// => addresses for EVM, Solana, BTC, Cosmos, Tron, TON, Filecoin, Sui
const sig = signTypedData("agent-treasury", "evm", typedDataJson);
// Private key decrypted in Rust enclave, signed, immediately wiped
One seed. Eight chain families. Keys encrypted at rest with AES-256-GCM, decrypted only inside an isolated signing process, held in mlocked memory (pinned, never swapped to disk), and zeroized the moment the signature is produced.
Your agent never sees the private key.
x402 is Coinbase's protocol for machine-to-machine payments. The flow works like this:
402 Payment Required with a payment specTransferWithAuthorization (typed data)Steps 1, 4, and 5 are handled by the Spraay gateway. But step 3 — signing the payment — is where OWS comes in.
Until now, every agent doing x402 payments had to manage its own keys. OWS standardizes that layer so any agent, any framework, any tool can use the same wallet with the same security model.
I tested this hands-on with the OWS Node SDK (@open-wallet-standard/core). Here's a complete flow — create a wallet, sign an x402 payment for a Spraay API call:
const { createWallet, signTypedData } = require("@open-wallet-standard/core");
// 1. Create wallet (one seed → addresses on every chain)
const wallet = createWallet("my-agent");
const evmAddress = wallet.accounts.find(a => a.chainId.startsWith("eip155:")).address;
// 2. Build the x402 payment (EIP-3009 TransferWithAuthorization)
const typedData = JSON.stringify({
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" }
],
TransferWithAuthorization: [
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "validAfter", type: "uint256" },
{ name: "validBefore", type: "uint256" },
{ name: "nonce", type: "bytes32" }
]
},
primaryType: "TransferWithAuthorization",
domain: {
name: "USD Coin",
version: "2",
chainId: "8453", // Base
verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
},
message: {
from: evmAddress,
to: "0xAd62f03C7514bb8c51f1eA70C2b75C37404695c8", // Spraay
value: "100000", // $0.10 USDC
validAfter: "0",
validBefore: String(Math.floor(Date.now() / 1000) + 3600),
nonce: "0x" + [...crypto.getRandomValues(new Uint8Array(32))]
.map(b => b.toString(16).padStart(2, "0")).join("")
}
});
// 3. Sign it (key never leaves the Rust enclave)
const sig = signTypedData("my-agent", "evm", typedData);
console.log("Signature:", "0x" + sig.signature);
I ran this. It works. The OWS SDK produced a valid 65-byte secp256k1 signature with recovery ID, exactly what x402 expects. The signing happened in the native Rust core via NAPI-RS — no JavaScript crypto library touched the key material.
After createWallet(), OWS stores everything at ~/.ows/ with 700 permissions:
~/.ows/
wallets/
480d3738-4839-4cb6-bbcf-2605eb841012.json # encrypted keystore
Each wallet JSON uses the Ethereum Keystore v3 format — the same encryption that's been in production since 2015. One file, one seed, addresses for all chains.
Both OWS and Spraay expose MCP servers. This means an AI agent (Claude, GPT, LangChain, etc.) can have both tools loaded simultaneously:
{
"mcpServers": {
"ows": {
"command": "ows",
"args": ["serve", "--mcp"]
},
"spraay": {
"command": "npx",
"args": ["-y", "@plagtech/spraay-x402-mcp"]
}
}
}
OWS handles the wallet side (create, sign, manage keys). Spraay handles the payment side (76+ endpoints, 13 chains, token prices, batch payments, DeFi, RTP robot tasks). The agent orchestrates both without ever touching a private key.
One feature that's particularly useful for autonomous agents: OWS has a policy engine that evaluates rules before any key material is decrypted.
You can create API keys scoped to specific wallets, attach policies (spending limits, chain restrictions, recipient allowlists), and delegate them to sub-agents. The owner bypasses policies entirely — sudo access. Agent keys are constrained.
For Spraay payments, this means you could set:
The agent can make as many API calls as it wants within those bounds. Step outside them and the policy engine blocks the signing request before the key is ever decrypted.
I'm building a spraay-ows adapter package that will handle the full x402 payment flow in a single function call:
const { SpraayOWS } = require("spraay-ows");
const client = new SpraayOWS({ wallet: "my-agent" });
const { data } = await client.fetch("/v1/token/price?symbol=ETH&chain=base");
That's it. The adapter will handle the 402 response, construct the EIP-3009 typed data, call OWS for signing, encode the payment header, and retry — all transparently.
The code is already working in prototype. Will publish to npm once I've wired it into the Spraay gateway's test suite.
npm install @open-wallet-standard/core
💧 The agent economy has payment rails. Now it has a wallet standard. And they work together.