Skip to main content
Optional preset hooks for Armory clients.

Installation

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

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

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

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

API Reference

PaymentPreference

Chain and token selection hooks.

PaymentPreference.chain(preferredChains)

Prefer specific networks when multiple options are available.
import { PaymentPreference } from '@armory-sh/client-hooks';

const hook = PaymentPreference.chain(['base', 'ethereum', 'skale-base']);
Parameter:
  • preferredChains - Array of network keys (string or string[])
Supported network keys:
  • ethereum - Ethereum Mainnet
  • base - Base Mainnet
  • base-sepolia - Base Sepolia Testnet
  • skale-base - SKALE Base
  • skale-base-sepolia - SKALE Base Sepolia
  • ethereum-sepolia - Ethereum Sepolia Testnet

PaymentPreference.token(preferredTokens)

Prefer specific tokens when multiple options are available.
const hook = PaymentPreference.token(['USDT', 'USDC', 'WBTC']);
Parameter:
  • preferredTokens - Array of token symbols (string or string[])

PaymentPreference.cheapest()

Select the option with the lowest amount within the selected network and asset.
const hook = PaymentPreference.cheapest();

Logger

Payment event logging.

Logger.console(options?)

Log payment events to the console.
import { Logger } from '@armory-sh/client-hooks';

const hook = Logger.console({
  prefix: '[x402]',
  enabled: true
});
Options:
OptionTypeDefaultDescription
prefixstring"[x402]"Log prefix
enabledbooleantrueEnable logging

Utilities

combineHooks(…hooks)

Combine multiple hook arrays into a single array.
import { combineHooks } from '@armory-sh/client-hooks';

const hooks = combineHooks(
  PaymentPreference.chain(['base']),
  [PaymentPreference.token(['USDC'])], // Can pass arrays
  null, // Nulls/undefined are filtered
);

Types

import type {
  PaymentRequiredContext,
  PaymentPayloadContext,
  ClientHookErrorContext,
  ClientHook,
} from '@armory-sh/client-hooks';

PaymentRequiredContext

Context passed when payment is required.
interface PaymentRequiredContext {
  url: RequestInfo | URL;
  requestInit: RequestInit | undefined;
  accepts: PaymentRequirementsV2[];
  requirements: PaymentRequirementsV2;
  selectedRequirement?: PaymentRequirementsV2;
  serverExtensions: Extensions | undefined;
  fromAddress: Address;
  nonce: `0x${string}`;
  validBefore: number;
}

PaymentPayloadContext

Context passed before signing payment.
interface PaymentPayloadContext<TWallet = unknown> {
  payload: PaymentPayloadV2;
  requirements: PaymentRequirementsV2;
  wallet: TWallet;
  paymentContext: PaymentRequiredContext;
}

ClientHookErrorContext

Context passed when a hook errors.
interface ClientHookErrorContext {
  error: unknown;
  phase: 'onPaymentRequired' | 'selectRequirement' | 'beforeSignPayment' | 'afterPaymentResponse';
}

ClientHook

Hook interface.
interface ClientHook<TWallet = unknown> {
  name?: string;
  onPaymentRequired?: (context: PaymentRequiredContext) => void | Promise<void>;
  selectRequirement?: (
    context: PaymentRequiredContext
  ) => PaymentRequirementsV2 | undefined | Promise<PaymentRequirementsV2 | undefined>;
  beforeSignPayment?: (
    context: PaymentPayloadContext<TWallet>
  ) => void | Promise<void>;
  afterPaymentResponse?: (
    context: PaymentPayloadContext<TWallet> & { response: Response }
  ) => void | Promise<void>;
  onError?: (context: ClientHookErrorContext) => void | Promise<void>;
}

Usage

import { createX402Client } from '@armory-sh/client-viem';
import { PaymentPreference, Logger } from '@armory-sh/client-hooks';

const client = createX402Client({
  wallet: { type: 'account', account },
  hooks: [
    PaymentPreference.chain(['base', 'ethereum', 'skale-base']),
    PaymentPreference.token(['USDT', 'USDC', 'WBTC']),
    PaymentPreference.cheapest(),
    Logger.console(),
  ],
});

Selection Behavior

  • Hook selection narrows progressively in array order
  • Chain preference runs first, then token preference can narrow within selected chain
  • Cheapest refines within the selected network and asset
  • If no hook selects, clients fall back to the first compatible accepts[] option from PAYMENT-REQUIRED

Notes

  • This package is optional. Core clients work without it.
  • Hooks are fail-closed by default: if a hook throws, payment flow throws.