Skip to main content
This example demonstrates integration using Crossmint Smart Wallets with x402 v2. Perfect for applications needing smart wallet features and account abstraction.

Overview

Integrate Snack Money payments using Crossmint’s smart wallet infrastructure with x402 v2, using Crossmint’s API for signing operations.

Features

  • Base network support
  • Smart wallet infrastructure - Account abstraction
  • API-based signing - Secure signature requests
  • Developer-controlled signing - Full control
  • x402 v2 integration - Latest protocol

Prerequisites

  • Node.js 18+
  • Crossmint account (Sign up)
  • Crossmint API key
  • Smart wallet with USDC balance on Base

Installation

npm install axios @x402/axios @x402/evm viem dotenv

Quick Start

1. Get Crossmint Credentials

  1. Visit Crossmint Console
  2. Create a project
  3. Get your API key
  4. Create a smart wallet

2. Set Up Environment

Create a .env file:
CROSSMINT_API_KEY=your_api_key
WALLET_LOCATOR=your_wallet_locator
SMART_WALLET_ADDRESS=your_smart_wallet_address
RECEIVER=your_x_username
AMOUNT=0.01
Important: The SMART_WALLET_ADDRESS should have USDC balance on Base network.

3. Implement Payment

Create base-send.ts:
import axios from "axios";
import { config } from "dotenv";
import { type Hex, type Address, type SignableMessage } from "viem";
import { toAccount } from "viem/accounts";
import { x402Client, wrapAxiosWithPayment, decodePaymentResponseHeader } from "@x402/axios";
import { registerExactEvmScheme } from "@x402/evm/exact/client";

config();

const crossmintApiKey = process.env.CROSSMINT_API_KEY as string;
const walletLocator = process.env.WALLET_LOCATOR as string;
const smartWalletAddress = process.env.SMART_WALLET_ADDRESS as string;
const receiver = process.env.RECEIVER as string;
const amount = parseFloat(process.env.AMOUNT || "0.01");

// Helper to request signatures from Crossmint API
async function requestCrossmintSignature(
  walletLocator: string,
  apiKey: string,
  signatureRequest: any
): Promise<Hex> {
  const createResponse = await axios.post(
    `https://www.crossmint.com/api/2022-06-09/wallets/${walletLocator}/signatures`,
    signatureRequest,
    {
      headers: {
        "X-API-KEY": apiKey,
        "Content-Type": "application/json"
      }
    }
  );

  const signatureId = createResponse.data.id;

  // Poll for signature
  let signature: string | null = null;
  let attempts = 0;
  const maxAttempts = 20;

  while (!signature && attempts < maxAttempts) {
    attempts++;
    const statusResponse = await axios.get(
      `https://www.crossmint.com/api/2022-06-09/wallets/${walletLocator}/signatures/${signatureId}`,
      { headers: { "X-API-KEY": apiKey } }
    );

    signature = statusResponse.data.outputSignature;
    if (signature) return signature as Hex;
    if (statusResponse.data.status === 'failed') {
      throw new Error(`Signature failed: ${JSON.stringify(statusResponse.data)}`);
    }
    await new Promise(resolve => setTimeout(resolve, 1000));
  }

  throw new Error(`Signature did not complete after ${maxAttempts} attempts`);
}

// Create a viem account that uses Crossmint's signature API
function createCrossmintAccount(
  address: Address,
  walletLocator: string,
  apiKey: string
) {
  const account = toAccount({
    address,
    async signMessage({ message }: { message: SignableMessage }): Promise<Hex> {
      const messageStr = typeof message === 'string' ? message : (message as any).raw || JSON.stringify(message);
      return requestCrossmintSignature(walletLocator, apiKey, {
        type: "evm-message",
        params: { message: messageStr, chain: "base" }
      });
    },
    signTransaction: async () => {
      throw new Error("Transaction signing not supported - use Crossmint transaction API");
    },
    signTypedData: async (typedData: any): Promise<Hex> => {
      return requestCrossmintSignature(walletLocator, apiKey, {
        type: "evm-typed-data",
        params: { typedData, chain: "base" }
      });
    }
  });

  return {
    ...account,
    sign: async ({ hash }: { hash: Hex }): Promise<Hex> => {
      return account.signMessage({ message: { raw: hash } as any });
    }
  } as any;
}

async function sendPayment() {
  console.log("🚀 Starting Snack Money payment with Crossmint Smart Wallet\n");
  console.log("ℹ️  Network: Base (EVM)\n");
  console.log(`✅ Smart Wallet Address: ${smartWalletAddress}`);
  console.log(`💡 Check balance: https://basescan.org/address/${smartWalletAddress}\n`);

  // Create Crossmint account
  const crossmintAccount = createCrossmintAccount(
    smartWalletAddress as Address,
    walletLocator,
    crossmintApiKey
  );

  // Create x402 client and register EVM scheme
  const client = new x402Client();
  registerExactEvmScheme(client, { signer: crossmintAccount });

  // Wrap axios with payment interceptor
  const api = wrapAxiosWithPayment(
    axios.create({ baseURL: "https://api.snack.money" }),
    client
  );

  console.log(`💸 Sending ${amount} USDC to @${receiver} on X via Base...\n`);

  try {
    const response = await api.post("/payments/x/pay", {
      amount,
      currency: "USDC",
      receiver,
      description: "Payment via Crossmint Smart Wallet (Base)"
    });

    console.log("✅ Payment successful!");
    console.log("\n📊 Response:", JSON.stringify(response.data, null, 2));

    const paymentResponseHeader = response.headers["payment-response"];
    if (paymentResponseHeader) {
      const paymentResponse = decodePaymentResponseHeader(paymentResponseHeader);
      console.log("\n🔐 Payment Response Details:", JSON.stringify(paymentResponse, null, 2));
    }

    if (response.data.data?.receipt) {
      console.log(`\n🧾 Receipt: ${response.data.data.receipt}`);
    }
  } catch (error: any) {
    console.error("❌ Payment failed:", error.response?.data || error.message);
    process.exit(1);
  }
}

sendPayment();

4. Run the Example

npm run base
# or
npx tsx base-send.ts

Expected Output

🚀 Starting Snack Money payment with Crossmint Smart Wallet

ℹ️  Network: Base (EVM)

✅ Smart Wallet Address: 0x...
💡 Check balance: https://basescan.org/address/0x...

🔐 Creating Crossmint account...

💸 Sending 0.01 USDC to @username on X via Base...

✅ Payment successful!

📊 Response: {
  "code": 200,
  "msg": "0.01 USDC sent successfully",
  "data": {
    "txn_id": "...",
    "amount": 0.01,
    "receipt": "https://snack.money/x/username?txn=..."
  }
}

How It Works

  1. Create Crossmint Account: Build a viem-compatible account that uses Crossmint’s signature API
  2. Register x402 EVM Scheme: Configure x402 client with the Crossmint signer
  3. Wrap Axios: Add x402 payment interceptor
  4. Send Payment: Make payment request
  5. Auto Signatures: Crossmint API handles message and typed data signing
  6. Success: Receive confirmation and receipt

Why Crossmint?

Benefits

  • Smart Wallets: Advanced wallet capabilities
  • Account Abstraction: Gasless transactions, batching
  • Viem Compatible: Standard interface
  • Developer Controlled: Full control over signing
  • Easy Integration: Simple SDK

Best For

  • Apps needing smart wallet features
  • Account abstraction requirements
  • Gasless transactions
  • Advanced wallet operations

Smart Wallet Features

Gasless Transactions

const wallet = await CrossmintSmartWallet.build({
  config: {
    chain: "base",
    apiKey: process.env.CROSSMINT_API_KEY!,
    gasless: true,  // Enable gasless transactions
  },
  signer: {
    type: "DEVELOPER_CONTROLLED",
    secret: process.env.CROSSMINT_SIGNER_SECRET!,
  },
});

Transaction Batching

// Batch multiple operations
await wallet.sendBatchTransaction([
  { to: address1, data: "0x..." },
  { to: address2, data: "0x..." },
]);

Supported Platforms

// X (Twitter)
await api.post("/payments/x/pay", {
  amount: 0.01,
  receiver: "username",
  currency: "USDC"
});

// Farcaster
await api.post("/payments/farcaster/pay", {
  amount: 0.5,
  receiver: "username",
  currency: "USDC"
});

// GitHub
await api.post("/payments/github/pay", {
  amount: 1.0,
  receiver: "username",
  currency: "USDC"
});

Error Handling

try {
  const response = await api.post("/payments/x/pay", {
    amount: 0.01,
    currency: "USDC",
    receiver: "username"
  });
  console.log("Success:", response.data);
} catch (error) {
  console.error("Payment failed:", error);

  // Handle Crossmint-specific errors
  if (error.message.includes("insufficient funds")) {
    console.error("Smart wallet needs more USDC");
  }
}

Wallet Management

Check Balance

const balance = await wallet.getBalance();
console.log("Wallet balance:", balance);

Get Wallet Info

console.log("Address:", wallet.address);
console.log("Chain:", wallet.chain);

Full Example Repository

View on GitHub → Includes:
  • Complete implementation
  • Smart wallet setup
  • Configuration guide
  • Detailed README

Next Steps

Dependencies

{
  "dependencies": {
    "axios": "^1.7.9",
    "@x402/axios": "^2.0.0",
    "@x402/evm": "^2.0.0",
    "viem": "^2.39.3",
    "dotenv": "^16.4.7"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "typescript": "^5.9.0"
  }
}

Learn More