Lucid Agents
Migration Guides

x402 v2 Migration Guide

Complete guide for migrating from x402 v1 to x402 v2 protocol

This guide covers migrating from x402 v1 to x402 v2 in Lucid Agents.

Versions:

  • Migrating from: Lucid Agents v2.2.x (using x402 v1)
  • Migrating to: Lucid Agents v2.4.0+ (using x402 v2)

Overview

The x402 protocol has been upgraded from v1 to v2, bringing:

  • Scoped packages - New @x402/* package naming
  • CAIP-2 network format - Standard network identifiers (e.g., eip155:8453)
  • Improved verification - Enhanced payment protocol and facilitator support

What Changed

Package Updates

All x402-related packages have been migrated to scoped @x402/* packages:

Old PackageNew PackageVersion
x402@x402/core^2.2.0
x402-fetch@x402/fetch^2.2.0
N/A@x402/evm^2.2.0
x402-hono@x402/hono^2.2.0
x402-express@x402/express^2.2.0
x402-next@x402/next^2.2.0

Network Format (CAIP-2)

Network identifiers now use the CAIP-2 standard:

Old FormatNew Format (CAIP-2)Network
"base""eip155:8453"Base Mainnet
"base-sepolia""eip155:84532"Base Sepolia
"ethereum""eip155:1"Ethereum Mainnet
"sepolia""eip155:11155111"Ethereum Sepolia
"solana""solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"Solana Mainnet
"solana-devnet""solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"Solana Devnet

Import Path Changes

Import paths have changed from x402/types to @x402/core/*:

// Before
import type { Network, RouteConfig } from 'x402/types';

// After
import type { Network } from '@x402/core/types';
import type { RouteConfig } from '@x402/core/server';

Migration Steps

1. Update Dependencies

Update your package.json to use the new scoped packages:

# Remove old packages
bun remove x402 x402-fetch x402-hono x402-express x402-next

# Install new packages
bun add @x402/core @x402/fetch @x402/evm @x402/hono @x402/express @x402/next

Or update your package.json directly:

{
  "dependencies": {
    "@x402/core": "^2.2.0",
    "@x402/fetch": "^2.2.0",
    "@x402/evm": "^2.2.0"
  }
}

2. Update Network Identifiers

Replace all old network strings with CAIP-2 format:

// Before
const config: PaymentsConfig = {
  payTo: '0x...',
  network: 'base-sepolia',
  facilitatorUrl: 'https://facilitator.daydreams.systems'
};

// After
const config: PaymentsConfig = {
  payTo: '0x...',
  network: 'eip155:84532',  // Base Sepolia in CAIP-2 format
  facilitatorUrl: 'https://facilitator.daydreams.systems'
};

3. Update Import Statements

Type Imports

// Before
import type { Network, Money, RouteConfig, RoutesConfig } from 'x402/types';

// After
import type { Network, Money } from '@x402/core/types';
import type { RouteConfig, RoutesConfig } from '@x402/core/server';

Payment Middleware Imports

Hono:

// Before
import { paymentMiddleware } from 'x402-hono';

// After
import { paymentMiddleware } from '@x402/hono';

Express:

// Before
import { paymentMiddleware } from 'x402-express';

// After
import { paymentMiddleware } from '@x402/express';

Next.js:

// Before
import { paymentMiddleware } from 'x402-next';

// After
import { paymentMiddleware } from '@x402/next';

4. Update Manual x402 Client Usage

If you're using x402 client directly (not through Lucid Agents helpers), update to the new API:

// Before
import { wrapFetchWithX402, createWallet } from 'x402-fetch';

const wallet = createWallet(privateKey, 'base');
const x402Fetch = wrapFetchWithX402(fetch, wallet);

// After
import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
import { ExactEvmScheme, toClientEvmSigner } from '@x402/evm';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(privateKey);
const signer = toClientEvmSigner(account);
const client = new x402Client()
  .register('eip155:8453', new ExactEvmScheme(signer))     // Base
  .register('eip155:84532', new ExactEvmScheme(signer))   // Base Sepolia
  .register('eip155:1', new ExactEvmScheme(signer));      // Ethereum

const x402Fetch = wrapFetchWithPayment(fetch, client);

If you use Lucid Agents payment helpers, minimal changes are needed:

import { createRuntimePaymentContext } from '@lucid-agents/payments';

// The helpers now use @x402/* packages internally
const paymentContext = await createRuntimePaymentContext({
  runtime,
  network: 'eip155:84532'  // Just update to CAIP-2 format
});

// Use the payment-enabled fetch
const response = await paymentContext.fetchWithPayment(url, options);

Framework-Specific Changes

TanStack Start

The TanStack paywall middleware has been rewritten for x402 v2:

import { createTanStackPaywall } from '@lucid-agents/tanstack';
import type { FacilitatorConfig } from '@x402/core/server';

const { invoke, stream } = createTanStackPaywall({
  runtime: agentRuntime,
  basePath: '/api/agent',
  facilitator: {
    url: 'https://facilitator.daydreams.systems'
  } satisfies FacilitatorConfig
});

Hono

Update to use the new @x402/hono package:

import { createHonoPaywall } from '@lucid-agents/hono';

const paywall = createHonoPaywall({
  runtime: agentRuntime,
  basePath: '/api/agent'
});

app.use('/api/agent/*', paywall);

Express

Update to use the new @x402/express package:

import { createExpressPaywall } from '@lucid-agents/express';

const paywall = createExpressPaywall({
  runtime: agentRuntime,
  basePath: '/api/agent'
});

app.use('/api/agent', paywall);

Test Updates

Mock Facilitator Responses

Tests now need to mock v2 facilitator responses:

import { beforeAll, afterAll } from 'bun:test';

const mockFacilitatorResponse = {
  kinds: [
    {
      scheme: "exact",
      network: "eip155:84532",  // CAIP-2 format
      asset: {
        address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
        decimals: 6,
        eip712: {
          name: "USDC",
          version: "2"
        }
      }
    }
  ]
};

let originalFetch: typeof globalThis.fetch;

beforeAll(() => {
  originalFetch = globalThis.fetch;
  globalThis.fetch = async (input, init) => {
    const url = typeof input === "string"
      ? input
      : input instanceof URL
        ? input.toString()
        : input.url;

    if (url.includes("/supported")) {
      return new Response(JSON.stringify(mockFacilitatorResponse), {
        status: 200,
        headers: { "Content-Type": "application/json" }
      });
    }

    return originalFetch(input, init);
  };
});

afterAll(() => {
  globalThis.fetch = originalFetch;
});

Network Identifiers in Tests

Update all network strings in test fixtures:

// Before
const payments: PaymentsConfig = {
  payTo: "0x...",
  network: "base-sepolia",
  facilitatorUrl: "https://facilitator.test"
};

// After
const payments: PaymentsConfig = {
  payTo: "0x...",
  network: "eip155:84532",
  facilitatorUrl: "https://facilitator.test"
};

Environment Variables

If you're using environment variables for network configuration, update them:

# Before
NETWORK=base-sepolia

# After
NETWORK=eip155:84532

Update your environment variable documentation:

/**
 * Required environment variables:
 *   - NETWORK - Network identifier in CAIP-2 format (e.g., eip155:84532 for Base Sepolia)
 */

Breaking Changes Summary

Required Changes

  1. Update packages: Replace all x402* packages with @x402/*
  2. Update networks: Convert to CAIP-2 format (e.g., "base""eip155:8453")
  3. Update imports: Change import paths from x402/types to @x402/core/*
  4. Update tests: Mock v2 facilitator responses and use CAIP-2 networks

Optional (If Using Raw x402 Client)

  1. Update client creation: Use new x402Client API with register() method
  2. Update signers: Use toClientEvmSigner from @x402/evm

Supported Networks

After migration, these networks are supported:

EVM Networks

  • eip155:8453 - Base Mainnet
  • eip155:84532 - Base Sepolia
  • eip155:1 - Ethereum Mainnet
  • eip155:11155111 - Ethereum Sepolia

Solana Networks

  • solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp - Solana Mainnet
  • solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 - Solana Devnet

Troubleshooting

TypeScript Errors

Error: Type '"base-sepolia"' is not assignable to type '${string}:${string}'

Solution: Update the network string to CAIP-2 format:

network: 'eip155:84532'  // Instead of 'base-sepolia'

Error: Cannot find module 'x402/types'

Solution: Update import to use scoped package:

import type { Network } from '@x402/core/types';

Runtime Errors

Error: Network 'base' is not supported

Solution: Register the network in CAIP-2 format:

const client = new x402Client()
  .register('eip155:8453', new ExactEvmScheme(signer));

Test Failures

Error: Facilitator mock not working

Solution: Update mock to return v2 format with kinds array (see Test Updates section)

Additional Resources

Need Help?

If you encounter issues:

On this page