Skip to main content
Make payments using Viem wallets.

Installation

# npm
npm install @armory-sh/client-viem

# yarn
yarn add @armory-sh/client-viem

# pnpm
pnpm add @armory-sh/client-viem

# bun
bun add @armory-sh/client-viem

armoryPay

import { armoryPay } from '@armory-sh/client-viem';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount('0x...');

const result = await armoryPay(
  { account },
  'https://api.example.com/data',
  'base',
  'usdc'
);

if (result.success) {
  console.log(result.data);
} else {
  console.error(result.code, result.message);
}

With Custom Amount

await armoryPay(
  { account },
  'https://api.example.com/premium',
  'base',
  'usdc',
  { amount: '5.0' }
);

createArmoryClient

import { createArmoryClient } from '@armory-sh/client-viem';

const armory = createArmoryClient({
  walletClient,
  publicClient,
});

const result = await armory.pay({
  to: '0x...',
  amount: 1000000000n,
  token: TOKENS.USDC_BASE,
});

Configuration Options

import { createX402Client } from '@armory-sh/client-viem';

const client = createX402Client({
  wallet: { type: 'account', account },

  // Protocol version (default: 2)
  version: 2,

  // Authorization expiry in seconds (default: 3600)
  defaultExpiry: 7200,

  // Custom nonce generator - MUST return bytes32 hex string
  nonceGenerator: () => `0x${Date.now().toString(16).padStart(64, '0')}` as `0x${string}`,

  // Enable debug logging
  debug: true,
});
Nonce Format: The nonceGenerator must return a bytes32 hex string (64 hex characters after 0x). This is required for EIP-712 signature compatibility with the x402 specification.

Extension Hooks

The client supports an extension hook system for handling protocol extensions like Sign-In-With-X and Payment Identifier.

Using Hooks

import { createX402Client } from '@armory-sh/client-viem';
import { createSIWxHook, createPaymentIdHook } from '@armory-sh/extensions';
import { PaymentPreference, Logger } from '@armory-sh/client-hooks';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount('0x...');

const client = createX402Client({
  wallet: { type: 'account', account },
  hooks: [
    createSIWxHook({
      domain: 'example.com',
      statement: 'Sign in to access premium content'
    }),
    createPaymentIdHook(),
    PaymentPreference.chain(['base', 'polygon', 'skale']),
    PaymentPreference.token(['USDT', 'USDC', 'WBTC']),
    PaymentPreference.cheapest(),
    Logger.console(),
  ]
});

// Hooks automatically add extensions when server requests them
const response = await client.fetch('https://api.example.com/protected');

Hook Order

Hooks execute in array order.

Custom Hooks

Create custom hooks for your own extensions:
import { createCustomHook } from '@armory-sh/extensions';

const myHook = createCustomHook({
  key: 'my-extension',
  handler: async (context) => {
    // context.payload - the payment payload (when available)
    // context.paymentContext - the original payment required context
    // context.serverExtensions - extensions requested by server

    if (context.payload) {
      context.payload.extensions = {
        ...(context.payload.extensions ?? {}),
        'my-extension': { customData: 'value' }
      };
    }
  },
  priority: 75
});

const client = createX402Client({
  wallet: { type: 'account', account },
  hooks: [myHook]
});

Hook Execution Points

Hooks can execute at:
  1. onPaymentRequired
  2. selectRequirement
  3. beforeSignPayment
  4. afterPaymentResponse
  5. onError

Exports

ExportDescription
createX402ClientCreate a full x402 client
createX402TransportCreate a fetch function for payments
armoryPayOne-line payment function
executeHooksExecute extension hooks
mergeExtensionsMerge extension objects