@agentokratia/x402-escrow
v2.1.2
Published
Escrow payment scheme for x402 protocol - session-based payments for high-frequency APIs
Maintainers
Readme
@agentokratia/x402-escrow
Escrow payment scheme for the x402 protocol. Supports direct USDC payments and cross-token swaps.
Features
- Direct USDC payments - Gasless via ERC-3009 ReceiveWithAuthorization
- Multi-token support - Pay with WETH, DAI, USDT. Receiver gets USDC.
- Automatic swap quotes - Server fetches DEX quotes, client just signs
- Gzip compression - Aggregator calldata compressed for smaller payloads
Installation
npm install @x402/core @x402/next @x402/fetch @x402/evm @agentokratia/x402-escrow viemServer Integration
Next.js with paymentProxy
import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
import { ExactEvmScheme } from '@x402/evm/exact/server';
import { EscrowScheme } from '@agentokratia/x402-escrow/server';
import { paymentProxy } from '@x402/next';
const facilitator = new HTTPFacilitatorClient({
url: 'https://facilitator.agentokratia.com',
createAuthHeaders: async () => ({
verify: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
settle: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
}),
});
const escrow = new EscrowScheme({ facilitator });
const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const server = new x402ResourceServer(facilitator)
.register('eip155:8453', new ExactEvmScheme())
.register('eip155:8453', escrow);
// Auto-discovers supported tokens (USDC, WETH, DAI...)
const escrowAccepts = await escrow.buildAccepts({
network: 'eip155:8453',
price: '$0.01',
payTo: '0xYourWallet...',
asset: USDC, // settlement token (default: auto-detect)
});
export const middleware = paymentProxy(
{
'/api/premium': {
accepts: [
{ scheme: 'exact', network: 'eip155:8453', price: '$0.01', payTo: '0xYourWallet...' },
...escrowAccepts,
],
},
},
server
);Express with paymentMiddleware
import { paymentMiddleware } from '@x402/express';
import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
import { EscrowScheme } from '@agentokratia/x402-escrow/server';
const facilitator = new HTTPFacilitatorClient({
url: 'https://facilitator.agentokratia.com',
createAuthHeaders: async () => ({
verify: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
settle: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
}),
});
const escrow = new EscrowScheme({ facilitator });
const accepts = await escrow.buildAccepts({
network: 'eip155:8453',
price: '$0.01',
payTo: '0xYourWallet',
});
app.use(
paymentMiddleware(
{
'GET /api/analyze': { accepts },
},
new x402ResourceServer(facilitator)
)
);Dynamic Pricing (Custom API Routes)
For endpoints with per-request pricing (like tip-app):
import { NextRequest, NextResponse } from 'next/server';
import { HTTPFacilitatorClient } from '@x402/core/server';
import { EscrowScheme, preprocessSwapPayload } from '@agentokratia/x402-escrow/server';
const facilitator = new HTTPFacilitatorClient({
/* config */
});
const escrow = new EscrowScheme({ facilitator, apiKey: process.env.X402_API_KEY });
export async function GET(request: NextRequest) {
const amount = request.nextUrl.searchParams.get('amount');
const recipient = request.nextUrl.searchParams.get('to');
const paymentSignature = request.headers.get('payment-signature');
if (!paymentSignature) {
// Fetch fresh DEX quotes with buildAcceptsResolved
const accepts = await escrow.buildAcceptsResolved({
network: 'eip155:8453',
price: `$${amount}`,
payTo: recipient,
});
const requirements = accepts.map((a) => ({
scheme: a.scheme,
network: a.network,
asset: a.price.asset,
amount: a.price.amount,
payTo: a.payTo,
maxTimeoutSeconds: 600,
extra: a.price.extra || {},
}));
const response = NextResponse.json({ message: 'Payment required' }, { status: 402 });
response.headers.set(
'PAYMENT-REQUIRED',
Buffer.from(JSON.stringify(requirements)).toString('base64')
);
return response;
}
// Process payment
const payload = JSON.parse(Buffer.from(paymentSignature, 'base64').toString());
const processed = preprocessSwapPayload({ x402Version: 2, ...payload });
const verifyResult = await facilitator.verify(processed, processed.accepted);
if (!verifyResult.isValid) {
return NextResponse.json({ error: verifyResult.invalidReason }, { status: 402 });
}
const settleResult = await facilitator.settle(processed, processed.accepted);
return NextResponse.json({ success: true, transaction: settleResult.transaction });
}Client Integration
Balance-Aware Token Selection (Recommended)
import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm/exact/client';
import { createWalletClient, createPublicClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import {
EscrowScheme,
createBalanceSelector,
preferTokenPolicy,
} from '@agentokratia/x402-escrow/client';
const WETH = '0x4200000000000000000000000000000000000006';
const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const walletClient = createWalletClient({
account,
chain: base,
transport: http(),
});
const publicClient = createPublicClient({
chain: base,
transport: http(),
});
// Balance-aware: auto-picks USDC (gasless) or WETH/DAI (swap)
const client = new x402Client(createBalanceSelector(publicClient, account.address))
.register('eip155:8453', new ExactEvmScheme(account))
.register('eip155:8453', new EscrowScheme(walletClient))
.registerPolicy(preferTokenPolicy([WETH, USDC]));
const paidFetch = wrapFetchWithPayment(fetch, client);
const res = await paidFetch('https://api.example.com/premium');
const data = await res.json();Simple Setup (Browser with wagmi)
import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm/exact/client';
import { EscrowScheme } from '@agentokratia/x402-escrow/client';
// With wagmi's useWalletClient
const { data: walletClient } = useWalletClient();
const signer = {
address: walletClient.account.address,
signTypedData: (msg) => walletClient.signTypedData({ account: walletClient.account, ...msg }),
};
const client = new x402Client()
.register('eip155:8453', new ExactEvmScheme(signer))
.register('eip155:8453', new EscrowScheme(walletClient));
const paidFetch = wrapFetchWithPayment(fetch, client);
const res = await paidFetch('https://api.example.com/premium');How It Works
Client Server Facilitator
│ │ │
│ GET /api/resource │ │
│──────────────────────────────>│ │
│ │ buildAccepts() │
│ │─────────────────────────────>│
│ │ (fetches quotes for swaps) │
│ │<─────────────────────────────│
│ 402 + PAYMENT-REQUIRED │ │
│<──────────────────────────────│ │
│ │ │
│ User selects token (WETH) │ │
│ Signs EIP-712 Permit2 │ │
│ │ │
│ GET + PAYMENT-SIGNATURE │ │
│──────────────────────────────>│ │
│ │ verify() + settle() │
│ │─────────────────────────────>│
│ │ (executes swap on-chain) │
│ │<─────────────────────────────│
│ 200 + transaction hash │ │
│<──────────────────────────────│ │Networks
| Network | Chain ID | Facilitator |
| ------------ | -------- | -------------------------------------- |
| Base Mainnet | 8453 | https://facilitator.agentokratia.com |
| Base Sepolia | 84532 | https://facilitator.agentokratia.com |
API
Client Exports
| Export | Description |
| ------------------------- | ------------------------------------------------- |
| EscrowScheme | Client scheme for x402Client (takes WalletClient) |
| createBalanceSelector | Async selector that checks on-chain balances |
| preferTokenPolicy | Sync policy that reorders by token preference |
| checkBalance | Utility for custom balance checks |
| signERC3009 | Sign ERC-3009 authorization |
| signPermit2TransferFrom | Sign Permit2 transfer |
| computePaymentNonce | Derive deterministic nonce from payment params |
| PERMIT2_ADDRESS | Universal Permit2 contract address |
| decompressCalldata | Decompress gzipped aggregator calldata |
Server Exports
| Export | Description |
| ----------------------- | ------------------------------------------------- |
| EscrowScheme | Server scheme for x402ResourceServer |
| HTTPFacilitatorClient | Re-export from @x402/core/server |
| preprocessSwapPayload | Decompress swap calldata before facilitator calls |
| compressCalldata | Compress aggregator calldata (gzip) |
Supported Input Tokens (Base Mainnet)
| Token | Address |
| ----- | -------------------------------------------- |
| USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| WETH | 0x4200000000000000000000000000000000000006 |
| DAI | 0x50c5725949a6f0c72e6c4a641f24049a917db0cb |
| USDT | 0xfde4c96c8593536e31f229ea8f37b2ada2699bb2 |
License
MIT
