DexterDexter

DexterSDK

The official x402 SDK for multi-chain payments.

📦 SDK Updated to v1.3

The x402 SDK has been updated to v1.3 (released January 17, 2026). This release includes new features, bug fixes, and performance improvements. See the full changelog for details.

Run npm install @dexterai/x402@latest to update.

@dexterai/x402

The official SDK for x402 payments (currently v1.3). Works with Solana, Base, Polygon, Avalanche, and all supported chains. Full Phantom wallet support.

npm install @dexterai/x402

Package Exports

// Client - browser & Node.js
import { createX402Client } from '@dexterai/x402/client';
 
// React hook
import { useX402Payment } from '@dexterai/x402/react';
 
// Server helpers
import { createX402Server } from '@dexterai/x402/server';
 
// Chain adapters (advanced)
import { createSolanaAdapter, createEvmAdapter } from '@dexterai/x402/adapters';
 
// Utilities
import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';

Client SDK

The client SDK handles the entire x402 flow automatically. When a server returns 402 Payment Required, the client:

  1. Parses payment requirements from the PAYMENT-REQUIRED header
  2. Prompts the user to sign a transaction
  3. Retries the request with the PAYMENT-SIGNATURE header
  4. Returns the response

Basic Usage

import { createX402Client } from '@dexterai/x402/client';
 
const client = createX402Client({
  wallets: {
    solana: solanaWallet,
    evm: evmWallet,
  },
});
 
// That's it. 402 responses are handled automatically.
const response = await client.fetch('https://api.example.com/protected');
const data = await response.json();

Configuration Options

OptionTypeRequiredDescription
wallets{ solana?, evm? }YesMulti-chain wallets
walletSolanaWalletNoSingle Solana wallet (legacy)
preferredNetworkstringNoPrefer this network when multiple options available
rpcUrlsRecord<string, string>NoRPC endpoints per network (defaults to Dexter proxy)
maxAmountAtomicstringNoMaximum payment cap
verbosebooleanNoEnable debug logging

Custom RPC URLs

RPC URLs are optional—the SDK uses Dexter's RPC proxy by default. Override if needed:

const client = createX402Client({
  wallets: { solana: solanaWallet },
  rpcUrls: {
    'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': 'https://your-rpc.com',
  },
});

React Hook

The useX402Payment hook provides a complete payment flow for React apps. Works with @solana/wallet-adapter-react and wagmi out of the box.

Basic Usage

import { useX402Payment } from '@dexterai/x402/react';
import { useWallet } from '@solana/wallet-adapter-react';  // Solana
import { useAccount } from 'wagmi';                        // EVM (Base)
 
function PayButton() {
  // Get wallets from your existing providers
  const solanaWallet = useWallet();
  const evmWallet = useAccount();
 
  const { fetch, isLoading, balances, transactionUrl } = useX402Payment({
    wallets: { 
      solana: solanaWallet,
      evm: evmWallet,
    },
  });
 
  return (
    <div>
      <p>Balance: ${balances[0]?.balance.toFixed(2)}</p>
      <button 
        onClick={() => fetch('/api/protected')} 
        disabled={isLoading || !solanaWallet.connected}
      >
        {isLoading ? 'Paying...' : 'Pay'}
      </button>
      {transactionUrl && <a href={transactionUrl}>View Transaction</a>}
    </div>
  );
}

Return Values

PropertyTypeDescription
fetchfunctionPayment-aware fetch
isLoadingbooleanPayment in progress
statusstring'idle' | 'pending' | 'success' | 'error'
errorX402Error?Error details if failed
transactionIdstring?Transaction signature
transactionUrlstring?Block explorer link
balancesBalance[]Token balances per chain
connectedChains{ solana, evm }Which chains are connected
refreshBalancesfunctionManual balance refresh
resetfunctionClear state

Multi-Chain Display

function WalletStatus() {
  const { balances, connectedChains } = useX402Payment({ wallets });
 
  return (
    <div>
      <p>Solana: {connectedChains.solana ? '✅' : '❌'}</p>
      <p>Base: {connectedChains.evm ? '✅' : '❌'}</p>
      {balances.map(b => (
        <p key={b.network}>{b.chainName}: ${b.balance.toFixed(2)} USDC</p>
      ))}
    </div>
  );
}

Server SDK

The server SDK helps you accept x402 payments. It handles payment requirement generation and settlement.

Basic Usage

import { createX402Server } from '@dexterai/x402/server';
 
const server = createX402Server({
  payTo: 'YourWalletAddress...',
  network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
});
 
// In your Express route handler
app.post('/protected', async (req, res) => {
  const paymentSig = req.headers['payment-signature'];
 
  if (!paymentSig) {
    // No payment - return 402 with requirements
    const requirements = await server.buildRequirements({
      amountAtomic: '50000',  // $0.05 USDC
      resourceUrl: req.originalUrl,
    });
    res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
    return res.status(402).json({});
  }
 
  // Has payment - verify and settle
  const result = await server.settlePayment(paymentSig);
  if (!result.success) {
    return res.status(402).json({ error: result.errorReason });
  }
 
  // Payment successful - return protected content
  res.json({ 
    data: 'Your protected content',
    transaction: result.transaction,
  });
});

Base (EVM) Server

const baseServer = createX402Server({
  payTo: '0xYourEvmAddress...',
  network: 'eip155:8453',
  asset: { 
    address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
    decimals: 6 
  },
});

Configuration Options

OptionTypeRequiredDescription
payTostringYesYour wallet address to receive payments
networkstringYesNetwork identifier (see Supported Networks)
facilitatorUrlstringNoFacilitator URL (defaults to x402.dexter.cash)
asset{ address, decimals }NoToken config (defaults to USDC)

Dynamic Pricing

Charge based on input size - characters, bytes, records, or any unit. No external dependencies.

import { createX402Server, createDynamicPricing } from '@dexterai/x402/server';
 
const server = createX402Server({ payTo: '...', network: '...' });
const pricing = createDynamicPricing({
  unitSize: 1000,      // chars per unit
  ratePerUnit: 0.01,   // $0.01 per unit
  minUsd: 0.01,        // floor
  maxUsd: 10.00,       // ceiling
});
 
app.post('/api/process', async (req, res) => {
  const { text } = req.body;
  const paymentSig = req.headers['payment-signature'];
 
  if (!paymentSig) {
    const quote = pricing.calculate(text);
    const requirements = await server.buildRequirements({
      amountAtomic: quote.amountAtomic,
      resourceUrl: req.originalUrl,
    });
    res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
    res.setHeader('X-Quote-Hash', quote.quoteHash);
    return res.status(402).json({ usdAmount: quote.usdAmount });
  }
 
  // Validate quote hasn't changed
  const quoteHash = req.headers['x-quote-hash'];
  if (!pricing.validateQuote(text, quoteHash)) {
    return res.status(400).json({ error: 'Input changed, re-quote required' });
  }
 
  const result = await server.settlePayment(paymentSig);
  if (!result.success) return res.status(402).json({ error: result.errorReason });
 
  const processed = await processText(text);
  res.json(processed);
});

Token Pricing (LLM)

Accurate token-based pricing for LLMs using tiktoken. Supports OpenAI models out of the box, plus custom rates for Anthropic, Gemini, Mistral, or any model.

OpenAI Models

import { createX402Server, createTokenPricing } from '@dexterai/x402/server';
 
const server = createX402Server({ payTo: '...', network: '...' });
const pricing = createTokenPricing({
  model: 'gpt-4o-mini',  // Uses real OpenAI rates
});
 
app.post('/api/chat', async (req, res) => {
  const { prompt, systemPrompt } = req.body;
  const paymentSig = req.headers['payment-signature'];
 
  if (!paymentSig) {
    const quote = pricing.calculate(prompt, systemPrompt);
    const requirements = await server.buildRequirements({
      amountAtomic: quote.amountAtomic,
      resourceUrl: req.originalUrl,
      description: `${quote.model}: ${quote.inputTokens.toLocaleString()} tokens`,
    });
    res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
    res.setHeader('X-Quote-Hash', quote.quoteHash);
    return res.status(402).json({
      inputTokens: quote.inputTokens,
      usdAmount: quote.usdAmount,
      model: quote.model,
    });
  }
 
  const quoteHash = req.headers['x-quote-hash'];
  if (!pricing.validateQuote(prompt, quoteHash)) {
    return res.status(400).json({ error: 'Prompt changed, re-quote required' });
  }
 
  const result = await server.settlePayment(paymentSig);
  if (!result.success) return res.status(402).json({ error: result.errorReason });
 
  const response = await openai.chat.completions.create({
    model: pricing.config.model,
    messages: [{ role: 'user', content: prompt }],
  });
 
  res.json({ response: response.choices[0].message.content });
});

Custom Models (Anthropic, Gemini, etc.)

// Anthropic Claude
const pricing = createTokenPricing({
  model: 'claude-3-sonnet',
  inputRate: 3.0,    // $3.00 per 1M input tokens
  outputRate: 15.0,  // $15.00 per 1M output tokens
  maxTokens: 4096,
});
 
// Google Gemini
const pricing = createTokenPricing({
  model: 'gemini-1.5-pro',
  inputRate: 1.25,
  outputRate: 5.0,
});
 
// Custom model with custom tokenizer
const pricing = createTokenPricing({
  model: 'llama-3-70b',
  inputRate: 0.50,
  tokenizer: (text) => llamaTokenizer.encode(text).length,
});

Available Models

import { MODEL_PRICING, getAvailableModels } from '@dexterai/x402/server';
 
// Get all models sorted by tier and price
const models = getAvailableModels();
// → [{ model: 'gpt-5-nano', inputRate: 0.05, tier: 'fast' }, ...]
 
// Check pricing for a specific model
MODEL_PRICING['gpt-4o-mini'];
// → { input: 0.15, output: 0.6, maxTokens: 4096, tier: 'fast' }

Tiers: fast, standard, reasoning, premium, custom


Utilities

import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';
 
// Convert dollars to atomic units (for API calls)
toAtomicUnits(0.05, 6);  // '50000'
toAtomicUnits(1.50, 6);  // '1500000'
 
// Convert atomic units back to dollars (for display)
fromAtomicUnits('50000', 6);   // 0.05
fromAtomicUnits(1500000n, 6);  // 1.5

Supported Networks

NetworkIdentifierAsset
Solana Mainnetsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpUSDC
Solana Devnetsolana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1USDC
Base Mainneteip155:8453USDC
Base Sepoliaeip155:84532USDC

The SDK also supports v1 legacy identifiers for backward compatibility: solana, solana-devnet, base, base-sepolia.


Version Compatibility

The SDK automatically handles x402 version compatibility:

  • v2: Uses CAIP-2 network identifiers (solana:5eykt..., eip155:8453)
  • v1: Uses legacy identifiers (solana, base)

When a server returns a 402 response, the SDK echoes back the same x402Version that was requested. This ensures compatibility with both v1-only and v2-only facilitators.


Error Handling

import { X402Error } from '@dexterai/x402/client';
 
try {
  const response = await client.fetch('/api/protected');
} catch (error) {
  if (error instanceof X402Error) {
    console.log(error.code);    // 'INSUFFICIENT_BALANCE', 'USER_REJECTED', etc.
    console.log(error.message); // Human-readable message
  }
}

Error Codes

CodeDescription
INSUFFICIENT_BALANCENot enough USDC to complete payment
USER_REJECTEDUser rejected the transaction
SETTLEMENT_FAILEDFacilitator failed to settle
NETWORK_ERRORNetwork connectivity issue
INVALID_RESPONSEServer returned invalid payment requirements

TypeScript

The SDK is fully typed. Key types:

import type {
  X402ClientConfig,
  X402Client,
  PaymentRequired,
  PaymentAccept,
  VerifyResponse,
  SettleResponse,
} from '@dexterai/x402/client';
 
import type {
  X402ServerConfig,
  X402Server,
  DynamicPricingConfig,
  TokenPricingConfig,
} from '@dexterai/x402/server';
 
import type {
  UseX402PaymentConfig,
  UseX402PaymentReturn,
  PaymentStatus,
  BalanceInfo,
} from '@dexterai/x402/react';

Support