@vaultfire/x402
v1.0.0
Published
Vaultfire x402 Payment Protocol — HTTP 402 internet-native USDC payments on Base and Avalanche
Maintainers
Readme
Vaultfire x402 Payment Protocol
Implementation of the x402 HTTP 402 Payment Protocol for internet-native USDC micropayments on Base and Avalanche. Built for the Vaultfire Protocol — AI accountability infrastructure with contracts on Base and Avalanche mainnet.
Overview
x402 is an open standard that enables internet-native payments using HTTP 402 ("Payment Required"). When a client requests a protected resource without a valid payment, the server responds with 402 and a machine-readable payment specification. The client signs a USDC authorization (EIP-3009 / EIP-712), retries the request with the payment header, and the facilitator settles the transaction on-chain.
This package provides:
- TypeScript client — payment-gated fetch, EIP-712 signing, payment history
- Python gateway — server-side payment enforcement, ledger, privacy hardening
- API endpoints — Next.js-compatible verify and settle routes
- Partner webhooks — HMAC-signed webhook integration for Assemble AI, NS3, and EVM rails
Protocol Flow
┌─────────────────────────────────────────────────────────────────────────────┐
│ x402 Protocol Flow (v2) │
└─────────────────────────────────────────────────────────────────────────────┘
Client Server Facilitator (x402.org)
│ │ │
│──── GET /resource ───────▶ │
│ │ │
│◀─── 402 Payment Required ─┤ │
│ X-PAYMENT-REQUIRED: │ │
│ {x402Version, accepts│ │
│ [network, amount, │ │
│ asset, payTo, ...]}│ │
│ │ │
│ ┌─── Sign EIP-712 ────┐ │ │
│ │ TransferWithAuth │ │ │
│ │ (EIP-3009 / USDC) │ │ │
│ └─────────────────────┘ │ │
│ │ │
│──── GET /resource ───────▶ │
│ X-PAYMENT: │ │
│ base64({signature, │ │
│ authorization, ...})│ │
│ │ │
│ │──── POST /verify ────────────▶
│ │ {payload, requirement} │
│ │◀─── {valid: true} ───────────│
│ │ │
│ │──── POST /settle ────────────▶
│ │ (or local transferFrom) │
│ │◀─── {txHash, settled: true} ─│
│ │ │
│◀─── 200 OK + resource ───┤ │
│ X-PAYMENT-RESPONSE: │ │
│ {success, txHash} │ │Contract Addresses
| Chain | Symbol | Contract Address | Chain ID |
|-------------|--------|----------------------------------------------|----------|
| Base | USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | 8453 |
| Avalanche | USDC | 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E | 43114 |
Facilitator: https://x402.org/facilitator
Deployer / Relayer: 0xA054f831B562e729F8D268291EBde1B2EDcFb84F
Hub: https://theloopbreaker.com
Quick Start
TypeScript
Install
# From npm (when published)
npm install @vaultfire/x402 ethers
# From GitHub (works now)
npm install github:Ghostkey316/vaultfire-x402 ethersConfigure and use
import {
configureX402,
x402Fetch,
initiatePayment,
formatUsdc,
WalletProvider,
} from '@vaultfire/x402';
// 1. Implement WalletProvider (or use a pre-built adapter)
const myWallet: WalletProvider = {
getAddress: () => '0xYourAddress',
getPrivateKey: () => process.env.AGENT_PRIVATE_KEY ?? null,
isUnlocked: () => true,
};
// 2. Configure the client once at startup
configureX402(myWallet);
// 3. Make a payment-gated HTTP request
const result = await x402Fetch(
'https://api.example.com/premium-data',
{ method: 'GET' },
{
autoPay: true,
maxAutoPayAmount: '0.10', // auto-pay up to $0.10 USDC
facilitatorUrl: 'https://x402.org/facilitator',
},
);
if (result.paid) {
const data = await result.response.json();
console.log('Paid', formatUsdc(result.paymentPayload!.accepted.amount), 'USDC');
console.log('TX:', result.settlement?.txHash);
}
// 4. Send a direct payment (for XMTP / agent-to-agent)
const { payload, record } = await initiatePayment(
'0xRecipientAddress',
'1.00', // 1.00 USDC
'Payment for AI service',
);Agent-mode (server-side, environment key)
import { getX402AgentClient } from '@vaultfire/x402';
// Reads VAULTFIRE_AGENT_KEY from environment
const client = getX402AgentClient();
const result = await client.signPayment(
'0xRecipient',
'1000000', // 1 USDC in micro-units
'USDC',
8453, // Base mainnet
);
console.log('Signature:', result.signature);
console.log('Signing address:', result.signingAddress);Python
Install
pip install flask # Only required for x402_listener webhook serverBasic usage
from python.x402_gateway import get_default_gateway, X402Rule, X402PaymentRequired
# Get the process-wide gateway
gw = get_default_gateway()
# Register a custom rule
gw.register_rule(X402Rule(
endpoint="myapp.premium_data",
category="api",
description="Premium data API",
minimum_charge=0.0001,
currency="USDC",
unlocks=("premium_access",),
))
# Execute a payment-gated function
try:
result = gw.execute(
"myapp.premium_data",
lambda: fetch_premium_data(),
amount=0.0001,
currency="USDC",
wallet_address="bpow20.cb.id", # or any verified wallet
signature="codex::auto", # or a real EIP-712 sig
)
print("Result:", result)
except X402PaymentRequired as e:
print("Payment required:", e.to_dict())Flask webhook server
from flask import Flask
from python.x402_listener import x402EventListener
app = Flask(__name__)
x402EventListener(app) # registers /vaultfire/x402-webhook and /vaultfire/x402-dashboard
if __name__ == "__main__":
app.run(port=5000)API Reference
TypeScript
Configuration
| Function | Description |
|----------|-------------|
| configureX402(wallet, vns?) | Configure wallet provider and optional VNS resolver |
Payment Functions
| Function | Description |
|----------|-------------|
| x402Fetch(url, init?, config?) | Payment-aware fetch — handles full 402 protocol flow |
| x402Get(url, config?) | Payment-aware GET |
| x402Post(url, body, config?) | Payment-aware POST |
| payAndRetry(url, init, requirement, resource?, config?) | Sign and retry with payment header |
| initiatePayment(recipient, amount, description?, config?) | Sign a direct payment payload |
| createPaymentPayload(requirement, resource?, config?) | Build a signed EIP-3009 payload |
| signTransferAuthorization(auth, customSigner?) | Sign an EIP-712 TransferWithAuthorization |
Verification
| Function | Description |
|----------|-------------|
| verifyPaymentSignature(payload) | Locally verify EIP-712 signature |
| verifyPaymentViaFacilitator(payload, req, facilitatorUrl?) | Verify via facilitator API |
Protocol Helpers
| Function | Description |
|----------|-------------|
| isPaymentRequired(response) | Returns true if response status is 402 |
| parsePaymentRequired(response) | Parse payment requirements from 402 response |
| selectPaymentRequirement(requirements) | Select best option (Base USDC EIP-3009 first) |
Utilities
| Function | Description |
|----------|-------------|
| formatUsdc(microAmount) | Convert micro-USDC to human-readable string |
| parseUsdc(humanAmount) | Convert human-readable USDC to micro-units |
| generateNonce() | Generate cryptographically secure bytes32 nonce |
| encodeHeaderPayload(obj) | Base64-encode a JSON object for x402 headers |
| decodeHeaderPayload<T>(b64) | Base64-decode an x402 header to typed JSON |
| getUsdcBalance(address?) | Check USDC balance on Base mainnet |
| hasEnoughUsdc(amount, address?) | Check if balance meets a payment amount |
Payment History (browser localStorage)
| Function | Description |
|----------|-------------|
| getPaymentHistory() | Get all payment records |
| savePaymentRecord(record) | Save a payment record |
| updatePaymentRecord(id, updates) | Update status/txHash of a record |
| clearPaymentHistory() | Clear all records |
| getPaymentStats() | Totals, counts by status |
| getEnrichedPaymentHistory() | History with VNS names resolved |
Interfaces
// Inject any wallet implementation
interface WalletProvider {
getAddress(): string | null;
getPrivateKey(): string | null; // SECURITY: never log this
isUnlocked(): boolean;
}
// Inject VNS name resolution (optional)
interface VNSResolver {
resolvePaymentAddress(nameOrAddress: string): Promise<{ address: string; vnsName?: string } | null>;
reverseResolveVNS(address: string): Promise<string | null>;
}
// Client configuration
interface X402ClientConfig {
facilitatorUrl?: string; // default: https://x402.org/facilitator
autoPay?: boolean; // default: false
maxAutoPayAmount?: string; // default: "1.00" USDC
customSigner?: (domain, types, value) => Promise<string>;
}Python
X402Gateway
class X402Gateway:
def register_rule(rule: X402Rule) -> None
def unregister_rule(endpoint: str) -> bool
def get_rule(endpoint: str) -> X402Rule | None
def describe_rules() -> dict
def execute(endpoint, callback, *, amount?, currency?, metadata?,
wallet_address?, belief_signal?, signature?, wallet_type?) -> Any
def record_external_event(*, event_type, status, amount?, currency?,
metadata?, wallet_address?, ...) -> dict
def get_denial_count(endpoint: str) -> intX402Rule
@dataclass
class X402Rule:
endpoint: str # "api.myapp.query"
category: str # "api" | "codex" | "cli" | "drops" | "yield"
description: str
minimum_charge: float
currency: str # "ETH" | "USDC" | "ASM" | "WLD"
default_amount: float | None
wallet_free: bool
unlocks: Iterable[str] # Feature flags unlocked on paymentverify_x402_wallet
def verify_x402_wallet(
address: str,
*,
belief_signal: dict | None = None,
signature: str | None = None,
wallet_type: str | None = None,
) -> tuple[bool, str]: # (verified, classification)Accepted wallet classifications:
"ghostkey"—bpow20.cb.id,ghostkey316.eth"zk"—wallet_type="zk"oraddress.startswith("zk-shard:")"ephemeral"—wallet_type="ephemeral"oraddress.startswith("ephemeral:")
verify_hmac_receipt
def verify_hmac_receipt(
payload: dict,
*,
secret: str,
require_partner: bool = True,
max_skew_seconds: int = 300,
nonce_ttl_seconds: int = 900,
) -> ReceiptVerificationResult:VNS Integration
The Vaultfire Name Service (VNS) enables human-readable payment addresses like vaultfire-sentinel.vns.
import { configureX402, initiatePayment, VNSResolver } from '@vaultfire/x402';
// Implement VNSResolver against your VNS contract
const vnsResolver: VNSResolver = {
async resolvePaymentAddress(nameOrAddress: string) {
if (!nameOrAddress.includes('.vns')) {
return { address: nameOrAddress }; // raw address passthrough
}
const address = await myVnsContract.resolve(nameOrAddress);
if (!address) return null;
return { address, vnsName: nameOrAddress };
},
async reverseResolveVNS(address: string) {
return myVnsContract.reverseLookup(address);
},
};
configureX402(myWallet, vnsResolver);
// Now you can pay to VNS names
const { payload } = await initiatePayment(
'vaultfire-sentinel', // .vns name resolved automatically
'0.50',
'Agent service payment',
);XMTP Integration
x402 payment payloads can be attached to XMTP messages for agent-to-agent payments:
import { getX402AgentClient, encodeHeaderPayload } from '@vaultfire/x402';
import { Client } from '@xmtp/xmtp-js';
const x402Client = getX402AgentClient();
// Sign a payment
const signResult = await x402Client.signPayment(
'0xAgentRecipient',
'500000', // 0.5 USDC
'USDC',
8453, // Base
);
// Send via XMTP
const message = {
type: 'x402-payment',
signature: signResult.signature,
payload: signResult.payload,
};
await xmtpClient.sendMessage(conversation, JSON.stringify(message));Partner Webhook Integration
Partners send HMAC-signed webhook payloads to /vaultfire/x402-webhook. The canonical message format ensures signatures cannot be replayed across partners or key rotations.
Env vars
# Preferred: per-partner key registry (supports rotation)
VAULTFIRE_X402_PARTNER_KEYS_JSON='{"assemble":{"k1":"secret"},"ns3":{"k1":"secret"}}'
# Legacy: single secret per rail
VAULTFIRE_X402_ASSEMBLE_SECRET=your-assemble-secret
VAULTFIRE_X402_NS3_SECRET=your-ns3-secretPayload format
{
"type": "payment",
"status": "completed",
"rail": "assemble",
"currency": "ASM",
"amount": 0.00021,
"wallet_address": "bpow20.cb.id",
"tx_ref": "asm_receipt_001",
"partner_id": "assemble",
"key_id": "k1",
"nonce": "abc123randomhex",
"timestamp": 1712345678.9,
"signature": "hex(HMAC-SHA256(secret, canonical_json))",
"loyalty_score": 0.85
}See examples/partner-webhook.py and examples/partner-webhook.js for complete signing examples.
API Endpoints
This repo includes Next.js-compatible API route examples in api/:
POST /api/x402/verify
Verifies a payment authorization by recovering the EIP-712 signer and checking USDC allowance/balance on Base mainnet.
Request:
{
"paymentHeader": "<base64-encoded JSON>",
"expectedRecipient": "0x...",
"expectedAmount": "1000000"
}Response (valid):
{
"valid": true,
"signer": "0x...",
"from": "0x...",
"to": "0x...",
"amount": "1000000",
"amountHuman": "1.000000",
"allowance": "10000000",
"balance": "50000000",
"deadline": "2024-01-01T00:00:00.000Z",
"relayer": "0xA054f831B562e729F8D268291EBde1B2EDcFb84F"
}POST /api/x402/settle
Executes USDC.transferFrom on Base mainnet after verifying the payment. Requires DEPLOYER_PRIVATE_KEY in environment.
Request:
{
"paymentHeader": "<base64-encoded JSON>",
"expectedRecipient": "0x...",
"taskId": "optional-task-id"
}Response (settled):
{
"settled": true,
"txHash": "0x...",
"from": "0x...",
"to": "0x...",
"amount": "1000000",
"amountHuman": "1.000000",
"blockNumber": "12345678",
"explorerUrl": "https://basescan.org/tx/0x..."
}Running Tests
TypeScript
npm install
npm testPython
python -m pytest tests/test_x402_gateway.py -v
# OR
python -m unittest tests/test_x402_gateway.pyRepository Structure
vaultfire-x402/
├── README.md # This file
├── LICENSE # MIT
├── package.json # TypeScript package config
├── tsconfig.json # TypeScript compiler config
├── .gitignore
├── src/
│ └── x402-client.ts # Standalone TypeScript client (1,389 lines)
├── python/
│ ├── __init__.py # Package exports
│ ├── x402_gateway.py # Core payment gateway + ledger (718 lines)
│ ├── x402_privacy.py # Privacy hardening + ghostkey mode (278 lines)
│ ├── x402_hooks.py # High-level billing hooks (300 lines)
│ ├── x402_listener.py # Flask webhook + dashboard blueprint (252 lines)
│ ├── x402_dashboard.py # Ledger introspection utilities (198 lines)
│ ├── x402_rails.py # Payment rail adapters (146 lines)
│ └── x402_receipts.py # HMAC receipt verification (276 lines)
├── api/
│ ├── verify.ts # Next.js verify endpoint (338 lines)
│ └── settle.ts # Next.js settle endpoint (323 lines)
├── examples/
│ ├── basic-payment.ts # TypeScript payment example
│ ├── payment-gated-api.ts # Payment-gated API (server + client)
│ ├── partner-webhook.js # Partner webhook (JavaScript)
│ └── partner-webhook.py # Partner webhook (Python)
└── tests/
├── x402-client.test.ts # TypeScript tests (482 lines)
└── test_x402_gateway.py # Python tests (715 lines)Security
- Private keys are never logged, returned in responses, or stored in files. The
WalletProviderinterface ensures keys stay in your custody. - Identity metadata (IP, email, device fingerprint, biometrics) is scrubbed from all ledger entries by the privacy layer.
- HMAC replay protection — partner webhooks require a unique nonce and timestamp within 5 minutes; replayed nonces are rejected.
- Simulation before settlement — the settle endpoint simulates
transferFrombefore execution to catch reverts without spending gas. - Rate limiting should be applied at the edge (Vercel middleware, Cloudflare) for the settle endpoint.
Ecosystem
| Package | Description |
|---|---|
| @vaultfire/xmtp | Trust-gated encrypted agent messaging with x402 payment commands |
| @vaultfire/vns | On-chain .vns name service — pay agents by human-readable name |
| @vaultfire/sdk | Core SDK — belief verification, attestations, agent reputation |
| vaultfire-contracts | Canonical contract registry — all deployed ABIs and addresses |
References
- Coinbase x402 Specification
- x402 Documentation
- EIP-3009: transferWithAuthorization
- EIP-712: Typed Structured Data Signing
- Base USDC on Basescan
- Avalanche USDC on Snowtrace
- Vaultfire Hub
License
MIT — see LICENSE
Copyright (c) 2024 Vaultfire Protocol
