x402 Payment Protocol
KnowledgeFlowDB uses the x402 payment protocol for per-call pricing on MCP tool calls. Payments use USDC on Base chain with EIP-3009 TransferWithAuthorization.
How It Works
- Client calls an MCP tool endpoint
- Server responds with
402 Payment Requiredand payment requirements - Client signs a USDC TransferWithAuthorization (EIP-712)
- Client retries the request with the signed payment in the
X-PAYMENTheader - Server verifies the payment via the facilitator and executes the tool
Client Gateway Facilitator
|--- POST /tool -------->| |
|<-- 402 + requirements --| |
| | |
| (sign EIP-712) | |
| | |
|--- POST /tool --------->| |
| X-PAYMENT: <signed> |--- verify payment ---------->|
| |<-- payment valid ------------|
|<-- 200 + result --------| |
Pricing
| Item | Cost |
|---|---|
| Default tool call | $0.0005 |
| Payment token | USDC |
| Chain | Base mainnet (chain ID 8453) |
| Gas cost to user | None (gasless via EIP-3009) |
Server operators can set custom per-server pricing.
SpendingWallet (SDK)
The rickydata SDK provides a SpendingWallet class for safe, isolated payment signing with built-in spending limits and circuit breakers.
Creating a SpendingWallet
import { SpendingWallet } from '@nickydata/sdk';
// From a BIP-39 seed phrase (recommended)
const wallet = await SpendingWallet.fromSeedPhrase(
'your seed phrase words...',
0, // HD derivation index
{
maxPerCall: 0.01, // Max $0.01 per call
maxPerDay: 5.0, // Max $5/day
maxPerWeek: 20.0, // Max $20/week
maxPerSession: 10.0, // Max $10/session
}
);
// From a private key
const wallet = await SpendingWallet.fromPrivateKey(
'0xPRIVATE_KEY',
{ maxPerDay: 5.0 }
);
Safety Features
The SpendingWallet provides defense-in-depth:
- Spending limits: Per-call, daily, weekly, and session limits
- Circuit breaker: Stops payments after consecutive failures
- Event emission:
payment:signed,payment:rejected,balance:low,spending:warning - Isolated key: Separate from your main wallet
- Memory cleanup:
wallet.destroy()clears private key material
Monitoring Spending
// Check current balance
const balance = await wallet.getBalance();
console.log(`USDC: ${balance.usdc}, ETH: ${balance.eth}`);
// Get spending summary
const spending = wallet.getSpending();
console.log(`Total spent: $${spending.totalUsd}`);
// Get remaining budget
const remaining = wallet.getRemainingBudget('day');
console.log(`Remaining today: $${remaining}`);
// Payment history
const history = wallet.getHistory({ limit: 10 });
Events
wallet.on('payment:signed', (receipt) => {
console.log(`Paid $${receipt.amountUsd} for ${receipt.toolName}`);
});
wallet.on('balance:low', ({ balance, threshold }) => {
console.warn(`Low balance: ${balance} USDC (threshold: ${threshold})`);
});
wallet.on('spending:warning', ({ period, percentUsed }) => {
console.warn(`${period} spending at ${percentUsed.toFixed(0)}% of limit`);
});
wallet.on('circuit-breaker:tripped', ({ failureCount }) => {
console.error(`Circuit breaker tripped after ${failureCount} failures`);
});
Operator Bypass
Certain wallet addresses (configured as operator bypass addresses) can call tools without x402 payment. This is used for gateway operators and internal services.
Authenticated Bypass
When enabled on the gateway, any wallet with a valid Bearer token (JWT or mcpwt_) can call tools without x402 payment. Usage is logged for future billing. This enables clients like Claude Code that cannot sign x402 payments to use tools with just wallet authentication.