@relai-fi/x402
Unified x402 payment SDK for Solana, Base, Polygon, Ethereum, Avalanche, and SKALE Base.
x402 is a protocol for HTTP-native micropayments. When a server returns HTTP 402 Payment Required, it includes payment details in the response. The client signs a payment, retries the request, and the server settles the payment and returns the protected content. This SDK handles the entire flow automatically — call fetch() and payments happen transparently.
Zero gas fees. The RelAI facilitator sponsors gas — users only pay for content (USDC). Works on all supported networks.
Installation
npm install @relai-fi/x402Quick Start — Client
Create a payment-aware fetch client. Works in the browser and Node.js:
import { createX402Client } from '@relai-fi/x402/client';
const client = createX402Client({
wallets: {
solana: solanaWallet, // @solana/wallet-adapter compatible
evm: evmWallet, // wagmi/viem compatible
},
});
// 402 responses are handled automatically
const response = await client.fetch('https://api.example.com/protected');
const data = await response.json();React Hook
Works with @solana/wallet-adapter-react and wagmi:
import { useRelaiPayment } from '@relai-fi/x402/react';
import { useWallet } from '@solana/wallet-adapter-react';
import { useAccount, useSignTypedData } from 'wagmi';
function PayButton() {
const solanaWallet = useWallet();
const { address } = useAccount();
const { signTypedDataAsync } = useSignTypedData();
const {
fetch,
isLoading,
status,
transactionUrl,
transactionNetworkLabel,
} = useRelaiPayment({
wallets: {
solana: solanaWallet,
evm: address
? { address, signTypedData: signTypedDataAsync }
: undefined,
},
});
return (
<div>
<button onClick={() => fetch('/api/protected')} disabled={isLoading}>
{isLoading ? 'Paying...' : 'Access API'}
</button>
{transactionUrl && (
<a href={transactionUrl} target="_blank">
View on {transactionNetworkLabel}
</a>
)}
</div>
);
}Supported Networks
All networks use USDC with 6 decimals. Gas fees are sponsored by the RelAI facilitator.
| Network | Identifier (v1) | CAIP-2 (v2) | Signing Method |
|---|---|---|---|
| Solana | solana | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | SPL transfer + fee payer |
| Base | base | eip155:8453 | EIP-3009 transferWithAuthorization |
| Avalanche | avalanche | eip155:43114 | EIP-3009 transferWithAuthorization |
| Polygon | polygon | eip155:137 | EIP-3009 transferWithAuthorization |
| Ethereum | ethereum | eip155:1 | EIP-3009 transferWithAuthorization |
| SKALE Base | skale-base | eip155:1187947933 | EIP-3009 transferWithAuthorization |
Both formats accepted. The SDK accepts v1 simple names (base, polygon) and v2 CAIP-2 identifiers (eip155:8453, eip155:137). Use networkV1ToV2() and networkV2ToV1() to convert between them.
createX402Client(config)
Creates a fetch wrapper that automatically handles 402 Payment Required responses.
| Option | Type | Default | Description |
|---|---|---|---|
wallets | { solana?, evm? } | {} | Wallet adapters for each chain |
facilitatorUrl | string | RelAI facilitator | Custom facilitator endpoint |
preferredNetwork | RelaiNetwork | — | Prefer this network when multiple accepts |
solanaRpcUrl | string | mainnet-beta | Solana RPC endpoint |
evmRpcUrls | Record<string, string> | Built-in | RPC URLs per network name |
maxAmountAtomic | string | — | Safety cap on payment amount |
verbose | boolean | false | Log payment flow to console |
Wallet Interfaces
// Solana — compatible with @solana/wallet-adapter-react useWallet()
interface SolanaWallet {
publicKey: { toString(): string } | null;
signTransaction: ((tx: unknown) => Promise<unknown>) | null;
}
// EVM — pass address + signTypedData from wagmi
interface EvmWallet {
address: string;
signTypedData: (params: {
domain: Record<string, unknown>;
types: Record<string, unknown[]>;
message: Record<string, unknown>;
primaryType: string;
}) => Promise<string>;
}useRelaiPayment(config)
React hook wrapping createX402Client with state management. Config is the same as above.
| Property | Type | Description |
|---|---|---|
fetch | (input, init?) => Promise<Response> | Payment-aware fetch |
isLoading | boolean | Payment in progress |
status | 'idle' | 'pending' | 'success' | 'error' | Current state |
error | Error | null | Error details on failure |
transactionId | string | null | Tx hash/signature on success |
transactionUrl | string | null | Block explorer link |
transactionNetworkLabel | string | null | Human-readable label (e.g. "Base") |
connectedChains | { solana, evm } | Which wallets are connected |
reset | () => void | Reset state to idle |
Server SDK (Express)
Protect any Express route with x402 micropayments:
import Relai from '@relai-fi/x402/server';
const relai = new Relai({
network: 'base', // or 'solana', 'avalanche', 'skale-base'
});
// Protect any Express route with micropayments
app.get('/api/data', relai.protect({
payTo: '0xYourWallet',
price: 0.01, // $0.01 USDC
description: 'Premium data access',
}), (req, res) => {
// req.payment = { verified, transactionId, payer, network, amount }
res.json({ data: 'Protected content', payment: req.payment });
});
// Dynamic pricing
app.get('/api/premium', relai.protect({
payTo: '0xYourWallet',
price: (req) => req.query.tier === 'pro' ? 0.10 : 0.01,
}), handler);
// Per-endpoint network override
app.get('/api/solana-data', relai.protect({
payTo: 'SolanaWalletAddress',
price: 0.005,
network: 'solana',
}), handler);req.payment
| Field | Type | Description |
|---|---|---|
verified | boolean | Always true after settlement |
transactionId | string | On-chain transaction hash |
payer | string | Payer wallet address |
network | string | Network name (e.g., base) |
amount | number | Price in USD |
Utilities
import { toAtomicUnits, fromAtomicUnits } from '@relai-fi/x402/utils';
toAtomicUnits(0.05, 6); // '50000' ($0.05 USDC)
toAtomicUnits(1.50, 6); // '1500000' ($1.50 USDC)
fromAtomicUnits('50000', 6); // 0.05
fromAtomicUnits('1500000', 6); // 1.5Payload Conversion (v1 ↔ v2)
import { convertV1ToV2, convertV2ToV1, networkV1ToV2 } from '@relai-fi/x402/utils';
networkV1ToV2('solana'); // 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
networkV1ToV2('base'); // 'eip155:8453'
networkV1ToV2('avalanche'); // 'eip155:43114'
networkV1ToV2('skale-base'); // 'eip155:1187947933'
const v2Payload = convertV1ToV2(v1Payload);
const v1Payload = convertV2ToV1(v2Payload);Package Exports
// Client — browser & Node.js fetch wrapper with automatic 402 handling
import { createX402Client } from '@relai-fi/x402/client';
// React hook — state management + wallet integration
import { useRelaiPayment } from '@relai-fi/x402/react';
// Server — Express middleware for protecting endpoints
import Relai from '@relai-fi/x402/server';
// Utilities — payload conversion, unit helpers
import {
convertV1ToV2, convertV2ToV1,
networkV1ToV2, networkV2ToV1,
toAtomicUnits, fromAtomicUnits,
} from '@relai-fi/x402/utils';
// Types & constants
import {
RELAI_NETWORKS, CHAIN_IDS, USDC_ADDRESSES,
NETWORK_CAIP2, EXPLORER_TX_URL,
type RelaiNetwork, type SolanaWallet,
type EvmWallet, type WalletSet,
} from '@relai-fi/x402';How It Works
Client Server Facilitator
| | |
|── GET /api/data ──────────>| |
|<── 402 Payment Required ───| |
| (accepts: network, amount, asset) |
| | |
| SDK signs payment | |
| (EIP-3009/SPL) | |
| | |
|── GET /api/data ──────────>| |
| X-PAYMENT: <signed> |── settle ─────────────────>|
| |<── tx hash ────────────────|
|<── 200 OK + data ─────────| |
| PAYMENT-RESPONSE: <tx> | |
© 2026 RelAI. Monetize APIs with x402 Protocol.