JSPM

xclaw02

0.1.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1
  • Score
    100M100P100Q36885F
  • License MIT

xClaw02 - x402 payments SDK for OpenClaw agents. Pay and charge for APIs with stablecoins.

Package Exports

  • xclaw02
  • xclaw02/errors
  • xclaw02/payee
  • xclaw02/payer
  • xclaw02/signer
  • xclaw02/testing
  • xclaw02/utils
  • xclaw02/wallet

Readme

xClaw02

npm version Python License: MIT

TypeScript SDK for x402 HTTP payments by Primer. OpenClaw compatible 🦞

Easily add pay-per-request monetization to your JavaScript/TypeScript APIs using the x402 protocol. Accept stablecoin payments (USDC, EURC) or any ERC-20 token with gasless transactions—payers never pay gas fees.

Quick Start (CLI)

# Create a new wallet
xclaw02 wallet create

# Check balance
xclaw02 wallet balance 0xYourAddress

# Probe a URL for x402 support
xclaw02 probe https://api.example.com/paid

# Set up for OpenClaw
xclaw02 openclaw init

Why x402?

  • HTTP-native payments - Uses the standard HTTP 402 Payment Required status code
  • Gasless for payers - Payments are authorized via EIP-712 signatures; facilitators handle gas
  • Stablecoin support - Native support for USDC/EURC via EIP-3009 transferWithAuthorization
  • Any ERC-20 token - Support for other tokens via Primer's Prism settlement contract
  • Multi-chain - Base, Ethereum, Arbitrum, Optimism, Polygon (mainnet + testnet)
  • Framework integrations - Express, Hono, Next.js middleware included
  • Testing utilities - Mock facilitator for integration testing

Installation

npm install xclaw02

Payer (Client)

Wrap fetch or axios to automatically handle 402 responses:

const { createSigner, x402Fetch, x402Axios } = require('xclaw02');

// Create a signer (use CAIP-2 network format)
const signer = await createSigner('eip155:8453', process.env.PRIVATE_KEY);

// Wrap fetch
const fetch402 = x402Fetch(fetch, signer, { maxAmount: '1.00' });
const response = await fetch402('https://example.com/api/paywall');

// Or wrap axios
const axios402 = x402Axios(axios.create(), signer, { maxAmount: '1.00' });
const response = await axios402.get('https://example.com/api/paywall');

Options

Option Required Description
maxAmount Yes Maximum payment per request (e.g., '1.00')
facilitator No Custom facilitator URL
verify No Verify payment before sending (default: true)

Payee (Server)

Middleware for Express, Hono, and Next.js:

Express

const { x402Express } = require('xclaw02');

app.use(x402Express('0xYourAddress', {
  '/api/paywall': {
    amount: '0.01',
    asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    network: 'eip155:8453'  // CAIP-2 format
  },
  '/api/data/*': {
    amount: '0.001',
    asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    network: 'eip155:8453'  // CAIP-2 format
  }
}));

Hono

const { x402Hono } = require('xclaw02');

app.use('*', x402Hono('0xYourAddress', {
  '/api/paywall': { amount: '0.01', asset: '0x...', network: 'eip155:8453' }
}));

Next.js

// App Router (Next.js 13+)
import { x402Next } from 'xclaw02';

async function handler(req) {
  return Response.json({ data: 'paywall' });
}

export const GET = x402Next(handler, {
  payTo: '0xYourAddress',
  amount: '0.01',
  asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  network: 'eip155:8453'  // CAIP-2 format
});

Token Types

EIP-3009 Tokens (USDC, EURC)

These tokens support gasless transfers natively via transferWithAuthorization. The payer signs an authorization, and the facilitator executes the transfer—payer pays zero gas.

Standard ERC-20 Tokens

For other ERC-20 tokens, Primer's Prism contract enables gasless payments:

  1. One-time approval - Approve the Prism contract to spend your tokens
  2. Gasless payments - Sign authorizations; Prism handles the transfers
const { createSigner, approveToken } = require('xclaw02');

const signer = await createSigner('eip155:8453', process.env.PRIVATE_KEY);

// One-time approval (this transaction requires gas)
await approveToken(signer, '0xTokenAddress');

// Now you can make gasless payments with this token

Networks

Networks use CAIP-2 identifiers (e.g., eip155:8453 for Base).

Network (CAIP-2) Chain ID Legacy Name Default Facilitator
eip155:8453 8453 base âś“ Primer
eip155:84532 84532 base-sepolia âś“ Primer
eip155:1 1 ethereum Custom required
eip155:11155111 11155111 sepolia Custom required
eip155:42161 42161 arbitrum Custom required
eip155:421614 421614 arbitrum-sepolia Custom required
eip155:10 10 optimism Custom required
eip155:11155420 11155420 optimism-sepolia Custom required
eip155:137 137 polygon Custom required
eip155:80002 80002 polygon-amoy Custom required

Note: Legacy network names (e.g., 'base') are still accepted for backward compatibility but CAIP-2 format is recommended.

Custom Facilitator

For non-Base networks, you must provide your own facilitator:

// Payee
app.use(x402Express('0xYourAddress', {
  '/api/paywall': { amount: '0.01', asset: '0x...', network: 'eip155:1' }
}, { facilitator: 'https://your-facilitator.com' }));

// Payer
const fetch402 = x402Fetch(fetch, signer, {
  maxAmount: '1.00',
  facilitator: 'https://your-facilitator.com'
});

Debug Logging

DEBUG=x402:* node app.js
DEBUG=x402:payer,x402:payee node app.js

Testing Your Integration

The SDK provides testing utilities to help you test your x402 integration without making real payments. Import from xclaw02/testing:

const {
  createMockFacilitator,
  createTestPayment,
  createTest402Response,
  fixtures
} = require('xclaw02/testing');

What's Provided

Utility Purpose
createMockFacilitator() Fake payment server that approves/rejects without blockchain
createTestPayment() Generate valid X-PAYMENT headers without a real wallet
createTest402Response() Generate 402 responses for testing client code
fixtures Pre-built test addresses, route configs, and sample payloads

Testing a Payee (Server)

If you've built an API with x402 payments, test it like this:

// my-api.test.js
const request = require('supertest');
const app = require('./my-app');
const { createMockFacilitator, createTestPayment } = require('xclaw02/testing');

describe('My Paid API', () => {
  let mockFac;

  beforeAll(async () => {
    // Start a fake facilitator on port 3001
    mockFac = await createMockFacilitator({ port: 3001 });
  });

  afterAll(async () => {
    await mockFac.close();
  });

  test('returns 402 when no payment provided', async () => {
    const res = await request(app).get('/api/premium');
    expect(res.status).toBe(402);
    expect(res.headers['payment-required']).toBeDefined();
  });

  test('returns 200 when valid payment provided', async () => {
    const payment = createTestPayment({ amount: '10000' }); // 0.01 USDC

    const res = await request(app)
      .get('/api/premium')
      .set('X-PAYMENT', payment);

    expect(res.status).toBe(200);
  });

  test('rejects insufficient payment', async () => {
    const payment = createTestPayment({ amount: '1' }); // way too little

    const res = await request(app)
      .get('/api/premium')
      .set('X-PAYMENT', payment);

    expect(res.status).toBe(402);
  });
});

Important: Point your middleware at the mock facilitator during tests:

// In your app setup for tests
const middleware = x402Express(payTo, routes, {
  facilitator: 'http://127.0.0.1:3001'  // Mock facilitator URL
});

Testing a Payer (Client)

If you've built a client that pays for APIs, test the 402 handling:

const { createTest402Response, fixtures } = require('xclaw02/testing');

test('client handles 402 response', async () => {
  // Create a mock server that returns 402
  const server = setupMockServer((req, res) => {
    const body = createTest402Response({
      amount: '10000',
      payTo: fixtures.TEST_ADDRESSES.payee,
      resource: '/api/data'
    });
    res.status(402)
       .set('PAYMENT-REQUIRED', Buffer.from(JSON.stringify(body)).toString('base64'))
       .end();
  });

  // Test your client handles it correctly
  const result = await myClient.fetchWithPayment('/api/data');
  expect(result).toBeDefined();
});

Mock Facilitator Options

// Auto-approve all payments (default)
const mock = await createMockFacilitator({ mode: 'approve' });

// Reject all payments
const mock = await createMockFacilitator({ mode: 'reject' });

// Custom logic
const mock = await createMockFacilitator({
  mode: 'custom',
  handler: (payload) => {
    const amount = payload.paymentRequirements?.maxAmountRequired;
    if (parseInt(amount) > 1000000) {
      return { success: false, error: 'Amount too high for test' };
    }
    return { success: true, transaction: '0x' + 'f'.repeat(64) };
  }
});

// Add artificial latency (for timeout testing)
const mock = await createMockFacilitator({ latencyMs: 5000 });

Inspecting Requests

The mock facilitator records all requests for assertions:

const mock = await createMockFacilitator();

// ... run your test ...

// Check what was sent to the facilitator
expect(mock.requests.length).toBe(1);
expect(mock.lastRequest().payload.paymentRequirements.network).toBe('eip155:8453');

// Clear between tests
mock.clearRequests();

createTestPayment Options

createTestPayment({
  amount: '10000',             // Amount in smallest units (default: '10000')
  from: '0x...',               // Payer address (default: test address)
  to: '0x...',                 // Payee address (default: test address)
  network: 'eip155:84532',     // Network in CAIP-2 format (default: 'eip155:8453')
  validForSeconds: 7200        // Validity window (default: 3600)
});

Available Fixtures

const { fixtures } = require('xclaw02/testing');

fixtures.TEST_ADDRESSES.payer   // '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
fixtures.TEST_ADDRESSES.payee   // '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'

fixtures.USDC_ADDRESSES['eip155:8453']   // USDC on Base mainnet
fixtures.USDC_ADDRESSES['eip155:84532']  // USDC on Base Sepolia

fixtures.sampleRouteConfig      // Example route config for middleware
fixtures.sample402ResponseBody  // Example 402 response structure
fixtures.samplePaymentPayload   // Example payment payload structure

CLI Reference

The SDK includes a command-line interface for wallet management, probing, and OpenClaw integration.

xclaw02 <command> [options]

Commands

Command Description
wallet create Create a new wallet (address, private key, mnemonic)
wallet balance <address> Check USDC/ETH balance
wallet from-mnemonic Restore wallet from mnemonic phrase
probe <url> Check if URL supports x402 payments
pay <url> Make a payment to a 402 endpoint
pay <url> --dry-run Preview payment without paying
networks List supported networks
facilitator Show facilitator info
openclaw init Set up x402 for OpenClaw agents
openclaw status Check OpenClaw x402 status

Environment Variables

Variable Description
XCLAW02_PRIVATE_KEY Wallet private key
XCLAW02_NETWORK Default network (default: base)
XCLAW02_MAX_AMOUNT Default max payment amount
XCLAW02_FACILITATOR Facilitator URL override

Examples

# Create wallet and save output
xclaw02 wallet create --json > wallet.json

# Check balance on Arbitrum
xclaw02 wallet balance 0x... --network arbitrum

# Preview payment (dry run)
xclaw02 pay https://api.example.com/data --dry-run

# Pay for an API
XCLAW02_PRIVATE_KEY=0x... xclaw02 pay https://api.example.com/data --max-amount 0.10

Wallet Utilities

const {
  createWallet,
  walletFromMnemonic,
  getBalance,
  x402Probe
} = require('xclaw02');

// Create a new wallet
const wallet = createWallet();
console.log(wallet.address);     // 0x...
console.log(wallet.privateKey);  // 0x...
console.log(wallet.mnemonic);    // "word1 word2 ..."

// Restore from mnemonic
const restored = walletFromMnemonic('word1 word2 ...');

// Check balance
const balance = await getBalance('0x...', 'base', 'USDC');
console.log(balance.balance);  // "100.50"

// Probe a URL for x402 support
const probe = await x402Probe('https://api.example.com/paid');
if (probe.supports402) {
  console.log('Payment required:', probe.requirements);
}

Error Handling

The SDK provides structured errors for programmatic handling:

const { X402Error, ErrorCodes } = require('xclaw02');

try {
  const response = await x402Fetch(url, signer, { maxAmount: '0.10' });
} catch (error) {
  if (error instanceof X402Error) {
    switch (error.code) {
      case ErrorCodes.INSUFFICIENT_FUNDS:
        console.log('Need more funds:', error.details);
        break;
      case ErrorCodes.AMOUNT_EXCEEDS_MAX:
        console.log('Payment too expensive:', error.details);
        break;
      case ErrorCodes.SETTLEMENT_FAILED:
        console.log('Settlement failed:', error.details);
        break;
    }
  }
}

Error Codes

Code Description
INVALID_CONFIG Missing or invalid configuration
MISSING_PRIVATE_KEY Private key not provided
UNSUPPORTED_NETWORK Network not supported
INSUFFICIENT_FUNDS Wallet balance too low
AMOUNT_EXCEEDS_MAX Payment exceeds maxAmount
PAYMENT_FAILED Payment transaction failed
SETTLEMENT_FAILED Facilitator settlement failed
INVALID_RESPONSE Malformed 402 response
MISSING_PAYMENT_HEADER No payment requirements header

OpenClaw Integration

npm install xclaw02
xclaw02 openclaw init

Or install skill from ClawHub: clawhub install xclaw02

Changelog

v0.1.0

  • Initial release
  • CLI for wallet management, probing, and payments
  • Express, Hono, and Next.js middleware
  • Testing utilities with mock facilitator
  • OpenClaw skill integration

License

MIT - Primer Systems