import { C as Caip2, X as X402AcceptEntry, y as X402AnyAccept, B as X402ExactAcceptEntry, e as ExactPaymentPayloadAny, q as SignedReceipt, x as VerifyResult, I as X402UptoAcceptEntry, o as Permit2UptoPaymentPayload, w as SpendSummary, z as X402Challenge, F as X402Receipt, S as SettleOutcome, v as SpendStore, t as SpendLedger, u as SpendRecord, p as PipRailReceipt, V as VerifyErrorCode, a as AssetId, A as AddressId, P as PaidReceipt } from './ledger-uFtXlIHY.js'; export { E as EXT_OFFER_RECEIPT, b as EXT_PAYMENT_IDENTIFIER, c as ExactAuthorizationWire, d as ExactPaymentPayload, H as HEADER_REQUIRED, f as HEADER_RESPONSE, g as HEADER_RESPONSE_V1, h as HEADER_SIGNATURE, i as HEADER_SIGNATURE_V1, j as ParsedExactPayment, k as ParsedUptoPayment, l as Permit2Authorization, m as Permit2PaymentPayload, n as Permit2UptoAuthorization, r as SpendAssetTotal, s as SpendDenomTotal, D as X402PaymentSignature, G as X402ResourceObject, J as buildChallengeHeader, K as buildExactSignatureHeader, L as buildPaymentIdentifierAdvertisement, M as buildReceiptExtension, N as buildReceiptHeader, O as buildSignatureHeader, Q as buildUptoSignatureHeader, R as decodeBase64Json, T as memorySpendStore, U as parseChallenge, W as parseExactObject, Y as parseExactPaymentHeader, Z as parseReceipt, _ as parseReceiptExtension, $ as parseSettleResponse, a0 as parseSignatureHeader, a1 as parseSignatureObject, a2 as parseUptoObject, a3 as parseUptoPaymentHeader, a4 as pickAccept, a5 as readPaymentIdentifier } from './ledger-uFtXlIHY.js'; import * as viem_zksync from 'viem/zksync'; import * as abitype from 'abitype'; import * as viem_chains from 'viem/chains'; import * as viem from 'viem'; import { Chain, Account, Hex, PublicClient } from 'viem'; /** * ── EVM SECTION: chains ── * Chains are a parameter, and the popular ones are built in. * * The easy path — name a built-in chain: * requirePayment({ chain: 'base', token: 'USDC', amount: '0.05', payTo }) // USDC on Base * * The exotic path — ANY EVM chain we don't ship, by viem `Chain` or a bare * `{ id, rpcUrl }`, plus the token you want paid in: * requirePayment({ chain: someViemChain, token: { address, decimals }, … }) * requirePayment({ chain: { id: 1313161554, rpcUrl: 'https://mainnet.aurora.dev' }, * token: { address: '0x…', decimals: 6 }, … }) * * If viem can reach the RPC, PipRail works on it — there is no allowlist. */ interface TokenInfo { address: `0x${string}`; decimals: number; symbol: string; } interface ChainPreset { /** The underlying viem chain (id, name, native coin, default RPCs). */ chain: Chain; /** * Override the default RPC used when the caller passes no `rpcUrl`. Set only * where viem's bundled default is unreliable. Public RPCs are rate-limited — * production callers should always pass their own `rpcUrl`. */ defaultRpc?: string; /** Well-known tokens on this chain, keyed by UPPERCASE symbol. */ tokens: Record; } /** * Built-in EVM mainnets, each with canonical USDC (decimals included) * pre-filled so a developer never pastes a token address. Add a chain = one * entry here. */ declare const CHAINS: { ethereum: { chain: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://etherscan.io"; readonly apiUrl: "https://api.etherscan.io/api"; }; }; blockTime: 12000; contracts: { readonly ensUniversalResolver: { readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee"; readonly blockCreated: 23085558; }; readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 14353601; }; }; ensTlds?: readonly string[] | undefined; id: 1; name: "Ethereum"; nativeCurrency: { readonly name: "Ether"; readonly symbol: "ETH"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://eth.merkle.io"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; defaultRpc: string; tokens: { USDC: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; decimals: number; symbol: string; }; USDT: { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7"; decimals: number; symbol: string; }; EURC: { address: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c"; decimals: number; symbol: string; }; }; }; base: { chain: { blockExplorers: { readonly default: { readonly name: "Basescan"; readonly url: "https://basescan.org"; readonly apiUrl: "https://api.basescan.org/api"; }; }; blockTime: 2000; contracts: { readonly disputeGameFactory: { readonly 1: { readonly address: "0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e"; }; }; readonly l2OutputOracle: { readonly 1: { readonly address: "0x56315b90c40730925ec5485cf004d835058518A0"; }; }; readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 5022; }; readonly portal: { readonly 1: { readonly address: "0x49048044D57e1C92A77f79988d21Fa8fAF74E97e"; readonly blockCreated: 17482143; }; }; readonly l1StandardBridge: { readonly 1: { readonly address: "0x3154Cf16ccdb4C6d922629664174b904d80F2C35"; readonly blockCreated: 17482143; }; }; readonly gasPriceOracle: { readonly address: "0x420000000000000000000000000000000000000F"; }; readonly l1Block: { readonly address: "0x4200000000000000000000000000000000000015"; }; readonly l2CrossDomainMessenger: { readonly address: "0x4200000000000000000000000000000000000007"; }; readonly l2Erc721Bridge: { readonly address: "0x4200000000000000000000000000000000000014"; }; readonly l2StandardBridge: { readonly address: "0x4200000000000000000000000000000000000010"; }; readonly l2ToL1MessagePasser: { readonly address: "0x4200000000000000000000000000000000000016"; }; }; ensTlds?: readonly string[] | undefined; id: 8453; name: "Base"; nativeCurrency: { readonly name: "Ether"; readonly symbol: "ETH"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://mainnet.base.org"]; }; }; sourceId: 1; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters: { readonly block: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcBlock, action?: string | undefined) => { baseFeePerGas: bigint | null; blobGasUsed: bigint; difficulty: bigint; excessBlobGas: bigint; extraData: viem.Hex; gasLimit: bigint; gasUsed: bigint; hash: `0x${string}` | null; logsBloom: `0x${string}` | null; miner: abitype.Address; mixHash: viem.Hash; nonce: `0x${string}` | null; number: bigint | null; parentBeaconBlockRoot?: `0x${string}` | undefined; parentHash: viem.Hash; receiptsRoot: viem.Hex; sealFields: viem.Hex[]; sha3Uncles: viem.Hash; size: bigint; stateRoot: viem.Hash; timestamp: bigint; totalDifficulty: bigint | null; transactions: `0x${string}`[] | viem_chains.OpStackTransaction[]; transactionsRoot: viem.Hash; uncles: viem.Hash[]; withdrawals?: viem.Withdrawal[] | undefined | undefined; withdrawalsRoot?: `0x${string}` | undefined; } & {}; type: "block"; }; readonly transaction: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcTransaction, action?: string | undefined) => ({ blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: boolean; mint?: bigint | undefined | undefined; sourceHash: viem.Hex; type: "deposit"; } | { r: viem.Hex; s: viem.Hex; v: bigint; to: abitype.Address | null; from: abitype.Address; gas: bigint; nonce: number; value: bigint; blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; hash: viem.Hash; input: viem.Hex; transactionIndex: number | null; typeHex: viem.Hex | null; accessList?: undefined | undefined; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId?: number | undefined; yParity?: undefined | undefined; type: "legacy"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip2930"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip1559"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes: readonly viem.Hex[]; chainId: number; type: "eip4844"; gasPrice?: undefined | undefined; maxFeePerBlobGas: bigint; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList: viem.SignedAuthorizationList; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip7702"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; }) & {}; type: "transaction"; }; readonly transactionReceipt: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcTransactionReceipt, action?: string | undefined) => { blobGasPrice?: bigint | undefined; blobGasUsed?: bigint | undefined; blockHash: viem.Hash; blockNumber: bigint; blockTimestamp?: bigint | undefined; contractAddress: abitype.Address | null | undefined; cumulativeGasUsed: bigint; effectiveGasPrice: bigint; from: abitype.Address; gasUsed: bigint; logs: viem.Log[]; logsBloom: viem.Hex; root?: `0x${string}` | undefined; status: "success" | "reverted"; to: abitype.Address | null; transactionHash: viem.Hash; transactionIndex: number; type: viem.TransactionType; l1GasPrice: bigint | null; l1GasUsed: bigint | null; l1Fee: bigint | null; l1FeeScalar: number | null; } & {}; type: "transactionReceipt"; }; }; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers: { readonly transaction: typeof viem_chains.serializeTransactionOpStack; }; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; decimals: number; symbol: string; }; EURC: { address: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"; decimals: number; symbol: string; }; }; }; arbitrum: { chain: { blockExplorers: { readonly default: { readonly name: "Arbiscan"; readonly url: "https://arbiscan.io"; readonly apiUrl: "https://api.arbiscan.io/api"; }; }; blockTime: 250; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 7654707; }; }; ensTlds?: readonly string[] | undefined; id: 42161; name: "Arbitrum One"; nativeCurrency: { readonly name: "Ether"; readonly symbol: "ETH"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://arb1.arbitrum.io/rpc"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"; decimals: number; symbol: string; }; USDT: { address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"; decimals: number; symbol: string; }; }; }; optimism: { chain: { blockExplorers: { readonly default: { readonly name: "Optimism Explorer"; readonly url: "https://optimistic.etherscan.io"; readonly apiUrl: "https://api-optimistic.etherscan.io/api"; }; }; blockTime: 2000; contracts: { readonly disputeGameFactory: { readonly 1: { readonly address: "0xe5965Ab5962eDc7477C8520243A95517CD252fA9"; }; }; readonly l2OutputOracle: { readonly 1: { readonly address: "0xdfe97868233d1aa22e815a266982f2cf17685a27"; }; }; readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 4286263; }; readonly portal: { readonly 1: { readonly address: "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"; }; }; readonly l1StandardBridge: { readonly 1: { readonly address: "0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"; }; }; readonly gasPriceOracle: { readonly address: "0x420000000000000000000000000000000000000F"; }; readonly l1Block: { readonly address: "0x4200000000000000000000000000000000000015"; }; readonly l2CrossDomainMessenger: { readonly address: "0x4200000000000000000000000000000000000007"; }; readonly l2Erc721Bridge: { readonly address: "0x4200000000000000000000000000000000000014"; }; readonly l2StandardBridge: { readonly address: "0x4200000000000000000000000000000000000010"; }; readonly l2ToL1MessagePasser: { readonly address: "0x4200000000000000000000000000000000000016"; }; }; ensTlds?: readonly string[] | undefined; id: 10; name: "OP Mainnet"; nativeCurrency: { readonly name: "Ether"; readonly symbol: "ETH"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://mainnet.optimism.io"]; }; }; sourceId: 1; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters: { readonly block: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcBlock, action?: string | undefined) => { baseFeePerGas: bigint | null; blobGasUsed: bigint; difficulty: bigint; excessBlobGas: bigint; extraData: viem.Hex; gasLimit: bigint; gasUsed: bigint; hash: `0x${string}` | null; logsBloom: `0x${string}` | null; miner: abitype.Address; mixHash: viem.Hash; nonce: `0x${string}` | null; number: bigint | null; parentBeaconBlockRoot?: `0x${string}` | undefined; parentHash: viem.Hash; receiptsRoot: viem.Hex; sealFields: viem.Hex[]; sha3Uncles: viem.Hash; size: bigint; stateRoot: viem.Hash; timestamp: bigint; totalDifficulty: bigint | null; transactions: `0x${string}`[] | viem_chains.OpStackTransaction[]; transactionsRoot: viem.Hash; uncles: viem.Hash[]; withdrawals?: viem.Withdrawal[] | undefined | undefined; withdrawalsRoot?: `0x${string}` | undefined; } & {}; type: "block"; }; readonly transaction: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcTransaction, action?: string | undefined) => ({ blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: boolean; mint?: bigint | undefined | undefined; sourceHash: viem.Hex; type: "deposit"; } | { r: viem.Hex; s: viem.Hex; v: bigint; to: abitype.Address | null; from: abitype.Address; gas: bigint; nonce: number; value: bigint; blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; hash: viem.Hash; input: viem.Hex; transactionIndex: number | null; typeHex: viem.Hex | null; accessList?: undefined | undefined; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId?: number | undefined; yParity?: undefined | undefined; type: "legacy"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip2930"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip1559"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes: readonly viem.Hex[]; chainId: number; type: "eip4844"; gasPrice?: undefined | undefined; maxFeePerBlobGas: bigint; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList: viem.SignedAuthorizationList; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip7702"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; }) & {}; type: "transaction"; }; readonly transactionReceipt: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcTransactionReceipt, action?: string | undefined) => { blobGasPrice?: bigint | undefined; blobGasUsed?: bigint | undefined; blockHash: viem.Hash; blockNumber: bigint; blockTimestamp?: bigint | undefined; contractAddress: abitype.Address | null | undefined; cumulativeGasUsed: bigint; effectiveGasPrice: bigint; from: abitype.Address; gasUsed: bigint; logs: viem.Log[]; logsBloom: viem.Hex; root?: `0x${string}` | undefined; status: "success" | "reverted"; to: abitype.Address | null; transactionHash: viem.Hash; transactionIndex: number; type: viem.TransactionType; l1GasPrice: bigint | null; l1GasUsed: bigint | null; l1Fee: bigint | null; l1FeeScalar: number | null; } & {}; type: "transactionReceipt"; }; }; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers: { readonly transaction: typeof viem_chains.serializeTransactionOpStack; }; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"; decimals: number; symbol: string; }; USDT: { address: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58"; decimals: number; symbol: string; }; }; }; polygon: { chain: { blockExplorers: { readonly default: { readonly name: "PolygonScan"; readonly url: "https://polygonscan.com"; readonly apiUrl: "https://api.etherscan.io/v2/api"; }; }; blockTime: 2000; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 25770160; }; }; ensTlds?: readonly string[] | undefined; id: 137; name: "Polygon"; nativeCurrency: { readonly name: "POL"; readonly symbol: "POL"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://polygon.drpc.org"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"; decimals: number; symbol: string; }; USDT: { address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F"; decimals: number; symbol: string; }; }; }; bnb: { chain: { blockExplorers: { readonly default: { readonly name: "BscScan"; readonly url: "https://bscscan.com"; readonly apiUrl: "https://api.bscscan.com/api"; }; }; blockTime: 750; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 15921452; }; }; ensTlds?: readonly string[] | undefined; id: 56; name: "BNB Smart Chain"; nativeCurrency: { readonly decimals: 18; readonly name: "BNB"; readonly symbol: "BNB"; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://56.rpc.thirdweb.com"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"; decimals: number; symbol: string; }; USDT: { address: "0x55d398326f99059fF775485246999027B3197955"; decimals: number; symbol: string; }; FDUSD: { address: "0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409"; decimals: number; symbol: string; }; USD1: { address: "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d"; decimals: number; symbol: string; }; U: { address: "0xcE24439F2D9C6a2289F741120FE202248B666666"; decimals: number; symbol: string; }; }; }; avalanche: { chain: { blockExplorers: { readonly default: { readonly name: "SnowTrace"; readonly url: "https://snowtrace.io"; readonly apiUrl: "https://api.snowtrace.io"; }; }; blockTime: 1700; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 11907934; }; }; ensTlds?: readonly string[] | undefined; id: 43114; name: "Avalanche"; nativeCurrency: { readonly decimals: 18; readonly name: "Avalanche"; readonly symbol: "AVAX"; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://api.avax.network/ext/bc/C/rpc"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"; decimals: number; symbol: string; }; USDT: { address: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7"; decimals: number; symbol: string; }; EURC: { address: "0xC891EB4cbdEFf6e073e859e987815Ed1505c2ACD"; decimals: number; symbol: string; }; }; }; mantle: { chain: { blockExplorers: { readonly default: { readonly name: "Mantle Explorer"; readonly url: "https://mantlescan.xyz/"; readonly apiUrl: "https://api.mantlescan.xyz/api"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11"; readonly blockCreated: 304717; }; }; ensTlds?: readonly string[] | undefined; id: 5000; name: "Mantle"; nativeCurrency: { readonly decimals: 18; readonly name: "MNT"; readonly symbol: "MNT"; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://rpc.mantle.xyz"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9"; decimals: number; symbol: string; }; USDT: { address: "0x201EBa5CC46D216Ce6DC03F6a759e8E766e956aE"; decimals: number; symbol: string; }; }; }; sonic: { chain: { blockExplorers: { readonly default: { readonly name: "Sonic Explorer"; readonly url: "https://sonicscan.org"; }; }; blockTime: 630; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 60; }; }; ensTlds?: readonly string[] | undefined; id: 146; name: "Sonic"; nativeCurrency: { readonly decimals: 18; readonly name: "Sonic"; readonly symbol: "S"; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://rpc.soniclabs.com"]; }; }; sourceId?: number | undefined | undefined; testnet: false; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894"; decimals: number; symbol: string; }; USDT: { address: "0x6047828dc181963ba44974801FF68e538dA5eaF9"; decimals: number; symbol: string; }; }; }; linea: { chain: { blockExplorers: { readonly default: { readonly name: "Etherscan"; readonly url: "https://lineascan.build"; readonly apiUrl: "https://api.lineascan.build/api"; }; }; blockTime: 2000; contracts: { readonly multicall3: { readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11"; readonly blockCreated: 42; }; readonly ensRegistry: { readonly address: "0x50130b669B28C339991d8676FA73CF122a121267"; readonly blockCreated: 6682888; }; readonly ensUniversalResolver: { readonly address: "0x4D41762915F83c76EcaF6776d9b08076aA32b492"; readonly blockCreated: 22222151; }; }; ensTlds: readonly [".linea.eth"]; id: 59144; name: "Linea Mainnet"; nativeCurrency: { readonly name: "Linea Ether"; readonly symbol: "ETH"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://rpc.linea.build"]; readonly webSocket: readonly ["wss://rpc.linea.build"]; }; }; sourceId?: number | undefined | undefined; testnet: false; custom?: Record | undefined; extendSchema?: Record | undefined; fees: { readonly estimateFeesPerGas: ({ client, multiply, request, type, }: Parameters[0]) => ReturnType; readonly maxPriorityFeePerGas: ({ block, client, request }: viem.ChainFeesFnParameters) => Promise; }; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x176211869cA2b568f2A7D4EE941E073a821EE1ff"; decimals: number; symbol: string; }; USDT: { address: "0xA219439258ca9da29E9Cc4cE5596924745e12B93"; decimals: number; symbol: string; }; }; }; scroll: { chain: { blockExplorers: { readonly default: { readonly name: "Scrollscan"; readonly url: "https://scrollscan.com"; readonly apiUrl: "https://api.scrollscan.com/api"; }; }; blockTime: 3000; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 14; }; }; ensTlds?: readonly string[] | undefined; id: 534352; name: "Scroll"; nativeCurrency: { readonly name: "Ether"; readonly symbol: "ETH"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://rpc.scroll.io"]; readonly webSocket: readonly ["wss://wss-rpc.scroll.io/ws"]; }; }; sourceId?: number | undefined | undefined; testnet: false; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4"; decimals: number; symbol: string; }; USDT: { address: "0xf55BEC9cafDbE8730f096Aa55dad6D22d44099Df"; decimals: number; symbol: string; }; }; }; celo: { chain: { blockExplorers: { readonly default: { readonly name: "Celo Explorer"; readonly url: "https://celoscan.io"; readonly apiUrl: "https://api.celoscan.io/api"; }; }; blockTime: 1000; contracts: { readonly multicall3: { readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11"; readonly blockCreated: 13112599; }; }; ensTlds?: readonly string[] | undefined; id: 42220; name: "Celo"; nativeCurrency: { readonly decimals: 18; readonly name: "CELO"; readonly symbol: "CELO"; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://forno.celo.org"]; }; }; sourceId?: number | undefined | undefined; testnet: false; custom?: Record | undefined; extendSchema?: Record | undefined; fees: viem.ChainFees<{ readonly block: { exclude: [] | undefined; format: (args: viem_chains.CeloRpcBlock, action?: string | undefined) => { baseFeePerGas: bigint | null; blobGasUsed: bigint; difficulty: bigint; excessBlobGas: bigint; extraData: viem.Hex; gasLimit: bigint; gasUsed: bigint; hash: `0x${string}` | null; logsBloom: `0x${string}` | null; miner: abitype.Address; mixHash: viem.Hash; nonce: `0x${string}` | null; number: bigint | null; parentBeaconBlockRoot?: `0x${string}` | undefined; parentHash: viem.Hash; receiptsRoot: viem.Hex; sealFields: viem.Hex[]; sha3Uncles: viem.Hash; size: bigint; stateRoot: viem.Hash; timestamp: bigint; totalDifficulty: bigint | null; transactions: `0x${string}`[] | viem_chains.CeloTransaction[]; transactionsRoot: viem.Hash; uncles: viem.Hash[]; withdrawals?: viem.Withdrawal[] | undefined | undefined; withdrawalsRoot?: `0x${string}` | undefined; } & {}; type: "block"; }; readonly transaction: { exclude: [] | undefined; format: (args: viem_chains.CeloRpcTransaction, action?: string | undefined) => ({ r: viem.Hex; s: viem.Hex; v: bigint; to: abitype.Address | null; from: abitype.Address; gas: bigint; nonce: number; value: bigint; blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; hash: viem.Hash; input: viem.Hex; transactionIndex: number | null; typeHex: viem.Hex | null; accessList?: undefined | undefined; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId?: number | undefined; yParity?: undefined | undefined; type: "legacy"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip2930"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip1559"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes: readonly viem.Hex[]; chainId: number; type: "eip4844"; gasPrice?: undefined | undefined; maxFeePerBlobGas: bigint; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList: viem.SignedAuthorizationList; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip7702"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; accessList: viem.AccessList; chainId: number; feeCurrency: abitype.Address | null; gatewayFee: bigint | null; gatewayFeeRecipient: abitype.Address | null; type: "cip42"; blobVersionedHashes?: undefined; authorizationList?: undefined; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; accessList: viem.AccessList; chainId: number; feeCurrency: abitype.Address | null; type: "cip64"; blobVersionedHashes?: undefined; authorizationList?: undefined; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: boolean; mint?: bigint | undefined | undefined; sourceHash: viem.Hex; type: "deposit"; blobVersionedHashes?: undefined; accessList?: undefined; authorizationList?: undefined; chainId?: undefined; feeCurrency?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; }) & {}; type: "transaction"; }; readonly transactionRequest: { exclude: [] | undefined; format: (args: viem_chains.CeloTransactionRequest, action?: string | undefined) => ({ data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x0" | undefined; value?: `0x${string}` | undefined; gasPrice?: `0x${string}` | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; accessList?: undefined; sidecars?: undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x1" | undefined; value?: `0x${string}` | undefined; gasPrice?: `0x${string}` | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; accessList?: viem.AccessList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x2" | undefined; value?: `0x${string}` | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { type?: "0x3" | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; to: `0x${string}` | null; gasPrice?: undefined | undefined; maxFeePerBlobGas?: `0x${string}` | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; sidecars?: readonly viem.BlobSidecar<`0x${string}`>[] | undefined; blobs?: readonly `0x${string}`[] | readonly viem.ByteArray[] | undefined; blobVersionedHashes: readonly viem.Hex[]; kzg?: undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { type?: "0x3" | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; to: `0x${string}` | null; gasPrice?: undefined | undefined; maxFeePerBlobGas?: `0x${string}` | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; sidecars?: readonly viem.BlobSidecar<`0x${string}`>[] | undefined; blobs: readonly viem.Hex[] | readonly viem.ByteArray[]; blobVersionedHashes?: readonly `0x${string}`[] | undefined; kzg?: viem.Kzg | undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { type?: "0x4" | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; authorizationList?: viem.RpcAuthorizationList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; feeCurrency?: `0x${string}` | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x7b" | undefined; value?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; feeCurrency?: `0x${string}` | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; authorizationList?: undefined; }) & {}; type: "transactionRequest"; }; }>; formatters: { readonly block: { exclude: [] | undefined; format: (args: viem_chains.CeloRpcBlock, action?: string | undefined) => { baseFeePerGas: bigint | null; blobGasUsed: bigint; difficulty: bigint; excessBlobGas: bigint; extraData: viem.Hex; gasLimit: bigint; gasUsed: bigint; hash: `0x${string}` | null; logsBloom: `0x${string}` | null; miner: abitype.Address; mixHash: viem.Hash; nonce: `0x${string}` | null; number: bigint | null; parentBeaconBlockRoot?: `0x${string}` | undefined; parentHash: viem.Hash; receiptsRoot: viem.Hex; sealFields: viem.Hex[]; sha3Uncles: viem.Hash; size: bigint; stateRoot: viem.Hash; timestamp: bigint; totalDifficulty: bigint | null; transactions: `0x${string}`[] | viem_chains.CeloTransaction[]; transactionsRoot: viem.Hash; uncles: viem.Hash[]; withdrawals?: viem.Withdrawal[] | undefined | undefined; withdrawalsRoot?: `0x${string}` | undefined; } & {}; type: "block"; }; readonly transaction: { exclude: [] | undefined; format: (args: viem_chains.CeloRpcTransaction, action?: string | undefined) => ({ r: viem.Hex; s: viem.Hex; v: bigint; to: abitype.Address | null; from: abitype.Address; gas: bigint; nonce: number; value: bigint; blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; hash: viem.Hash; input: viem.Hex; transactionIndex: number | null; typeHex: viem.Hex | null; accessList?: undefined | undefined; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId?: number | undefined; yParity?: undefined | undefined; type: "legacy"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip2930"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip1559"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes: readonly viem.Hex[]; chainId: number; type: "eip4844"; gasPrice?: undefined | undefined; maxFeePerBlobGas: bigint; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList: viem.SignedAuthorizationList; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip7702"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; feeCurrency: abitype.Address | null; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; accessList: viem.AccessList; chainId: number; feeCurrency: abitype.Address | null; gatewayFee: bigint | null; gatewayFeeRecipient: abitype.Address | null; type: "cip42"; blobVersionedHashes?: undefined; authorizationList?: undefined; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; accessList: viem.AccessList; chainId: number; feeCurrency: abitype.Address | null; type: "cip64"; blobVersionedHashes?: undefined; authorizationList?: undefined; mint?: undefined; isSystemTx?: undefined; sourceHash?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: boolean; mint?: bigint | undefined | undefined; sourceHash: viem.Hex; type: "deposit"; blobVersionedHashes?: undefined; accessList?: undefined; authorizationList?: undefined; chainId?: undefined; feeCurrency?: undefined; gatewayFee?: undefined; gatewayFeeRecipient?: undefined; }) & {}; type: "transaction"; }; readonly transactionRequest: { exclude: [] | undefined; format: (args: viem_chains.CeloTransactionRequest, action?: string | undefined) => ({ data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x0" | undefined; value?: `0x${string}` | undefined; gasPrice?: `0x${string}` | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; accessList?: undefined; sidecars?: undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x1" | undefined; value?: `0x${string}` | undefined; gasPrice?: `0x${string}` | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; accessList?: viem.AccessList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x2" | undefined; value?: `0x${string}` | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { type?: "0x3" | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; to: `0x${string}` | null; gasPrice?: undefined | undefined; maxFeePerBlobGas?: `0x${string}` | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; sidecars?: readonly viem.BlobSidecar<`0x${string}`>[] | undefined; blobs?: readonly `0x${string}`[] | readonly viem.ByteArray[] | undefined; blobVersionedHashes: readonly viem.Hex[]; kzg?: undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { type?: "0x3" | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; to: `0x${string}` | null; gasPrice?: undefined | undefined; maxFeePerBlobGas?: `0x${string}` | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; sidecars?: readonly viem.BlobSidecar<`0x${string}`>[] | undefined; blobs: readonly viem.Hex[] | readonly viem.ByteArray[]; blobVersionedHashes?: readonly `0x${string}`[] | undefined; kzg?: viem.Kzg | undefined; authorizationList?: undefined; feeCurrency?: `0x${string}` | undefined; } | { type?: "0x4" | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; authorizationList?: viem.RpcAuthorizationList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; feeCurrency?: `0x${string}` | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x7b" | undefined; value?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; feeCurrency?: `0x${string}` | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; authorizationList?: undefined; }) & {}; type: "transactionRequest"; }; }; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers: { readonly transaction: typeof viem_chains.serializeTransactionCelo; }; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C"; decimals: number; symbol: string; }; USDT: { address: "0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e"; decimals: number; symbol: string; }; }; }; zksync: { chain: { blockExplorers: { readonly default: { readonly name: "ZKsync Explorer"; readonly url: "https://explorer.zksync.io/"; readonly apiUrl: "https://block-explorer-api.mainnet.zksync.io/api"; }; }; blockTime: 200; contracts: { readonly multicall3: { readonly address: "0xF9cda624FBC7e059355ce98a31693d299FACd963"; readonly blockCreated: 3908235; }; readonly erc6492Verifier: { readonly address: "0xfB688330379976DA81eB64Fe4BF50d7401763B9C"; readonly blockCreated: 45659388; }; }; ensTlds?: readonly string[] | undefined; id: 324; name: "ZKsync Era"; nativeCurrency: { readonly decimals: 18; readonly name: "Ether"; readonly symbol: "ETH"; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://mainnet.era.zksync.io"]; readonly webSocket: readonly ["wss://mainnet.era.zksync.io/ws"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom: { readonly getEip712Domain: viem_zksync.EIP712DomainFn; }; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters: { readonly block: { exclude: [] | undefined; format: (args: viem_zksync.ZkSyncRpcBlock, action?: string | undefined) => { baseFeePerGas: bigint | null; blobGasUsed: bigint; difficulty: bigint; excessBlobGas: bigint; extraData: viem.Hex; gasLimit: bigint; gasUsed: bigint; hash: `0x${string}` | null; logsBloom: `0x${string}` | null; miner: abitype.Address; mixHash: viem.Hash; nonce: `0x${string}` | null; number: bigint | null; parentBeaconBlockRoot?: `0x${string}` | undefined; parentHash: viem.Hash; receiptsRoot: viem.Hex; sealFields: viem.Hex[]; sha3Uncles: viem.Hash; size: bigint; stateRoot: viem.Hash; timestamp: bigint; totalDifficulty: bigint | null; transactions: `0x${string}`[] | viem_zksync.ZkSyncTransaction[]; transactionsRoot: viem.Hash; uncles: viem.Hash[]; withdrawals?: viem.Withdrawal[] | undefined | undefined; withdrawalsRoot?: `0x${string}` | undefined; l1BatchNumber: bigint | null; l1BatchTimestamp: bigint | null; } & {}; type: "block"; }; readonly transaction: { exclude: [] | undefined; format: (args: viem_zksync.ZkSyncRpcTransaction, action?: string | undefined) => ({ r: viem.Hex; s: viem.Hex; v: bigint; to: abitype.Address | null; from: abitype.Address; gas: bigint; nonce: number; value: bigint; blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; hash: viem.Hash; input: viem.Hex; transactionIndex: number | null; typeHex: viem.Hex | null; accessList?: undefined | undefined; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId?: number | undefined; yParity?: undefined | undefined; type: "legacy"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; l1BatchNumber: bigint | null; l1BatchTxIndex: bigint | null; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip2930"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; l1BatchNumber: bigint | null; l1BatchTxIndex: bigint | null; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip1559"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; l1BatchNumber: bigint | null; l1BatchTxIndex: bigint | null; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes: readonly viem.Hex[]; chainId: number; type: "eip4844"; gasPrice?: undefined | undefined; maxFeePerBlobGas: bigint; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; l1BatchNumber: bigint | null; l1BatchTxIndex: bigint | null; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList: viem.SignedAuthorizationList; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip7702"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; l1BatchNumber: bigint | null; l1BatchTxIndex: bigint | null; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; l1BatchNumber: bigint | null; l1BatchTxIndex: bigint | null; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; type: "priority"; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; l1BatchNumber: bigint | null; l1BatchTxIndex: bigint | null; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; type: "eip712" | "priority"; }) & {}; type: "transaction"; }; readonly transactionReceipt: { exclude: [] | undefined; format: (args: viem_zksync.ZkSyncRpcTransactionReceipt, action?: string | undefined) => { type: viem_zksync.ZkSyncTransactionType; contractAddress: abitype.Address | null | undefined; to: abitype.Address | null; from: abitype.Address; blockHash: viem.Hash; blockNumber: bigint; blockTimestamp?: bigint | undefined; transactionIndex: number; status: "success" | "reverted"; transactionHash: viem.Hash; logsBloom: viem.Hex; blobGasUsed?: bigint | undefined; gasUsed: bigint; blobGasPrice?: bigint | undefined; cumulativeGasUsed: bigint; effectiveGasPrice: bigint; root?: `0x${string}` | undefined; l1BatchNumber: bigint | null; l1BatchTxIndex: bigint | null; logs: viem_zksync.ZkSyncLog[]; l2ToL1Logs: viem_zksync.ZkSyncL2ToL1Log[]; } & {}; type: "transactionReceipt"; }; readonly transactionRequest: { exclude: ("paymaster" | "gasPerPubdata" | "factoryDeps" | "paymasterInput" | "customSignature")[] | undefined; format: (args: viem_zksync.ZkSyncTransactionRequest, action?: string | undefined) => ({ data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x0" | undefined; value?: `0x${string}` | undefined; gasPrice?: `0x${string}` | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; accessList?: undefined; sidecars?: undefined; authorizationList?: undefined; eip712Meta?: undefined | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x1" | undefined; value?: `0x${string}` | undefined; gasPrice?: `0x${string}` | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; accessList?: viem.AccessList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; authorizationList?: undefined; eip712Meta?: undefined | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type?: "0x2" | undefined; value?: `0x${string}` | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; authorizationList?: undefined; eip712Meta?: undefined | undefined; } | { type?: "0x3" | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; to: `0x${string}` | null; gasPrice?: undefined | undefined; maxFeePerBlobGas?: `0x${string}` | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; sidecars?: readonly viem.BlobSidecar<`0x${string}`>[] | undefined; blobs?: readonly `0x${string}`[] | readonly viem.ByteArray[] | undefined; blobVersionedHashes: readonly viem.Hex[]; kzg?: undefined; authorizationList?: undefined; eip712Meta?: undefined | undefined; } | { type?: "0x3" | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; to: `0x${string}` | null; gasPrice?: undefined | undefined; maxFeePerBlobGas?: `0x${string}` | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; sidecars?: readonly viem.BlobSidecar<`0x${string}`>[] | undefined; blobs: readonly viem.Hex[] | readonly viem.ByteArray[]; blobVersionedHashes?: readonly `0x${string}`[] | undefined; kzg?: viem.Kzg | undefined; authorizationList?: undefined; eip712Meta?: undefined | undefined; } | { type?: "0x4" | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; value?: `0x${string}` | undefined; accessList?: viem.AccessList | undefined; authorizationList?: viem.RpcAuthorizationList | undefined; blobs?: undefined; blobVersionedHashes?: undefined; kzg?: undefined; sidecars?: undefined; eip712Meta?: undefined | undefined; } | { data?: `0x${string}` | undefined; from?: `0x${string}` | undefined; gas?: `0x${string}` | undefined; nonce?: `0x${string}` | undefined; to?: `0x${string}` | null | undefined; type: "0xff" | "0x71"; value?: `0x${string}` | undefined; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: `0x${string}` | undefined; maxPriorityFeePerGas?: `0x${string}` | undefined; eip712Meta: viem_zksync.ZkSyncEip712Meta; }) & { paymaster: never; gasPerPubdata: never; factoryDeps: never; paymasterInput: never; customSignature: never; }; type: "transactionRequest"; }; }; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers: { readonly transaction: typeof viem_zksync.serializeTransaction; }; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; readonly network: "zksync-era"; }; tokens: { USDC: { address: "0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4"; decimals: number; symbol: string; }; USDT: { address: "0x493257fD37EDB34451f62EDf8D2a0C418852bA4C"; decimals: number; symbol: string; }; }; }; unichain: { chain: { blockExplorers: { readonly default: { readonly name: "Uniscan"; readonly url: "https://uniscan.xyz"; readonly apiUrl: "https://api.uniscan.xyz/api"; }; }; blockTime: 1000; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 0; }; readonly disputeGameFactory: { readonly 1: { readonly address: "0x2F12d621a16e2d3285929C9996f478508951dFe4"; }; }; readonly portal: { readonly 1: { readonly address: "0x0bd48f6B86a26D3a217d0Fa6FfE2B491B956A7a2"; }; }; readonly l1StandardBridge: { readonly 1: { readonly address: "0x81014F44b0a345033bB2b3B21C7a1A308B35fEeA"; }; }; readonly gasPriceOracle: { readonly address: "0x420000000000000000000000000000000000000F"; }; readonly l1Block: { readonly address: "0x4200000000000000000000000000000000000015"; }; readonly l2CrossDomainMessenger: { readonly address: "0x4200000000000000000000000000000000000007"; }; readonly l2Erc721Bridge: { readonly address: "0x4200000000000000000000000000000000000014"; }; readonly l2StandardBridge: { readonly address: "0x4200000000000000000000000000000000000010"; }; readonly l2ToL1MessagePasser: { readonly address: "0x4200000000000000000000000000000000000016"; }; }; ensTlds?: readonly string[] | undefined; id: 130; name: "Unichain"; nativeCurrency: { readonly name: "Ether"; readonly symbol: "ETH"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://mainnet.unichain.org/"]; }; }; sourceId: 1; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters: { readonly block: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcBlock, action?: string | undefined) => { baseFeePerGas: bigint | null; blobGasUsed: bigint; difficulty: bigint; excessBlobGas: bigint; extraData: viem.Hex; gasLimit: bigint; gasUsed: bigint; hash: `0x${string}` | null; logsBloom: `0x${string}` | null; miner: abitype.Address; mixHash: viem.Hash; nonce: `0x${string}` | null; number: bigint | null; parentBeaconBlockRoot?: `0x${string}` | undefined; parentHash: viem.Hash; receiptsRoot: viem.Hex; sealFields: viem.Hex[]; sha3Uncles: viem.Hash; size: bigint; stateRoot: viem.Hash; timestamp: bigint; totalDifficulty: bigint | null; transactions: `0x${string}`[] | viem_chains.OpStackTransaction[]; transactionsRoot: viem.Hash; uncles: viem.Hash[]; withdrawals?: viem.Withdrawal[] | undefined | undefined; withdrawalsRoot?: `0x${string}` | undefined; } & {}; type: "block"; }; readonly transaction: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcTransaction, action?: string | undefined) => ({ blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: boolean; mint?: bigint | undefined | undefined; sourceHash: viem.Hex; type: "deposit"; } | { r: viem.Hex; s: viem.Hex; v: bigint; to: abitype.Address | null; from: abitype.Address; gas: bigint; nonce: number; value: bigint; blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; hash: viem.Hash; input: viem.Hex; transactionIndex: number | null; typeHex: viem.Hex | null; accessList?: undefined | undefined; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId?: number | undefined; yParity?: undefined | undefined; type: "legacy"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip2930"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip1559"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes: readonly viem.Hex[]; chainId: number; type: "eip4844"; gasPrice?: undefined | undefined; maxFeePerBlobGas: bigint; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList: viem.SignedAuthorizationList; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip7702"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; }) & {}; type: "transaction"; }; readonly transactionReceipt: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcTransactionReceipt, action?: string | undefined) => { blobGasPrice?: bigint | undefined; blobGasUsed?: bigint | undefined; blockHash: viem.Hash; blockNumber: bigint; blockTimestamp?: bigint | undefined; contractAddress: abitype.Address | null | undefined; cumulativeGasUsed: bigint; effectiveGasPrice: bigint; from: abitype.Address; gasUsed: bigint; logs: viem.Log[]; logsBloom: viem.Hex; root?: `0x${string}` | undefined; status: "success" | "reverted"; to: abitype.Address | null; transactionHash: viem.Hash; transactionIndex: number; type: viem.TransactionType; l1GasPrice: bigint | null; l1GasUsed: bigint | null; l1Fee: bigint | null; l1FeeScalar: number | null; } & {}; type: "transactionReceipt"; }; }; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers: { readonly transaction: typeof viem_chains.serializeTransactionOpStack; }; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x078D782b760474a361dDA0AF3839290b0EF57AD6"; decimals: number; symbol: string; }; USDT: { address: "0x9151434b16b9763660705744891fA906F660EcC5"; decimals: number; symbol: string; }; }; }; worldchain: { chain: { blockExplorers: { readonly default: { readonly name: "Worldscan"; readonly url: "https://worldscan.org"; readonly apiUrl: "https://api.worldscan.org/api"; }; readonly blockscout: { readonly name: "Blockscout"; readonly url: "https://worldchain-mainnet.explorer.alchemy.com"; readonly apiUrl: "https://worldchain-mainnet.explorer.alchemy.com/api"; }; }; blockTime: 2000; contracts: { readonly multicall3: { readonly address: "0xca11bde05977b3631167028862be2a173976ca11"; readonly blockCreated: 0; }; readonly disputeGameFactory: { readonly 1: { readonly address: "0x069c4c579671f8c120b1327a73217D01Ea2EC5ea"; }; }; readonly l2OutputOracle: { readonly 1: { readonly address: "0x19A6d1E9034596196295CF148509796978343c5D"; }; }; readonly portal: { readonly 1: { readonly address: "0xd5ec14a83B7d95BE1E2Ac12523e2dEE12Cbeea6C"; }; }; readonly l1StandardBridge: { readonly 1: { readonly address: "0x470458C91978D2d929704489Ad730DC3E3001113"; }; }; readonly gasPriceOracle: { readonly address: "0x420000000000000000000000000000000000000F"; }; readonly l1Block: { readonly address: "0x4200000000000000000000000000000000000015"; }; readonly l2CrossDomainMessenger: { readonly address: "0x4200000000000000000000000000000000000007"; }; readonly l2Erc721Bridge: { readonly address: "0x4200000000000000000000000000000000000014"; }; readonly l2StandardBridge: { readonly address: "0x4200000000000000000000000000000000000010"; }; readonly l2ToL1MessagePasser: { readonly address: "0x4200000000000000000000000000000000000016"; }; }; ensTlds?: readonly string[] | undefined; id: 480; name: "World Chain"; nativeCurrency: { readonly name: "Ether"; readonly symbol: "ETH"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://worldchain-mainnet.g.alchemy.com/public"]; }; }; sourceId: 1; testnet: false; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters: { readonly block: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcBlock, action?: string | undefined) => { baseFeePerGas: bigint | null; blobGasUsed: bigint; difficulty: bigint; excessBlobGas: bigint; extraData: viem.Hex; gasLimit: bigint; gasUsed: bigint; hash: `0x${string}` | null; logsBloom: `0x${string}` | null; miner: abitype.Address; mixHash: viem.Hash; nonce: `0x${string}` | null; number: bigint | null; parentBeaconBlockRoot?: `0x${string}` | undefined; parentHash: viem.Hash; receiptsRoot: viem.Hex; sealFields: viem.Hex[]; sha3Uncles: viem.Hash; size: bigint; stateRoot: viem.Hash; timestamp: bigint; totalDifficulty: bigint | null; transactions: `0x${string}`[] | viem_chains.OpStackTransaction[]; transactionsRoot: viem.Hash; uncles: viem.Hash[]; withdrawals?: viem.Withdrawal[] | undefined | undefined; withdrawalsRoot?: `0x${string}` | undefined; } & {}; type: "block"; }; readonly transaction: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcTransaction, action?: string | undefined) => ({ blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: boolean; mint?: bigint | undefined | undefined; sourceHash: viem.Hex; type: "deposit"; } | { r: viem.Hex; s: viem.Hex; v: bigint; to: abitype.Address | null; from: abitype.Address; gas: bigint; nonce: number; value: bigint; blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; hash: viem.Hash; input: viem.Hex; transactionIndex: number | null; typeHex: viem.Hex | null; accessList?: undefined | undefined; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId?: number | undefined; yParity?: undefined | undefined; type: "legacy"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip2930"; gasPrice: bigint; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas?: undefined | undefined; maxPriorityFeePerGas?: undefined | undefined; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip1559"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList?: undefined | undefined; blobVersionedHashes: readonly viem.Hex[]; chainId: number; type: "eip4844"; gasPrice?: undefined | undefined; maxFeePerBlobGas: bigint; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash /** * Normalise a `ChainInput` (+ optional rpc override) into the * `{ chain, chainId, rpcUrl, tokens }` the wallet and verifier need. */ ? /** * Normalise a `ChainInput` (+ optional rpc override) into the * `{ chain, chainId, rpcUrl, tokens }` the wallet and verifier need. */: undefined | undefined; } | { blockHash: `0x${string}` | null; blockNumber: bigint | null; blockTimestamp?: bigint | undefined; from: abitype.Address; gas: bigint; hash: viem.Hash; input: viem.Hex; nonce: number; r: viem.Hex; s: viem.Hex; to: abitype.Address | null; transactionIndex: number | null; typeHex: viem.Hex | null; v: bigint; value: bigint; yParity: number; accessList: viem.AccessList; authorizationList: viem.SignedAuthorizationList; blobVersionedHashes?: undefined | undefined; chainId: number; type: "eip7702"; gasPrice?: undefined | undefined; maxFeePerBlobGas?: undefined | undefined; maxFeePerGas: bigint; maxPriorityFeePerGas: bigint; isSystemTx?: undefined | undefined; mint?: undefined | undefined; sourceHash?: undefined | undefined; }) & {}; type: "transaction"; }; readonly transactionReceipt: { exclude: [] | undefined; format: (args: viem_chains.OpStackRpcTransactionReceipt, action?: string | undefined) => { blobGasPrice?: bigint | undefined; blobGasUsed?: bigint | undefined; blockHash: viem.Hash; blockNumber: bigint; blockTimestamp?: bigint | undefined; contractAddress: abitype.Address | null | undefined; cumulativeGasUsed: bigint; effectiveGasPrice: bigint; from: abitype.Address; gasUsed: bigint; logs: viem.Log[]; logsBloom: viem.Hex; root?: `0x${string}` | undefined; status: "success" | "reverted"; to: abitype.Address | null; transactionHash: viem.Hash; transactionIndex: number; type: viem.TransactionType; l1GasPrice: bigint | null; l1GasUsed: bigint | null; l1Fee: bigint | null; l1FeeScalar: number | null; } & {}; type: "transactionReceipt"; }; }; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers: { readonly transaction: typeof viem_chains.serializeTransactionOpStack; }; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; readonly network: "worldchain"; }; tokens: { USDC: { address: "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1"; decimals: number; symbol: string; }; }; }; sei: { chain: { blockExplorers: { readonly default: { readonly name: "Seiscan"; readonly url: "https://seiscan.io"; readonly apiUrl: "https://api.etherscan.io/v2/api"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11"; }; }; ensTlds?: readonly string[] | undefined; id: 1329; name: "Sei Network"; nativeCurrency: { readonly name: "Sei"; readonly symbol: "SEI"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://evm-rpc.sei-apis.com/"]; readonly webSocket: readonly ["wss://evm-ws.sei-apis.com/"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392"; decimals: number; symbol: string; }; }; }; injective: { chain: { blockExplorers: { readonly default: { readonly name: "Injective Explorer"; readonly url: "https://blockscout.injective.network"; readonly apiUrl: "https://blockscout.injective.network/api"; }; }; blockTime?: number | undefined | undefined; contracts?: { [x: string]: viem.ChainContract | { [sourceId: number]: viem.ChainContract | undefined; } | undefined; ensRegistry?: viem.ChainContract | undefined; ensUniversalResolver?: viem.ChainContract | undefined; multicall3?: viem.ChainContract | undefined; erc6492Verifier?: viem.ChainContract | undefined; } | undefined; ensTlds?: readonly string[] | undefined; id: 1776; name: "Injective"; nativeCurrency: { readonly decimals: 18; readonly name: "Injective"; readonly symbol: "INJ"; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://sentry.evm-rpc.injective.network"]; readonly webSocket: readonly ["wss://sentry.evm-ws.injective.network"]; }; }; sourceId?: number | undefined | undefined; testnet: false; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0xa00C59fF5a080D2b954d0c75e46E22a0c371235a"; decimals: number; symbol: string; }; USDT: { address: "0x88f7F2b685F9692caf8c478f5BADF09eE9B1Cc13"; decimals: number; symbol: string; }; }; }; hyperevm: { chain: { blockExplorers: { readonly default: { readonly name: "HyperEVMScan"; readonly url: "https://hyperevmscan.io"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11"; readonly blockCreated: 13051; }; }; ensTlds?: readonly string[] | undefined; id: 999; name: "HyperEVM"; nativeCurrency: { readonly name: "HYPE"; readonly symbol: "HYPE"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://rpc.hyperliquid.xyz/evm"]; }; }; sourceId?: number | undefined | undefined; testnet: false; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0xb88339CB7199b77E23DB6E890353E22632Ba630f"; decimals: number; symbol: string; }; }; }; monad: { chain: { blockExplorers: { readonly default: { readonly name: "MonadVision"; readonly url: "https://monadvision.com"; }; readonly monadscan: { readonly name: "Monadscan"; readonly url: "https://monadscan.com"; readonly apiUrl: "https://api.monadscan.com/api"; }; }; blockTime: 400; contracts: { readonly multicall3: { readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11"; readonly blockCreated: 9248132; }; }; ensTlds?: readonly string[] | undefined; id: 143; name: "Monad"; nativeCurrency: { readonly name: "Monad"; readonly symbol: "MON"; readonly decimals: 18; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://rpc.monad.xyz", "https://rpc1.monad.xyz"]; readonly webSocket: readonly ["wss://rpc.monad.xyz", "wss://rpc1.monad.xyz"]; }; }; sourceId?: number | undefined | undefined; testnet: false; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDC: { address: "0x754704Bc059F8C67012fEd69BC8A327a5aafb603"; decimals: number; symbol: string; }; }; }; kaia: { chain: { blockExplorers: { readonly default: { readonly name: "KaiaScan"; readonly url: "https://kaiascan.io"; readonly apiUrl: "https://api-cypress.klaytnscope.com/api"; }; }; blockTime?: number | undefined | undefined; contracts: { readonly multicall3: { readonly address: "0xcA11bde05977b3631167028862bE2a173976CA11"; readonly blockCreated: 96002415; }; }; ensTlds?: readonly string[] | undefined; id: 8217; name: "Kaia"; nativeCurrency: { readonly decimals: 18; readonly name: "Kaia"; readonly symbol: "KAIA"; }; experimental_preconfirmationTime?: number | undefined | undefined; rpcUrls: { readonly default: { readonly http: readonly ["https://public-en.node.kaia.io"]; }; }; sourceId?: number | undefined | undefined; testnet?: boolean | undefined | undefined; custom?: Record | undefined; extendSchema?: Record | undefined; fees?: viem.ChainFees | undefined; formatters?: undefined; prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: { client: viem.Client; phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters"; }) => Promise) | undefined, options: { runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[]; }] | undefined; serializers?: viem.ChainSerializers | undefined; verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise) | undefined; }; tokens: { USDT: { address: "0xd077A400968890Eacc75cdc901F0356c943e4fDb"; decimals: number; symbol: string; }; }; }; }; /** A built-in EVM chain name. */ type ChainName = keyof typeof CHAINS; type ChainInput = ChainName | Chain | { /** EVM chain id, e.g. 5000 for Mantle. */ id: number; /** JSON-RPC endpoint. */ rpcUrl: string; /** Display name. Defaults to `EVM `. */ name?: string; /** Native coin metadata. Defaults to 18-decimal ETH. */ nativeCurrency?: { name: string; symbol: string; decimals: number; }; }; interface ResolvedChain { chain: Chain; chainId: number; rpcUrl: string; /** Known tokens on this chain (empty for unknown custom chains). */ tokens: Record; } /** * Normalise a `ChainInput` (+ optional rpc override) into the * `{ chain, chainId, rpcUrl, tokens }` the wallet and verifier need. */ declare function resolveChain(input: ChainInput, rpcUrlOverride?: string): ResolvedChain; /** * The PaymentDriver contract. Every chain-family section (EVM, Solana, …) * implements this and nothing else; the protocol layer (server/client/x402) * depends on THIS file only — zero `viem`, zero `@solana/web3.js`. * * Convention used throughout the driver layer: * `chain` = the developer-supplied selector ('base', { id, rpcUrl }, …) * `network` = the resolved CAIP-2 id it maps to ('eip155:8453', 'solana:…') * * Identifiers cross this boundary as plain strings (CAIP-2 networks, base-unit * amounts, `0x…`/base58 addresses). `_native` on a WalletHandle is the single * intentional `unknown`: each driver stashes its own wallet object there. */ /** The chain families the SDK knows about. */ type ChainFamily = 'evm' | 'solana' | 'ton' | 'stellar' | 'xrpl' | 'tron' | 'sui' | 'near' | 'aptos' | 'algorand'; /** What chain to use: an EVM name/Chain/{id,rpcUrl}, or a non-EVM family name. */ type ChainSelector = ChainInput | 'solana' | 'ton' | 'stellar' | 'xrpl' | 'tron' | 'sui' | 'near' | 'aptos' | 'algorand'; /** An EVM ERC-20 token, by contract address. */ interface EvmToken { address: `0x${string}`; decimals: number; symbol?: string; } /** A Solana SPL token, by mint. */ interface SolanaToken { mint: string; decimals: number; symbol?: string; } /** A TON jetton, by master address. */ interface TonToken { master: string; decimals: number; symbol?: string; } /** A Stellar classic asset, by issuer + code. */ interface StellarToken { issuer: string; code: string; decimals: number; symbol?: string; } /** An XRP Ledger issued currency, by issuer + 160-bit currency code (hex). */ interface XrplToken { issuer: string; /** The currency: a 3-char ASCII code, or the 40-char 160-bit hex for 4+ chars. */ currencyHex: string; decimals: number; symbol?: string; } /** A Tron TRC-20 token, by contract address (Base58 T…). */ interface TronToken { address: string; decimals: number; symbol?: string; } /** A Sui coin, by its fully-qualified coin type (`package::module::TYPE`). */ interface SuiToken { coinType: string; decimals: number; symbol?: string; } /** A NEAR NEP-141 token, by its contract account id. */ interface NearToken { contractId: string; decimals: number; symbol?: string; } /** An Aptos Fungible Asset, by its metadata object address. */ interface AptosToken { metadata: string; decimals: number; symbol?: string; } /** An Algorand Standard Asset (ASA), by its numeric asset id. */ interface AlgorandToken { assetId: number; decimals: number; symbol?: string; } /** * What to be paid in. Each driver validates the forms it accepts: * - 'native' the chain's native coin (ETH, BNB, SOL, TON, XLM, XRP, TRX, NEAR, SUI, APT, ALGO) * - 'USDC' (string) a symbol resolved against the chosen chain * - EvmToken any ERC-20 (EVM chains) * - SolanaToken any SPL token (Solana) * - TonToken any jetton (TON) * - StellarToken any classic asset (Stellar) * - XrplToken any issued currency (XRPL) * - TronToken any TRC-20 (Tron) * - SuiToken any coin (Sui) * - NearToken any NEP-141 (NEAR) * - AptosToken any Fungible Asset (Aptos) * - AlgorandToken any ASA (Algorand) */ type TokenInput = 'native' | (string & {}) | EvmToken | SolanaToken | TonToken | StellarToken | XrplToken | TronToken | SuiToken | NearToken | AptosToken | AlgorandToken; /** What a driver resolves a TokenInput into. `asset`: 0x | base58 mint | 'native'. */ interface ResolvedToken { asset: string; decimals: number; symbol?: string; } /** * A best-effort estimate of the on-chain NETWORK FEE (gas) a payment will cost * the payer — denominated in the chain's NATIVE coin, which is distinct from the * payment token (you pay in USDC but burn ETH / SOL / TRX / … for gas). Lets an * agent learn the gas cost BEFORE it pays, so it can keep enough native coin on * hand. Built uniformly by the shared `nativeCost()` helper, so every family's * shape is identical. Converting to fiat needs the caller's own price source — * PipRail stays backendless and never calls an oracle. */ interface CostEstimate { /** The native fee coin's ticker — ETH, BNB, SOL, TON, XLM, XRP, TRX, … */ feeSymbol: string; /** The native coin's decimals (18 EVM, 9 Solana/TON, 7 Stellar, 6 XRPL/Tron). */ feeDecimals: number; /** Estimated fee in the native coin's base units (a non-negative integer string). */ fee: string; /** Human-readable fee, e.g. '0.000021'. */ feeFormatted: string; /** 'estimated' = derived from a live RPC read; 'heuristic' = a typical-cost constant. */ basis: 'estimated' | 'heuristic'; /** Short human note on what's included (e.g. 'gas ~21000 @ 12 gwei'). */ detail?: string; } /** Opaque per-driver wallet handle — `_native` holds the driver's own object. */ interface WalletHandle { readonly _native: unknown; } /** * Why a recipient can't receive an asset yet — the chain's one-time receive * prerequisite, when it has one. Surfaced by {@link ResolvedNetwork.recipientReady} * and relayed by the client's planner so an agent fixes the RECIPIENT (not its own * balance). Families with no prerequisite report `ready: 'n/a'` and no reason. */ type RecipientReason = 'NO_TRUSTLINE' | 'NOT_REGISTERED' | 'NOT_OPTED_IN' | 'INACTIVE'; /** What {@link ResolvedNetwork.balanceOf} returns — base-unit balances, or null per * field when that read was unavailable (transient/RPC), never a false 0. */ interface WalletBalance { /** The payment token's balance in base units, or null if the read was unavailable. */ token: bigint | null; /** The native gas coin's balance in base units, or null if unavailable. For * `asset === 'native'`, this equals `token`. */ native: bigint | null; } interface ConfirmInfo { /** Block number (EVM) or slot (Solana) as a string — numeric-agnostic. */ height: string; } /** * A discovery signer — the bound wallet's address + a message signer. Returned * by {@link ResolvedNetwork.discoverySigner} and used ONLY for discovery * (ownership proofs / SIWX registration), never to move funds. Structurally what * the open-index register helpers consume. */ interface DiscoverySigner { /** The wallet's own public address (EVM 0x…), declared in a SIWX challenge. */ address: string; /** Sign an arbitrary UTF-8 message (EVM: eip191) — for proofs/SIWX only. */ signMessage(message: string): Promise; } /** * The official x402 `offer-receipt` §5.2 signed-field set a merchant attests over in * Tier-2 — the SMALLER official payload (NOT PipRail's full {@link X402Receipt}), * consumed by the optional EVM-only {@link ResolvedNetwork.signReceipt}. The gate * maps PipRail's settlement data into it. `payTo` rides ALONGSIDE the signed fields * purely so a verifier knows the expected signer (`recover === payTo`); it is NOT * part of the EIP-712 message. */ interface ReceiptInput { /** The merchant `payTo` wallet — the signer a verifier checks `recover ===` against. NOT signed. */ payTo: string; /** CAIP-2 network the settlement happened on (signed `network`). */ network: string; /** The paid resource URL (signed `resourceUrl`). */ resourceUrl: string; /** The payer identifier — the on-chain sender (signed `payer`). */ payer: string; /** Unix SECONDS the receipt was issued (signed `issuedAt`). */ issuedAt: number; /** * The settlement tx hash, or the empty string `''` when suppressed * (`includeTxHash:false`). §5.3: the signed EIP-712 message MUST carry `''`, * NEVER an omitted key — a verifier treats `''` as absence. Default `''`. */ transaction?: string; } /** * How a family advertises a standard `exact` rail for one asset — returned by * {@link ResolvedNetwork.resolveExactRail} and consumed by the gate to build the * `X402ExactAcceptEntry`. The `method` is the family's transfer method (EVM: * `'eip3009'`/`'permit2'`, Solana: `'svm'`); `extra` is merged VERBATIM into the * accept's `extra`, carrying the family-specific bits a payer needs (EVM: the EIP-712 * `name`/`version`; Solana: the merchant `feePayer` + `tokenProgram`). Keeping the * chain-specific shape behind this descriptor is what lets `server.ts` stay * chain-agnostic — it never names a family, it just merges `extra`. */ interface ExactRailInfo { method: 'eip3009' | 'permit2' | 'svm' | 'algorand' | 'aptos' | 'near'; /** Family-specific `extra` keys merged into the exact accept (e.g. `{ name, version }` * for EVM EIP-3009, `{ feePayer, tokenProgram }` for Solana, `{ feePayer }` for * Algorand/Aptos/NEAR). */ extra?: Record; } /** * How a family advertises a standard `upto` (metered) rail for one asset — the metered * sibling of {@link ExactRailInfo}, returned by {@link ResolvedNetwork.resolveUptoRail} * and consumed by the gate to build the `X402UptoAcceptEntry`. The `method` is always the * PipRail-internal `'permit2-upto'` (upto is EVM-Permit2 only); `extra` is merged VERBATIM * into the accept's `extra`, carrying `{ facilitatorAddress, name?, version? }` — the * relayer address the buyer signs into `witness.facilitator` plus the token's EIP-712 * domain bits. Keeping the chain-specific shape behind this descriptor is what lets * `server.ts` stay chain-agnostic — it never names a family, it just merges `extra`. */ interface UptoRailInfo { method: 'permit2-upto'; /** Family-specific `extra` keys merged into the upto accept: `{ facilitatorAddress, name?, version? }`. */ extra?: Record; } /** * A driver bound to one concrete network — what the gate and client hold. Each * method's error behaviour is fixed by the SDK error standard (see ERRORS.md §5): * `resolveToken`/`assertValidPayTo`/`bindWallet` throw `WrongFamilyError` / * `UnknownTokenError`; `send` maps affordability to `InsufficientFundsError`; * `verify` RETURNS a `VerifyResult` (and never throws for an RPC hiccup — * transient reads become `tx_not_found`); `confirm` throws `ConfirmationTimeoutError`. */ interface ResolvedNetwork { readonly family: ChainFamily; /** The resolved CAIP-2 id: 'eip155:8453' | 'solana:' | … */ readonly network: Caip2; /** Does this bound network handle the given CAIP-2 network string? */ supports(network: string): boolean; /** Turn a TokenInput into { asset, decimals, symbol } for this network. */ resolveToken(token: TokenInput): ResolvedToken; /** * Trusted metadata for a resolved on-chain `asset` id on THIS network — the * SDK's OWN decimals/symbol for a built-in token or the native coin, or `null` * if the SDK doesn't recognise the asset (a custom token it can't safely * price). Pure/synchronous — a built-in-map + native lookup, never an RPC call. * * The client uses it to enforce a spend budget against the token's TRUE * decimals (so a server can't understate a price by lying about * `extra.decimals`) and to flag when a challenge's stated symbol disagrees * with the real one. The inverse of `resolveToken` for known assets. */ describeAsset(asset: string): { symbol?: string; decimals: number; } | null; /** Throw if `payTo` isn't a valid address for this family. */ assertValidPayTo(payTo: string): void; /** Validate + wrap the user's wallet config for this family. */ bindWallet(wallet: unknown): WalletHandle; /** Broadcast payment for `accept`; return the proof ref (tx hash / signature). */ send(wallet: WalletHandle, accept: X402AcceptEntry): Promise; /** Wait until `ref` reaches minConfirmations (or finality). */ confirm(ref: string, minConfirmations: number): Promise; /** * Best-effort estimate of the network fee (gas) to settle `accept`, in the * chain's NATIVE coin (see {@link CostEstimate}). Async (may read RPC) but * never throws for a transient RPC issue — it falls back to a 'heuristic' * constant. `opts.from` (the payer's address) sharpens chains whose fee * depends on the sender (notably Tron energy); omit it for a typical estimate. * Payer-side + informational — the gate never calls this. Accepts either rail * shape ({@link X402AnyAccept}): a standard `exact` rail estimates the BUYER's gas * as ~0 (the server/facilitator broadcasts the signed authorization). */ estimateCost(accept: X402AnyAccept, opts?: { from?: string; }): Promise; /** * Read the BOUND WALLET's own balance of the payment `asset` AND of the native * gas coin, in base units — what the client's `planPayment` checks affordability * against. RPC-read-only and NEVER throws: a field whose read was unavailable * (rate-limited / transient) comes back `null` (unknown), never `0` (which would * falsely read "broke"). For `asset === 'native'`, `token === native`. * Payer-side + informational — the gate never calls this. See {@link WalletBalance}. */ balanceOf(wallet: WalletHandle, asset: string): Promise; /** * Can `payTo` RECEIVE `asset` on this network right now? Reports the chain's * one-time receive prerequisite (trustline / storage_deposit / ASA opt-in / * activation): * - `{ ready: 'n/a' }` the family has NO prerequisite (EVM, Solana, TON, * Tron, Sui, Aptos — and every native coin) * - `{ ready: true }` the prerequisite is satisfied * - `{ ready: false, reason }` it's missing (see {@link RecipientReason}) — fix the recipient * - `{ ready: 'unknown' }` the probe read failed (transient) — NEVER throws * Re-derives nothing from a client ref; reads only the given `payTo`. Payer-side * pre-flight — the gate never calls this. */ recipientReady(payTo: string, asset: string): Promise<{ ready: boolean | 'n/a' | 'unknown'; reason?: RecipientReason; }>; /** * OPTIONAL (EVM EIP-3009/Permit2 + Solana SVM + Algorand + Aptos) — the BUYER counterpart to {@link settleExactSelf}. * Build + EIP-712-sign an EIP-3009 `transferWithAuthorization` for a standard x402 * `exact` rail, so a PipRail agent can PAY any standard x402 server (not just PipRail's * own `onchain-proof` gates). The client frames the returned `payload` + `accepted` echo * into the `PAYMENT-SIGNATURE` header and re-requests; the server / merchant-chosen * facilitator BROADCASTS the authorization (the buyer never broadcasts and spends ~0 gas). * * Re-derives the token's EIP-712 domain ON-CHAIN (never trusts the server-supplied * `extra.{name,version}`), generates a CSPRNG 32-byte nonce + current unix time * internally, and signs through the wallet client (bring-your-own JsonRpcAccount safe). * THROWS a typed `PipRailError` (`UnsupportedSchemeError`) when the asset isn't EIP-3009 * (USDT/native/plain ERC-20) or the signer is a contract / EIP-1271 / EIP-7702 account. * * The third optional `exact` method (after {@link exactDomain}/{@link settleExactSelf}); * optional `?` is the gather gate — non-EVM families omit it, so an `exact` rail is never * gathered/paid on those chains. Returns the signed payload, the chosen-rail echo (the * server's RAW rail, verbatim, so a facilitator's extra keys survive), the payer address, * and the nonce (for the client's spend record + a re-present-the-same-auth retry). */ payExact?(wallet: WalletHandle, accept: X402ExactAcceptEntry): Promise<{ payload: ExactPaymentPayloadAny; accepted: X402ExactAcceptEntry; payerFrom: string; nonce: string; }>; /** * OPTIONAL — a DISCOVERY signer for the bound wallet: its public address plus a * message signer, used only for ownership proofs + SIWX index registration, * NEVER the payment path. `signMessage` returns a chain-native signature string * (EVM: 0x eip191 hex, recoverable with `recoverMessageAddress` — exactly how * x402scan verifies origin ownership). Deliberately optional: a family ships it * only once an open index verifies its signatures — EVM today (the 402 Index * register path needs no signature at all). The client's `register()` skips * signature-gated steps for a family that omits it. The first optional contract * method, so it does NOT trigger the "implement in all families" rule for * REQUIRED methods. Returns `null` if the bound wallet can't sign. */ discoverySigner?(wallet: WalletHandle): DiscoverySigner | null; /** * OPTIONAL (EVM-only today) — Tier-2 service-delivery attestation. Sign the * official x402 `offer-receipt` EIP-712 `RECEIPT_TYPES` (the SMALLER official * §5.2 payload) with the bound wallet — typically the merchant's existing `payTo` * key — so a buyer can prove the resource was SERVED, the one thing the chain * can't attest. A verifier ({@link PipRailClient.verifyAttestation}) re-recovers * and checks `recover === payTo`. The signed domain is hardcoded `chainId:1` for * EVERY network (uniform signing), so this is chain-independent and EVM-only by * the optional `?` gate — non-EVM families omit it (no all-families mirror), * exactly like {@link discoverySigner}/{@link payExact}. The gate calls it after * settle when `receipts.attest` is set + the rail is EVM; the result rides in * `extensions['offer-receipt'].info.attestation`. NEVER part of the payment path. */ signReceipt?(wallet: WalletHandle, input: ReceiptInput): Promise; /** Verify `ref` satisfies `accept`, RPC-only, in-process. */ verify(ref: string, accept: X402AcceptEntry): Promise; /** * OPTIONAL — resolve a standard `exact` rail for `asset` on this network, or `null` * when this asset/chain can't carry one (a native coin, an unsupported token, or a * family with no `exact` settlement). This is the gate's rail-ADVERTISEMENT SPI: the * chain-agnostic `server.ts` calls it to decide whether to dual-advertise an `exact` * rail beside `onchain-proof`, and uses the returned {@link ExactRailInfo} to build the * `X402ExactAcceptEntry` — so the gate never special-cases a family. * * `method` is the merchant's preference (`'auto'` lets the family pick — EVM auto-selects * EIP-3009 over Permit2; Solana ignores it and always uses `'svm'`). The fee payer for a * family that needs one (Solana) comes from EITHER `feePayer` (a facilitator-provided * sponsor pubkey, in facilitator mode — so neither buyer nor merchant pays gas) OR `relayer` * (the merchant's own bound self-settle wallet, in self mode); `feePayer` takes precedence. * RPC-read (EVM reads the token's EIP-712 domain; Solana reads the mint's token program); MAY * throw a typed config error for an explicitly-requested-but-unsupported method (EVM does). A * family that omits this method offers no `exact` rail (today: every family except EVM, Solana, Algorand, and Aptos). */ resolveExactRail?(input: { asset: string; method: 'eip3009' | 'permit2' | 'svm' | 'auto'; relayer?: WalletHandle; /** A facilitator-provided fee-payer pubkey (facilitator mode); overrides the relayer's. */ feePayer?: string; }): Promise; /** * OPTIONAL (EVM-only today) — the on-chain EIP-712 domain `{ name, version }` of an * EIP-3009 token `asset`, read from the contract (`name()`/`version()`). Returns * `null` when the asset is NOT an EIP-3009 token (no `transferWithAuthorization` — * e.g. USDT, native coin, or a plain ERC-20), so the gate can refuse to advertise a * standard `exact` rail for it. Never derived from the symbol (USDC's domain name is * "USD Coin", not "USDC"; EURC's is "Euro Coin" on Ethereum/Avalanche but "EURC" on Base — * which is exactly why it must be READ, never assumed). Called once at exact-rail resolution * (cached by the gate). RPC-read; may throw on a transient read (the gate surfaces a * clear config error). The first of two optional server methods for the `exact` rail. */ exactDomain?(asset: string): Promise<{ name: string; version: string; } | null>; /** * OPTIONAL (EVM-only) — whether this chain can carry the **Permit2** transfer method * of the `exact` scheme: i.e. the canonical Permit2 **and** the `x402ExactPermit2Proxy` * are deployed here. The gate calls it to AVOID advertising a Permit2 `exact` rail it * could never settle (a non-EIP-3009 token on a proxy-less chain — e.g. Binance-Peg * USDC on a chain without the proxy). EIP-3009 needs no proxy, so this gates ONLY the * Permit2 fallback. Omitted (or `false`) ⇒ treat Permit2 as unavailable on this chain. */ exactPermit2Supported?(): boolean; /** * OPTIONAL (EVM EIP-3009/Permit2 + Solana SVM + Algorand + Aptos) — verify a standard x402 `exact` * payment locally, then SELF-SETTLE it by broadcasting from the merchant's own `relayer` * wallet (the merchant pays the network fee to receive; EVM broadcasts * `transferWithAuthorization`, Solana co-signs the fee payer, Algorand signs the pooled fee * txn + submits the group, Aptos adds the fee-payer signature + submits — the transfer * binds `payTo`, so no redirect risk). RETURNS a `VerifyResult`: * - `{ ok:false, error }` for a CLIENT-fixable fault (bad signature, expired, * wrong recipient/amount, used nonce, simulation revert) → gate replies 402; * - `{ ok:true, receipt }` once the settle tx is mined. * THROWS {@link SettlementError} when a VALID + simulated payment fails to BROADCAST * (relayer out of gas / RPC down) → gate replies 5xx (the payer's authorization is * still good and its nonce unused). Re-derives every checked field from the trusted * `accept`, never the client echo. */ settleExactSelf?(input: { relayer: WalletHandle; payload: ExactPaymentPayloadAny; accept: X402ExactAcceptEntry; }): Promise; /** * OPTIONAL (EVM-Permit2 ONLY) — advertise a standard `upto` (metered) rail for `asset`, * or `null` when this asset/chain can't carry one (a native coin, a non-Permit2-proxy * chain, a non-Permit2 token). The gate's rail-advertisement SPI for the metered rail — * the parallel of {@link resolveExactRail}. The `relayer` becomes the bound * `witness.facilitator` (upto is self-settle only in v1, no facilitator mode), so the * returned `extra.facilitatorAddress` is the relayer's own address. The optional `?` IS * the EVM-only gate — every non-EVM family omits it, so the gate never offers upto there. * RPC-read (EVM reads the token's EIP-712 domain); never throws for a transient read. */ resolveUptoRail?(input: { asset: string; relayer: WalletHandle; }): Promise; /** * OPTIONAL (EVM-Permit2 ONLY) — BUYER side: sign a Permit2 `PermitWitnessTransferFrom` * authorization for the MAX (`accept.amount`), binding `witness.facilitator = * accept.extra.facilitatorAddress` as the MIDDLE witness field. Re-derives the proxy + * witness types internally; never trusts a server-supplied domain. The metered counterpart * to {@link payExact}. The client frames the returned `payload` + `accepted` echo into the * `PAYMENT-SIGNATURE` header; the merchant self-settles the actual. Returns the signed * payload, the chosen-rail echo, the payer address, and the Permit2 nonce. THROWS a typed * `PipRailError` when the signer is a contract / EIP-1271 / EIP-7702 account. */ payUpto?(wallet: WalletHandle, accept: X402UptoAcceptEntry): Promise<{ payload: Permit2UptoPaymentPayload; accepted: X402UptoAcceptEntry; payerFrom: string; nonce: string; }>; /** * OPTIONAL (EVM-Permit2 ONLY) — SELLER side: verify a standard x402 `upto` payment * locally, then SELF-SETTLE the ACTUAL (`settleAmount`, ≤ the signed max) through the upto * proxy from the merchant's `relayer`. The metered counterpart to {@link settleExactSelf}. * Re-verifies the signature against `permitted.amount` (the signed MAX, NEVER the metered * actual — verifying against the actual would reject every partial settle); guards * `witness.facilitator === relayer.address`; clamps/rejects `settleAmount > max` with * `upto_settle_exceeds_max`; SKIPS the on-chain tx when `settleAmount === 0n` (a synthetic * zero-charge receipt with `transaction: ""`). RETURNS a `VerifyResult` for a client-fixable * fault; THROWS {@link SettlementError} on a broadcast failure of a valid auth. Re-derives * every checked field from the trusted `accept`, never the client echo. */ settleUptoSelf?(input: { relayer: WalletHandle; payload: Permit2UptoPaymentPayload; accept: X402UptoAcceptEntry; settleAmount: bigint; }): Promise; } interface ResolveOptions { /** The developer-supplied `chain` selector. */ chain: unknown; rpcUrl?: string; } /** A stateless family driver. The registry calls resolve() to bind a network. */ interface PaymentDriver { readonly family: ChainFamily; /** Recognise + bind the chain input, or return null to let another try. */ resolve(opts: ResolveOptions): ResolvedNetwork | null; } /** The open directories PipRail can read from / write to. */ type DiscoverySource = 'bazaar' | '402index' | 'x402scan'; /** One payment option as an index reports it — looser than a live `accepts[]` * entry (indexes are cross-scheme: `exact` is the norm, `onchain-proof` is ours). */ interface DiscoveredRail { scheme: string; network: string; asset?: string; amount?: string; payTo?: string; symbol?: string; } /** A resource as returned by an open index, normalized to one shape. */ interface DiscoveredResource { /** The gated resource URL (what an agent then quotes/pays). */ resource: string; /** Which open index surfaced it. */ source: DiscoverySource; name?: string; description?: string; category?: string; /** Free-text tags/keywords, when the index reports them. */ tags?: string[]; /** Advertised price in USD, when the index reports one (402 Index). */ priceUsd?: number; /** Health/uptime score 0–100, when the index reports one (402 Index `reliability_score`). * Higher = a more reliable, consistently-probeable endpoint — sort/filter on it to * skip flaky resources. Absent for indexes that don't measure it (Bazaar). */ reliabilityScore?: number; /** Liveness as the index last probed it, e.g. `'healthy'` / `'degraded'` / `'down'` * (402 Index `health_status`). Absent where unmeasured. */ health?: string; /** Whether the listing's domain is verified at the index (402 Index `domain_verified`). * A verified resource is a stronger trust + relevance signal. Absent where the index * has no such concept (Bazaar). */ verified?: boolean; /** Relevance score for the active query (set by {@link rankResources}); higher ranks * first. Absent when no query was given (results keep first-seen / sort order). */ score?: number; /** The payment options the index advertises (best-effort, cross-scheme). */ rails: DiscoveredRail[]; } /** Where a listing stands after a register attempt — a branchable lifecycle * state so an agent doesn't have to re-derive each index's behaviour: * - `'live'` — findable now (search it immediately). * - `'pending-review'` — accepted, but the index reviews/propagates before it's * publicly findable; allow a short delay before `discover()`. * - `'not-listable'` — it didn't list (a failure, or this index structurally * can't list a PipRail resource). See `detail` + `note`. */ type ListingVisibility = 'live' | 'pending-review' | 'not-listable'; /** The result of trying to list a resource on one open index. */ interface RegisterOutcome { source: DiscoverySource; ok: boolean; /** HTTP status, when a request was made. */ status?: number; /** Human note — success summary or the reason it didn't list. */ detail?: string; /** A link to the listing, when the index returns one. */ listingUrl?: string; /** The lifecycle state of this listing — `'live'`, `'pending-review'`, or * `'not-listable'`. Projected from {@link DIRECTORY_INFO}; branch on this * instead of guessing how soon `discover()` will find the resource. */ visibility?: ListingVisibility; /** A one-line, agent-readable caveat for this source (from {@link DIRECTORY_INFO}) * — e.g. "402 Index reviews before publishing" or "discover() doesn't read * x402scan, so a live listing there won't appear in discover() results". */ note?: string; } /** How to order results. `'relevance'` (the default when a {@link SearchOpenIndexesOptions.query} * is given) ranks by query match via {@link rankResources}; the rest sort by a single field * (descending unless `order:'asc'`), using each index's reported value (unknowns sort last). */ type DiscoverySort = 'relevance' | 'reliability' | 'price' | 'uptime' | 'name'; interface SearchOpenIndexesOptions { /** * Free-text query. Tokenized and matched across name / description / category / URL: * 402 Index is searched server-side (one request per token, unioned — so a multi-word * query like `"piprail demo"` still finds a resource that matches each word, which the * index's own AND-tokenized `?q=` would miss), the Bazaar list is filtered client-side, * and the merged set is ranked by relevance ({@link rankResources}). */ query?: string; /** Which indexes to read. Default `['bazaar', '402index']` (both free). */ sources?: DiscoverySource[]; /** Max results to FETCH per index request. Default 20. */ limit?: number; /** Keep ONLY this category (prefix match, case-insensitive) — strict: a resource the * index didn't categorize is dropped (pushed to 402 Index server-side too). */ category?: string; /** Keep only resources paying in this asset symbol, e.g. `'USDC'` (pushed to 402 Index; * applied client-side elsewhere, keeping items whose asset the index didn't report). */ asset?: string; /** Drop results advertised above this USD price (results with no advertised price pass). */ maxPrice?: number; /** Drop results whose reliability score is BELOW this (0–100). Results with no reported * score pass through — use {@link DiscoveredResource.reliabilityScore} to inspect. */ minReliability?: number; /** Prefer verified listings (402 Index `verified=true`, server-side). NOTE: 402 Index's * `verified` flag and its per-record `domain_verified` differ, so this is applied at the * index, not re-filtered client-side; sources without a verified concept pass through. * Inspect {@link DiscoveredResource.verified} for the per-record ownership signal. */ verified?: boolean; /** Restrict to listings the index confirmed are payable x402 (402 Index `payment_valid=true`). */ paymentValid?: boolean; /** Result ordering — see {@link DiscoverySort}. Default `'relevance'` with a query, else * first-seen order (source priority). */ sort?: DiscoverySort; /** Sort direction for a non-relevance {@link sort}. Default `'desc'`. */ order?: 'asc' | 'desc'; signal?: AbortSignal; } /** What a merchant submits to register one resource on the open indexes. */ interface RegisterInput { url: string; name?: string; description?: string; priceUsd?: number; /** Payment asset symbol, e.g. 'USDC' (402 Index metadata). */ asset?: string; /** Payment network slug, e.g. 'base' (402 Index metadata). */ network?: string; /** HTTP method the resource answers on. Default 'GET'. */ method?: string; /** * A category for the listing, e.g. `'ai'`, `'finance'`, `'data'`. The single * highest-leverage field for findability: most of 402 Index's catalog is * `uncategorized`, so a real category makes a resource rank + filter where almost * nothing else does. Free-text; pick the obvious bucket for what the endpoint does. */ category?: string; /** * Keywords for the listing. 402 Index search is literal (a term must appear in the * name or description to match), so these are folded into the description as a compact * keyword tail — making the resource findable by each term — and also sent as a `tags` * field for any index that indexes them natively. Skipped if already present / over the * length cap (same tasteful rules as the attribution suffix). */ tags?: string[]; /** Who runs the resource (provider/org name) — 402 Index `provider` metadata. */ provider?: string; /** Contact email for the listing (402 Index `contact_email`) — also used by domain claim. */ contactEmail?: string; /** A JSON request body 402 Index should send when health-checking a POST/PUT resource * (402 Index `probe_body`), so probes succeed and the reliability score stays high. */ probeBody?: unknown; /** * Attribute the listing to PipRail. **Default ON** (set `false` to opt out). When on, the * payload gets a `via: '@piprail/sdk'` provenance field AND a compact `· Built with * @piprail/sdk` suffix on the description (see {@link appendAttribution}) — the one field an * index displays, so the listing is *visibly* built with PipRail as it spreads, the same * unobtrusive "Made with X" marker as the `/openapi.json` `x-generator`. It's metadata only: * never changes how the resource is paid or ranked, never double-stamps a description that * already mentions PipRail, and never fabricates one you didn't provide. The `User-Agent` * carries PipRail on every request regardless. */ attribution?: boolean; } /** * Static, agent-readable lifecycle facts about one open index — the SINGLE source * of truth that {@link RegisterOutcome.visibility}/`note` are projected from, and * that an agent can also query directly (via {@link getDirectoryInfo}) to reason * about an index BEFORE calling. Best-effort: an index can change its behaviour, * so treat the timing as guidance, not an SLA. */ interface DirectoryInfo { source: DiscoverySource; /** How a new listing is gated: a synchronous URL probe (`402index`, `x402scan`), * or coupled to a facilitator settling a payment (`bazaar`). */ review: 'probe-sync' | 'settle-coupled'; /** Auth needed to WRITE a listing. */ auth: 'none' | 'siwx' | 'facilitator-only'; /** Chains (CAIP-2) this index will list. `null` = any chain the resource advertises. */ chains: readonly string[] | null; /** Visibility a SUCCESSFUL listing reaches here (the steady state a non-failed * outcome maps to). */ onSuccess: ListingVisibility; /** Whether THIS SDK's {@link PipRailClient.discover} reads this index. It reads * `bazaar` + `402index`; it does NOT read `x402scan` — so a live x402scan listing * won't appear in `discover()` results. Don't read that absence as failure. */ readByDiscover: boolean; /** One-line caveat: why a register might fail, or what to expect afterwards. */ caveat: string; } /** * The open directories' lifecycle, as one queryable map. An agent can branch on * this without embedding directory knowledge: `DIRECTORY_INFO[source].readByDiscover`, * `.chains`, `.onSuccess`, etc. {@link PipRailClient.register} projects the relevant * entry onto every {@link RegisterOutcome} (`visibility` + `note`). */ declare const DIRECTORY_INFO: Readonly>; /** Lifecycle facts for one open index (auth, chains, how soon a listing is * findable, whether `discover()` reads it). See {@link DIRECTORY_INFO}. The param * is the closed {@link DiscoverySource} union (TS callers are safe); a string * outside it returns `undefined` at runtime. */ declare function getDirectoryInfo(source: DiscoverySource): DirectoryInfo; /** Project the static {@link DIRECTORY_INFO} lifecycle facts onto a register * outcome, so an agent gets `visibility` + `note` in the result it already holds — * no second lookup. A failed outcome is always `'not-listable'`. Idempotent. */ declare function decorateOutcome(o: RegisterOutcome): RegisterOutcome; /** Normalize an index's network field to CAIP-2 when we recognise the slug; * pass a value that's already CAIP-2 (`namespace:reference`) through unchanged. * A legacy/superseded CAIP-2 id is mapped to its canonical replacement first * (so a colon-bearing legacy id like `ton:-239` still canonicalizes). * An unknown slug returns unchanged (no `:`), which the client treats as * "unresolved — don't hide it" rather than a confident mismatch. */ declare function normalizeNetwork(network: string): string; /** * Search the open indexes for payable resources, in parallel, and merge them into * one ranked list (deduped by resource URL — the first source in `sources` wins). * NEVER throws: any index that errors, times out, or changes shape contributes `[]`. * * Pipeline: fetch (402 Index server-side, with a per-token fan-out for multi-word * queries + server-side filters; Bazaar list, filtered client-side) → merge + dedupe * → client-side filters ({@link SearchOpenIndexesOptions.maxPrice}/`category`/`asset`/ * `minReliability`, all keeping items the index didn't annotate) → rank/sort. */ declare function searchOpenIndexes(opts?: SearchOpenIndexesOptions): Promise; /** Relevance score of one resource for a tokenized query. Exact token hits score full * weight; a substring/prefix hit scores a fraction (only for tokens ≥4 chars, so short * words like "ai"/"for" don't fuzz-match noise). A resource matching EVERY query token * gets a big "complete match" bonus (this is what makes multi-word queries pinpoint). * Returns 0 when nothing matched (the caller drops those). */ declare function scoreResource(r: DiscoveredResource, queryTokens: string[]): number; /** * Rank resources by relevance to `query`, dropping non-matches and stamping each kept * resource with its `score` (descending). Pure + stable: equal scores keep input order * (so the dedupe's source priority survives a tie). No query → returned unchanged. */ declare function rankResources(items: DiscoveredResource[], query: string | undefined): DiscoveredResource[]; /** * Register a resource on **402 Index** — the primary, friction-free path: a * single POST, no auth, no signature, no payment. A self-registered listing is * **pending review** (not searchable until approved — verify your domain on * 402index.io for instant approval). Returns a structured outcome; never throws * for an HTTP/transport problem. NOTE: the outcome is BARE — `visibility`/`note` * are added by {@link PipRailClient.register} (or call {@link decorateOutcome}). */ declare function register402Index(input: RegisterInput): Promise; /** * Register on **x402scan** via SIWX (Sign-In-With-X): POST the URL, sign the * EIP-4361 challenge with the merchant's own key, resend with the * `SIGN-IN-WITH-X` header. Facilitator-free, but **Base/Solana-only** and EVM * signing today. EXPERIMENTAL — the open SIWX handshake is a moving convention; * validate against x402scan before relying on it. Never throws. NOTE: returns a * BARE outcome — `visibility`/`note` are added by {@link PipRailClient.register} * (or call {@link decorateOutcome}). */ declare function registerX402Scan(input: { url: string; }, signer: DiscoverySigner): Promise; /** What {@link claim402IndexDomain} returns — the proof to SERVE so 402 Index will * approve your domain (and flip your `pending-review` listings to searchable). */ interface DomainClaim { ok: boolean; domain: string; /** The exact text to serve as the ENTIRE body of `verificationUrl` — this is what * 402 Index fetches and checks (the SHA-256 of the token). Always populated on * success: read from the response, or computed as `sha256(verificationToken)` if * the API returns only the token. Serve THIS. */ verificationHash?: string; /** The raw 64-hex token 402 Index issued (the preimage of `verificationHash`). */ verificationToken?: string; /** Where to serve `verificationHash` — your `https:///.well-known/402index-verify.txt`. */ verificationUrl?: string; /** 402 Index's own human instructions. */ instructions?: string; httpStatus?: number; /** Failure reason when `ok:false`. */ detail?: string; } /** What {@link verify402IndexDomain} returns once the proof is in place. */ interface DomainVerification { ok: boolean; domain: string; /** 402 Index's status string, e.g. `'verified'`. */ status?: string; /** How many of your pending listings were approved by the verification. */ servicesCount?: number; httpStatus?: number; detail?: string; } /** * Step 1 of 402 Index domain verification: claim the host of `domainOrUrl`. 402 Index * lists a self-registered resource as PENDING REVIEW; verifying the domain approves it * (and every other pending listing on that domain) so it becomes searchable. Returns the * `verificationHash` to serve as the entire body of `verificationUrl` * (`https:///.well-known/402index-verify.txt`). Then call {@link verify402IndexDomain}. * No funds move. Never throws. */ declare function claim402IndexDomain(domainOrUrl: string, opts?: { contactEmail?: string; }): Promise; /** * Step 2 of 402 Index domain verification: after {@link claim402IndexDomain} and serving * the `verificationHash` at `verificationUrl`, tell 402 Index to re-fetch + approve. On * success, the domain's pending listings become searchable (`status:'verified'`, * `servicesCount` approved). No funds move. Never throws. */ declare function verify402IndexDomain(domainOrUrl: string): Promise; /** The compact "Made with X" marker {@link appendAttribution} adds to a listing's * description by default (the elegant, universally-accepted Swagger/Hugo pattern). * Middot-separated so it reads as metadata, never as part of the merchant's prose. */ declare const REGISTER_ATTRIBUTION = "\u00B7 Built with @piprail/sdk"; /** Append {@link REGISTER_ATTRIBUTION} to a listing description — *tastefully*. Pure. * Returns the description unchanged when it's absent (we never fabricate one), already * mentions PipRail (never double-stamp), or the result would exceed a sane listing cap * (≤ 500 chars). This is the only attribution an index actually DISPLAYS, so it's how a * registered listing stays visibly "built with PipRail" — opt out with `attribution:false`. */ declare function appendAttribution(description: string | undefined): string | undefined; /** * Fold keyword tags into a description as a compact, searchable tail — `desc · Keywords: * a, b, c` — because 402 Index search is literal (a term must appear in the name or * description to match). Pure + tasteful: drops tags already present in the text * (case-insensitive, no duplication), no-ops when there are no tags, and returns the * description unchanged rather than overflow a sane listing cap (≤ 500 chars). When the * description is empty it still seeds one from the tags (so they're not lost). */ declare function appendKeywords(description: string | undefined, tags: string[] | undefined): string | undefined; /** * ── The PipRail error model (the SDK-wide standard) ─────────────────────── * Full standard: `sdk/ERRORS.md`. In short, errors surface through exactly two * channels, both chain-agnostic — an EVM, Solana, TON or Stellar failure looks * identical to the caller: * * 1. THROWN — a typed {@link PipRailError} subclass, each with a stable * SCREAMING_SNAKE `.code`. For config / flow / wallet / registry problems * the caller can act on (wrong-family wallet, missing driver, insufficient * funds, retries exhausted, …). Catch with `err instanceof PipRailError`. * 2. RETURNED — `verify()` returns a `VerifyResult` `{ ok: false, error, * detail }`, where `error` is a closed `VerifyErrorCode` (see x402.ts). * This is how server-side proof verification rejects; the gate turns it * into a 402 body `{ status: 'invalid', error, detail }` (built once by * {@link toInvalidBody}), and the client relays that reason to the agent. * * Affordability ("wallet can't pay") always converges on ONE typed * {@link InsufficientFundsError}, by two mechanisms: message-regex drivers * (Solana, TON) call {@link toInsufficientFundsError}; structured-error drivers * (EVM via viem's `BaseError` walk, Stellar via Horizon `result_codes`) detect * it from typed data — but every one throws the same InsufficientFundsError. * * So whoever hits an error — a human, the merchant server, or an AI agent — * gets a typed `.code` (thrown) or a `VerifyErrorCode` (returned) plus a * human-readable message, never an opaque chain-library error. * * Base class: every error the SDK throws extends this, so consumers can filter * SDK-originated errors from arbitrary ones with: * * catch (err) { * if (err instanceof PipRailError) ... * } */ declare abstract class PipRailError extends Error { abstract readonly code: string; constructor(message: string, options?: ErrorOptions); } /** Wallet balance too low for the quoted amount + gas. */ declare class InsufficientFundsError extends PipRailError { readonly code = "INSUFFICIENT_FUNDS"; } /** * The payment can't be delivered because the RECIPIENT (`payTo`) isn't set up to * receive on this chain yet — a chain-level *state* requirement, NOT the payer's * balance. It's deliberately distinct from {@link InsufficientFundsError} so a * caller (especially an AI agent) can tell the two fixes apart: * * - `INSUFFICIENT_FUNDS` → fund the **payer** (more token, native gas, or reserve). * - `RECIPIENT_NOT_READY` → set up the **recipient**, e.g. * · XRPL — activate the account (it must hold ≥1 XRP base reserve to exist); * · Stellar — the account must exist (≥1 XLM reserve) and hold a trustline for the asset; * · NEAR — `storage_deposit`-register the recipient on the NEP-141 token (~0.00125 NEAR). * * The message states the requirement and the fix in plain language **and echoes * the raw chain code** (e.g. `(XRPL: tecNO_DST_INSUF_XRP)`), while the untouched * chain error is preserved on `.cause` for deeper debugging. Chains with no * receive prerequisite (EVM, Solana, Sui, Aptos, Tron, and native TON/NEAR) never throw it. */ declare class RecipientNotReadyError extends PipRailError { readonly code = "RECIPIENT_NOT_READY"; } /** * Best-effort: turn a chain library's "wallet can't afford it" error into the * SDK's typed {@link InsufficientFundsError}, by matching its message. Drivers * WITHOUT structured error data (Solana, TON) call this in their `send()` catch * and rethrow the original on a miss: * * catch (err) { throw toInsufficientFundsError(err) ?? err } * * Drivers WITH structured data (EVM via viem `BaseError.walk`, Stellar via * Horizon `result_codes`) detect affordability from that data but still throw * the SAME InsufficientFundsError, and fall through to this as a message-level * backstop so the two paths can't drift. A miss returns null, so the original * (still descriptive) error propagates unchanged — never swallowed. */ declare function toInsufficientFundsError(err: unknown): InsufficientFundsError | null; /** Paywall is on chain X, client wallet is on chain Y. */ declare class WrongChainError extends PipRailError { readonly code = "WRONG_CHAIN"; } /** * Broadcast confirmed on-chain but server didn't return 200 within timeout. * The user got their tokens debited but the gated content is unreachable. * * `.ref` is the on-chain proof (tx hash / signature / locator) that was already * broadcast. **Re-verify or re-submit `ref` — never re-pay** (a fresh payment * would double-spend). The same proof stays valid until the server's * `maxTimeoutSeconds` recency window elapses (default 600s). */ declare class PaymentTimeoutError extends PipRailError { readonly code = "PAYMENT_TIMEOUT"; /** The already-broadcast proof ref — recover with it, don't re-pay. */ readonly ref?: string; constructor(message: string, options?: ErrorOptions & { ref?: string; }); } /** * Paid, retried, still got 402 — the server rejected our proof on every attempt. * * `.ref` is the on-chain proof that was broadcast. The rejection may be transient * (the server's RPC node lagging/throttled and not yet seeing the tx) — so * **re-verify or re-submit `ref` before doing anything else; never re-pay**, or * you risk a double payment. The proof stays redeemable until the server's * `maxTimeoutSeconds` recency window elapses (default 600s). A persistent * rejection with a definitive code (`amount_too_low`, `wrong_recipient`, …) * means the proof genuinely doesn't satisfy the challenge. */ declare class MaxRetriesExceededError extends PipRailError { readonly code = "MAX_RETRIES_EXCEEDED"; /** * The proof ref — recover with it, don't re-pay. Its meaning depends on the * scheme: for `onchain-proof` it's the already-broadcast transaction ref * (re-verify or re-submit it). For a standard `exact` rail it's the authorization's * single-use marker (NOT a tx hash) — re-PRESENT the same signed authorization, never * re-sign a fresh nonce, and verify it on-chain before assuming it didn't settle. On * EVM that marker is the EIP-3009 NONCE (a `0x…` value), checked via the token's * `authorizationState(from, nonce)`; the non-EVM `exact` rails key off their own marker * (Solana tx signature, Algorand group id, Aptos account sequence number, NEAR access-key nonce). */ readonly ref?: string; constructor(message: string, options?: ErrorOptions & { ref?: string; }); } /** * A typed, machine-readable discriminator on a {@link PaymentDeclinedError} so an * agent can branch on WHY a payment was refused WITHOUT regexing the human * message. It's a HINT layered on top of the always-reliable `.code` * (`'PAYMENT_DECLINED'`) — the two-channel error model is unchanged; this adds NO * new `.code`. Values: * - `'POLICY'` — a chain/host/token/per-payment cap refused it. * - `'BUDGET'` — the per-(network,asset) lifetime `maxTotal` cap. * - `'OUTSIDE_WINDOW'` — the rolling `windowTotal` cap (wait for it to slide). * - `'SESSION_EXPIRED'`— the session TTL elapsed. **TERMINAL** — every payment * this process is now refused; do NOT retry, restart/extend the TTL. * - `'APPROVAL'` — an `onBeforePay` approval hook said no (e.g. an MCP * human-in-the-loop decline). Terminal for this pay — do NOT auto-retry. */ type DeclineReasonCode = 'POLICY' | 'BUDGET' | 'OUTSIDE_WINDOW' | 'SESSION_EXPIRED' | 'APPROVAL'; /** * The client refused to pay BEFORE any on-chain send — the quoted payment * exceeded the configured {@link PaymentPolicy} (amount/total ceiling, a * chain/token/host outside the allowlist, or the session's time envelope), or an * `onBeforePay` hook returned `false`. No funds moved. The message names which * guard fired; inspect the `quote` via `client.quote(url)` to see the full * breakdown. * * `.reasonCode` is an optional, typed {@link DeclineReasonCode} the client stamps * so an agent can branch on the cause (and spot a TERMINAL `'SESSION_EXPIRED'` / * `'APPROVAL'` it must not retry) without parsing the prose. `.code` stays * `'PAYMENT_DECLINED'`. */ declare class PaymentDeclinedError extends PipRailError { readonly code = "PAYMENT_DECLINED"; /** Why it was declined, as a typed enum (a hint; `.code` is the reliable channel). */ readonly reasonCode?: DeclineReasonCode; constructor(message: string, options?: ErrorOptions & { reasonCode?: DeclineReasonCode; }); } /** * The payment broadcast but didn't confirm within the driver's polling window * (EVM: minConfirmations; Solana: commitment; TON: seqno; Stellar: ledger * visibility). The tx may still confirm — re-check the proof ref. Distinct from * {@link PaymentTimeoutError}, which is the SERVER not responding *after* a * confirmed payment. */ declare class ConfirmationTimeoutError extends PipRailError { readonly code = "CONFIRMATION_TIMEOUT"; } /** * A standard `exact` payment was VALID (signature recovered, params checked, * simulation passed) but SETTLEMENT failed for a SERVER-side reason — the * merchant's own relayer couldn't broadcast `transferWithAuthorization` (out of * gas, RPC down, dropped tx), or a Mode-B facilitator returned a transport/auth * error. This is NOT the payer's fault: their signed EIP-3009 authorization is * still valid and its nonce UNUSED, so it can be re-presented once the merchant * fixes their relayer/facilitator. The gate THROWS this (it's an operational * problem to fix, not a proof to reject), so a framework adapter returns 5xx — * never a 402, which would wrongly tell the payer to re-pay. `.cause` carries the * raw chain/HTTP error. (Server-side; the `onchain-proof` rail can't raise it — * there the payer broadcasts, so there's nothing for the merchant to settle.) */ declare class SettlementError extends PipRailError { readonly code = "SETTLEMENT_FAILED"; } /** Server returned 402 but the PAYMENT-REQUIRED envelope was missing or malformed. */ declare class InvalidEnvelopeError extends PipRailError { readonly code = "INVALID_ENVELOPE"; } /** * A merchant-supplied gate config value is invalid — a non-decimal / scientific-notation / * non-string `amount` (e.g. `'1e3'`, which must NOT be silently read as 1000 tokens), a missing * `payTo`, or a malformed discovery input. A typed boundary error (ERRORS.md §5) so a merchant * gets a clear, catchable failure instead of a raw `viem`/`TypeError`. */ declare class InvalidConfigError extends PipRailError { readonly code = "INVALID_CONFIG"; } /** * The envelope didn't include any accepts[] entry compatible with the * client's chain id (or all entries used unsupported schemes). */ declare class NoCompatibleAcceptError extends PipRailError { readonly code = "NO_COMPATIBLE_ACCEPT"; } /** * The client was asked to pay a SCHEME the bound chain family/asset/signer can't * settle, and no fallback rail was offered. Specifically: a standard `exact` rail * on a non-EVM family (only EVM ships the buyer `payExact`); an `exact` rail for a * token that isn't EIP-3009 (USDT needs Permit2, native isn't exact-payable, a * plain ERC-20 has no `transferWithAuthorization`); or an `exact` rail whose signer * is a contract / EIP-1271 / EIP-7702-delegated account (no recoverable ECDSA sig). * * Distinct from {@link NoCompatibleAcceptError} (no rail for the network at all) * and {@link WrongFamilyError} (the wallet/payTo/token was given in another * family's shape). The fix is usually "enable/keep an `onchain-proof` rail" or * "pay with a supported chain/token". Thrown by the client / the EVM driver. */ declare class UnsupportedSchemeError extends PipRailError { readonly code = "UNSUPPORTED_SCHEME"; } /** init.body was provided but isn't replayable (e.g. a one-shot ReadableStream). */ declare class NonReplayableBodyError extends PipRailError { readonly code = "NON_REPLAYABLE_BODY"; } /** * A wallet-bound operation was called on a READ-ONLY client — one built with no * `wallet`. Read-only clients still `quote`, `discover`, and `register` (402 Index); * paying, planning, or signing needs a wallet. Pass `wallet` to enable them. */ declare class WalletRequiredError extends PipRailError { readonly code = "WALLET_REQUIRED"; } /** * The chosen chain belongs to one family (EVM, Solana, TON, Stellar, XRPL, Tron, * Sui, NEAR, Aptos, Algorand) but the wallet, payTo, or token was given in another family's form * (e.g. an `0x…` payTo on Solana, or a `{ mint }` token on a Stellar chain). */ declare class WrongFamilyError extends PipRailError { readonly code = "WRONG_FAMILY"; } /** * A built-in token symbol was requested that the chosen chain doesn't ship * (e.g. `token: 'DOGE'`). Use a symbol the chain ships, `'native'`, or pass the * token by full descriptor ({ address, decimals } EVM/Tron · { mint, decimals } * Solana · { master, decimals } TON · { issuer, code, decimals } Stellar · * { issuer, currencyHex, decimals } XRPL · { coinType, decimals } Sui · * { contractId, decimals } NEAR · { metadata, decimals } Aptos · * { assetId, decimals } Algorand). */ declare class UnknownTokenError extends PipRailError { readonly code = "UNKNOWN_TOKEN"; } /** * The requested chain family couldn't be mounted. Drivers auto-mount on first * use, so this means its optional peer deps aren't installed — Solana: * `npm install @solana/web3.js @solana/spl-token bs58`; TON: * `npm install @ton/ton @ton/core @ton/crypto`; Stellar: * `npm install @stellar/stellar-sdk`; XRPL: `npm install xrpl`; Tron: * `npm install tronweb`; Sui: `npm install @mysten/sui`; NEAR: * `npm install near-api-js`; Aptos: `npm install @aptos-labs/ts-sdk`; * Algorand: `npm install algosdk`. */ declare class MissingDriverError extends PipRailError { readonly code = "MISSING_DRIVER"; } /** No registered driver recognised the given `chain` value. */ declare class UnsupportedNetworkError extends PipRailError { readonly code = "UNSUPPORTED_NETWORK"; } /** * Fixed precision (decimal places) the cross-token GRAND-TOTAL accumulator sums in. * Every payment's base-unit amount is scaled to this many decimals before it joins its * denomination's running total, so tokens with DIFFERENT decimals (USDC 6dp, USD1 18dp, * …) add up exactly with no floating point. * * Pinned to {@link MAX_DECIMALS} — the most decimals any priceable token may have — ON * PURPOSE: it guarantees EVERY token the SDK will price can be summed into a grand total. * If this were smaller, a token whose decimals fell in the gap would be silently EXCLUDED * from the denomination total, letting a server slip spend past a `maxTotalPerDenom` cap * (a real bypass — caught by adversarial review: a hostile token labelled `USDC` with * `decimals:25` escaped a 24dp accumulator). With them equal, `scaleToDenom` never returns * null for a valid token, so no spend can escape the cap. */ declare const DENOM_PRECISION = 100; /** * Static, ship-time DENOMINATION labels for the SDK's built-in stablecoins — the * unit of account each one represents, so a caller can cap "total USD across every * stablecoin on every chain" with ONE number ({@link PaymentPolicy.maxTotalPerDenom}). * * This is **not a price oracle**. PipRail never reads a market and never prices a * volatile coin. A denomination is the same kind of static, verified-at-ship-time * metadata as a token's symbol or decimals; the grand total simply ADDS UP tokens * you've grouped as the same unit, each counted 1:1. Native coins (ETH/BNB/SOL/…) * and unrecognised tokens have NO denomination — they are never in a bucket (cap * them with the per-asset `maxTotal` or the payment-count caps instead). * * Keys are UPPERCASE true symbols; extend per-policy via {@link PaymentPolicy.denomFor}. */ declare const BUILTIN_DENOMS: Readonly>; /** * Resolve a token's denomination for the {@link PaymentPolicy.maxTotalPerDenom} * grand total. Resolution order: the caller's `denomFor` override (matched first by * exact on-chain asset id, then by case-insensitive TRUE symbol) → the built-in * label ({@link BUILTIN_DENOMS}) → `undefined` (the token is not part of any grand * total). The result is UPPERCASED so denomination keys are case-insensitive. Pure — * no market read, no chain import. */ declare function denomOf(symbol: string | undefined, asset: string | undefined, policy: PaymentPolicy | undefined): string | undefined; interface PaymentPolicy { /** Per-payment ceiling, human-readable (e.g. '0.10'). Compared using the * token's TRUE decimals, so a server can't understate the price. */ maxAmount?: string; /** Lifetime ceiling for this client, PER DISTINCT ASSET (network+asset). * Summing across different tokens is unit-meaningless without a price * oracle (which the SDK deliberately doesn't add), so each token gets its * own running cap. Pair with `tokens: ['USDC']` for a single-currency budget. * NOTE — the metered `upto` rail debits this (and `maxTotalPerDenom`/`windowTotal`) * by the authorized **MAX**, not the merchant's claimed settled actual, so an * under-reporting merchant can never loosen the leash; the settled actual is * surfaced on each spend record's `settledBase` for reconciliation. */ maxTotal?: string; /** Allowlist of chains the agent may pay on. A 402 on any other chain is * declined. Strings match the configured selector; objects match by id. */ chains?: ChainSelector[]; /** Allowlist of token symbols (matched against the TRUE symbol). The special * value `'native'` is a chain-agnostic alias for the chain's native coin — it * matches the native asset on ANY family (ETH/BNB/TRX/XLM/…) without naming * the ticker, mirroring the merchant-side `token: 'native'`. An asset the SDK * can't recognise never satisfies this list. */ tokens?: string[]; /** Allowlist of hosts. Exact (`api.example.com`) or wildcard (`*.example.com`). */ hosts?: string[]; /** Pay an asset the SDK can't price (custom/unknown token)? Default false — * declined, because its true decimals can't be verified. When true, the * server-stated decimals are trusted (the explicit, opt-in risk). */ allowUnknownTokens?: boolean; /** * Session time-to-live in SECONDS, relative to session start (client * construction). After the deadline EVERY payment is refused * (`SESSION_EXPIRED`), regardless of amount — a headless agent's time leash. * When both `ttlSeconds` and `expiresAt` are set, the EARLIER deadline wins. * Opt-in; omit for no time limit (default). PROCESS-SCOPED — resets on restart. */ ttlSeconds?: number; /** * Absolute session deadline as epoch MILLISECONDS (matches `Date.now()`). * SDK-only — there is no MCP env knob (the MCP exposes only the relative-seconds * `PIPRAIL_TTL`). A small value expires immediately BY DESIGN; it is NOT * auto-corrected from seconds, so pass milliseconds. Opt-in; omit for no limit. */ expiresAt?: number; /** * Rolling-window spend cap per (network, asset), in human units (e.g. '1.00'). * Refuses a payment that would push spend within the last {@link windowSeconds} * past this cap (`OUTSIDE_WINDOW`) — a rate limit on top of the lifetime * `maxTotal`. REQUIRED together with `windowSeconds`: setting one without the * other is a config error (a half-armed leash is forbidden). Opt-in; heavier. */ windowTotal?: string; /** Rolling-window width in seconds for {@link windowTotal}. REQUIRED together with it. * Shared by {@link maxPaymentsPerWindow} (the rolling payment-count cap). */ windowSeconds?: number; /** * Cross-chain, cross-token GRAND-TOTAL spend cap, keyed by DENOMINATION (a unit of * account you declare): e.g. `{ USD: '20.00', EUR: '5.00' }`. The SDK sums the * human-value of every token of that denomination — across every chain, when the * paying clients share a ledger (`MultiChainPayer.fromWallets` or a shared * `spendStore`) — and refuses a payment that would push the denomination's * running total past the cap (`MAX_TOTAL_DENOM`). * * This is the single "spend at most $X total, full stop" leash. It is **NOT a price * oracle** — PipRail never reads a market and never prices a volatile coin. A token's * denomination is a static, ship-time label ({@link BUILTIN_DENOMS}: USDC/USDT/USD1/ * FDUSD/RLUSD → `'USD'`; EURC → `'EUR'`); the SDK just ADDS UP tokens you've grouped * as one unit, each counted 1:1. Native coins and unrecognised tokens have NO * denomination and never enter a bucket — cap those with {@link maxTotal} or * {@link maxPayments}. Keys are case-insensitive. Coexists with the per-asset * `maxTotal`; the stricter cap wins. Opt-in; omit for no grand total (default). * * Best-effort under concurrency (like `maxTotal`, see STANDARDS §7): many in-flight * payments can race past it; the SDK ships no reservation system. */ maxTotalPerDenom?: Record; /** * Fold EXTRA tokens into a denomination for the {@link maxTotalPerDenom} grand total, * by TRUE symbol (`{ PYUSD: 'USD' }`) or by on-chain asset id (`{ '0x…': 'USD' }`). * Your assertion of equivalence, layered ON TOP of the built-in labels — use it for a * stablecoin the SDK doesn't ship a label for, or to group a custom token. Symbol keys * are case-insensitive; asset-id keys match exactly and win over symbol. */ denomFor?: Record; /** * Lifetime cap on the NUMBER of settled payments for this client/ledger — across * EVERY chain and token (a count needs no price oracle, so it spans everything). A * payment that would be the `(maxPayments + 1)`-th is refused (`MAX_PAYMENTS`). Opt-in. */ maxPayments?: number; /** * Rolling-window cap on the NUMBER of payments within {@link windowSeconds} — a rate * limit independent of amount (`WINDOW_COUNT`). REQUIRED together with `windowSeconds` * (which it shares with `windowTotal`); setting it without `windowSeconds` is a config * error. Opt-in. */ maxPaymentsPerWindow?: number; /** * Emit a `budget-threshold` event (via the client's `onEvent`) the FIRST time * cumulative spend crosses this fraction (0–1] of ANY configured cap — `maxTotal`, * `maxTotalPerDenom`, `maxPayments`, `windowTotal`, or `maxPaymentsPerWindow` — so an * agent/operator is warned BEFORE the hard decline. e.g. `0.8` = warn at 80%. Purely * observational (never blocks a payment). Opt-in; omit for no warning (default). */ warnAtFraction?: number; } /** What the policy reasons over — built by the client from the chosen accept. */ interface PaymentIntent { /** Host of the gated URL (for the `hosts` allowlist). */ host: string; /** The selector the client is configured with (for the `chains` allowlist). */ chain: ChainSelector; network: Caip2; asset: string; /** Server-stated base units — what actually transfers on-chain. */ amountBase: bigint; /** TRUE decimals if the asset is recognised, else the server-stated value. */ decimals: number; /** TRUE symbol if recognised, else the server-stated value (may be undefined). */ symbol?: string; /** Did the driver's `describeAsset` recognise this asset? */ recognized: boolean; } /** * A typed, machine-readable code for WHICH guard refused a payment — set by * `deny()` alongside the human `reason`, so the client routes a denial to the * right {@link PayBlocker}/`reasonCode` WITHOUT substring-matching the prose * (which would silently break on a wording tweak). */ type PolicyDenyCode = 'CHAIN' | 'HOST' | 'UNKNOWN_TOKEN' | 'TOKEN' | 'MAX_AMOUNT' | 'MAX_TOTAL' | 'MAX_TOTAL_DENOM' | 'MAX_PAYMENTS' | 'SESSION_EXPIRED' | 'WINDOW_TOTAL' | 'WINDOW_COUNT'; interface PolicyDecision { allowed: boolean; /** Why it was refused (only when `allowed === false`). */ reason?: string; /** Which guard fired, as a typed enum (only when `allowed === false`). */ code?: PolicyDenyCode; } /** * INTERNAL — the injected clock + the pre-sliced per-asset window total the * client builds for the otherwise-pure {@link evaluatePolicy}. NOT exported: the * client is the only producer, and keeping it private de-risks a future * fold-`spentForAssetBase`-into-ctx refactor. All time state is process-scoped. */ interface PolicyContext { /** A single `Date.now()` captured per quote — the expiry check and the window edge share it. */ now: number; /** Session clock origin (epoch-ms) = client construction. */ sessionStart: number; /** Base units spent on THIS (network, asset) within the rolling window; `0n` when no window cap. */ spentInWindowBase: bigint; /** * Running grand-total already spent on THIS intent's DENOMINATION, scaled to * {@link DENOM_PRECISION}, across the (possibly shared, cross-chain) ledger. * Powers `maxTotalPerDenom`. `0n` when no denom cap / the intent has no denomination. */ spentInDenomScaled?: bigint; /** Total settled payments across the ledger so far — powers `maxPayments`. */ paymentCount?: number; /** Settled payments within the rolling window — powers `maxPaymentsPerWindow`. */ paymentCountInWindow?: number; } /** * Evaluate a payment against the policy. `spentForAssetBase` is the running * total already spent on THIS (network, asset) — supplied by the client's * ledger — and powers the per-asset `maxTotal` cap. * * The optional `ctx` carries the injected clock + the pre-sliced window total so * this function stays PURE (no `Date.now()` inside). Omit `ctx` and no time check * runs — behaviour is byte-identical to a time-free policy. * * Checks run in a pinned, deterministic order, first-failure-wins so the reason * is specific: **session expiry → chains → hosts → unknown-token → tokens → * maxAmount → maxTotal → maxTotalPerDenom → maxPayments → windowTotal → * maxPaymentsPerWindow**. Expiry is first because it's session-global (not * asset-scoped) — an expired session must always report expiry, not whichever * other gate happens to also fail. The grand-total / count caps come after the * per-asset caps so the most-specific money cap is reported first. */ declare function evaluatePolicy(intent: PaymentIntent, policy: PaymentPolicy | undefined, spentForAssetBase: bigint, ctx?: PolicyContext): PolicyDecision; /** The payment schemes a client can settle: PipRail's native `onchain-proof` (the * default), the standard x402 `exact` rail (EVM EIP-3009/Permit2 + Solana SVM + Algorand * + Aptos + NEAR, opt-in), and the standard x402 `upto` (metered) rail (EVM-Permit2, opt-in). */ type PaymentScheme = 'onchain-proof' | 'exact' | 'upto'; /** Observability events. `ref` is the proof — a chain-specific id (EVM tx hash, Solana signature, TON locator, Stellar tx hash). */ type PipRailEvent = { kind: 'payment-required'; challenge: X402Challenge; accept: X402AnyAccept; } | { kind: 'payment-broadcast'; ref: string; } | { kind: 'payment-confirmed'; ref: string; blockNumber: bigint; } /** * Broadcast succeeded (we hold `ref`) but LOCAL confirmation timed out — the * RPC was likely lagging/throttled while the tx is in fact on-chain. The proof * is NOT discarded: the client submits it to the server (whose own on-chain * verify is the authority) instead of throwing, so a real payment is never * orphaned into a double-pay. `reason` is the confirm error's message. */ | { kind: 'payment-unconfirmed'; ref: string; reason: string; } /** * The payment settled. `receipt` is PipRail's rich {@link X402Receipt} when the * server returns one (its own gate, or a facilitator that echoes the full shape); * `settle` is the standard x402 SettleResponse (`{ success, transaction, … }`) on * conformant third-party-facilitator interop, where the lean SettleResponse has no * rich receipt — read `settle.transaction` for the on-chain settle tx there. */ /** * The payment settled. `receipt` is PipRail's rich {@link X402Receipt} when the * server returns one (its own gate, or a facilitator that echoes the full shape); * `settle` is the standard x402 SettleResponse (`{ success, transaction, … }`) on * conformant third-party-facilitator interop, where the lean SettleResponse has no * rich receipt — read `settle.transaction` for the on-chain settle tx there. (For the * spend RECORD + remaining budget after a settle, use the `onSpend` callback or * `client.spent()`/`budget()`.) */ | { kind: 'payment-settled'; receipt: X402Receipt | null; settle?: SettleOutcome; } | { kind: 'payment-failed'; reason: string; /** A machine-readable failure code when one is known. For a SERVER rejection it's the SAME * code the merchant's `onFailed` hook receives (a canonical {@link VerifyErrorCode} from a * PipRail gate, or a foreign facilitator's reason string); for a pre-send client DECLINE * (policy / budget / approval) it's that decline reason (e.g. `'BUDGET'`, `'APPROVAL'`). * Absent when no structured code was given. */ code?: string; /** Human-readable detail, when present (e.g. `"Paid 40000, required 500000."`). */ detail?: string; } /** * The client REFUSED to pay before any on-chain send — the spend policy or an * `onBeforePay` hook said no. A dedicated, richer companion to `payment-failed` * (which ALSO still fires on a decline, unchanged): `reasonCode` is the typed * {@link DeclineReasonCode}, `code` the fine-grained {@link PolicyDenyCode}, `quote` * the priced requirement that was refused, and `budget` the spend leash at refusal. * Listen for this to distinguish "I hit my cap" from a server-side failure cleanly. */ | { kind: 'payment-declined'; reason: string; reasonCode?: DeclineReasonCode; code?: PolicyDenyCode; quote?: PipRailQuote; budget: SessionBudget; } /** * Cumulative spend crossed `policy.warnAtFraction` (e.g. 0.8 = 80%) of a configured * cap, BEFORE any hard decline — the early-warning signal. `scope` says which cap: * `'asset'` (per-(network,asset) `maxTotal`), `'denom'` (`maxTotalPerDenom`), `'count'` * (`maxPayments`), `'window'` (`windowTotal`), or `'window-count'` (`maxPaymentsPerWindow`). * `label` names the specific cap (the symbol/network, the denomination, or the cap name); * `fraction` is how far in (≥ warnAtFraction). Fires once per crossing per cap. Purely * observational — it never blocks a payment. */ | { kind: 'budget-threshold'; scope: 'asset' | 'denom' | 'count' | 'window' | 'window-count'; label: string; spentFormatted: string; capFormatted: string; fraction: number; }; /** * Wallet for the chosen chain family. **One field, every chain: `{ key }`** — the * chain's secret as a string (the `chain` selector routes; each driver validates the * format). NEAR also needs `{ accountId }`. What `key` is, per chain: * - EVM / Tron → a 0x… hex private key (secp256k1; Tron also accepts it without the 0x prefix) * - Sui → a `suiprivkey1…` bech32 secret * - Aptos → an AIP-80 `ed25519-priv-0x…` (or raw `0x…`) secret * - Solana → a base58 secret key (or a `Uint8Array`) * - TON → a 24-word mnemonic (optional `version: 'v4' | 'v5r1'`, default v4) * - Algorand → a 25-word mnemonic * - Stellar → an `S…` secret seed * - XRPL → an `s…` secret seed * - NEAR → `{ accountId, key }`, where `key` is an `ed25519:…` secret * * Advanced (bring your own native signer object — type-specific): EVM `{ walletClient }`, * Solana `{ signer }`, TON `{ keyPair }`, Stellar/Sui `{ keypair }`, XRPL `{ wallet }`, * Aptos/Algorand `{ account }`. */ type WalletInput = { key: string; version?: 'v4' | 'v5r1'; } | { accountId: string; key: string; } | { walletClient: unknown; } | { signer: unknown; } | { keyPair: unknown; version?: 'v4' | 'v5r1'; } | { keypair: unknown; } | { wallet: unknown; } | { account: unknown; }; /** * A priced payment requirement — what `client.quote(url)` returns and what an * `onBeforePay` hook receives, so an agent can decide BEFORE any funds move. * `amount`/`decimals`/`symbol` reflect the TRUE token (the SDK's, via * `describeAsset`) when recognised, falling back to the server's stated values. */ interface PipRailQuote { /** The gated URL. */ url: string; /** The chain the client is configured for. */ chain: ChainSelector; network: Caip2; /** On-chain asset id (0x… / mint / jetton master / CODE:ISSUER / 'native'). */ asset: string; /** Amount in base units (what actually transfers). */ amount: string; /** Human-readable amount, e.g. '0.05'. */ amountFormatted: string; decimals: number; symbol?: string; payTo: string; description?: string; maxTimeoutSeconds: number; /** Did the SDK recognise the asset (and so trust its decimals/symbol)? */ recognized: boolean; /** True if the challenge's stated symbol disagrees with the SDK's real one — a * red flag worth surfacing to the agent. */ symbolMismatch: boolean; /** Would the configured `policy` allow paying this? (true when no policy set.) */ withinPolicy: boolean; /** Why the policy would refuse it (only when `withinPolicy === false`). */ policyReason?: string; /** The TYPED reason the policy refused it (only when `withinPolicy === false`) — * routes the denial to the right blocker/`reasonCode` without parsing prose. */ policyCode?: PolicyDenyCode; } /** * What `client.estimateCost(url)` returns: the priced payment requirement plus * the estimated NETWORK FEE (gas) to settle it, in the chain's native coin. * Two distinct numbers an agent weighs before paying — `quote.amountFormatted` * is what leaves the wallet as payment; `cost.feeFormatted` is the native-coin * gas burned to send it (you pay USDC but spend ETH/SOL/TRX/… on gas). */ interface PipRailCostQuote { /** The priced payment requirement (amount/token/chain/recipient/policy). */ quote: PipRailQuote; /** Estimated network fee (gas) in the chain's native coin. */ cost: CostEstimate; } /** A hard reason a rail can't be settled right now — each maps to a concrete fix. */ type PayBlocker = 'INSUFFICIENT_TOKEN' | 'INSUFFICIENT_GAS' | 'RECIPIENT_NOT_READY' | 'OUTSIDE_POLICY' | 'OUTSIDE_WINDOW'; /** A soft flag — never blocks, always worth surfacing to the agent. */ type PayWarning = 'SYMBOL_MISMATCH' | 'BALANCE_UNREADABLE' | 'RECIPIENT_READINESS_UNKNOWN' | 'GAS_HEURISTIC' | 'THIN_GAS_MARGIN'; /** One offered rail, fully analysed against the bound wallet's own holdings. */ interface PayOption { /** The rail this analyses (one entry from the 402's accepts[]) — an * `onchain-proof` rail or, when `schemes` enables it, a standard `exact` rail. */ accept: X402AnyAccept; /** The priced requirement — TRUE decimals/symbol + the policy verdict. */ quote: PipRailQuote; /** Estimated native-coin gas to send it (cost.basis surfaced). */ cost: CostEstimate; /** The verdict for THIS rail. 'unknown' = a read failed, so payability can't be confirmed. */ state: 'payable' | 'blocked' | 'unknown'; /** Hard reasons it's blocked (empty when payable). */ blockers: PayBlocker[]; /** Soft flags (may be present even when payable). */ warnings: PayWarning[]; /** Live wallet holdings, human units; null = the read was unavailable (NOT zero). */ balance: { token: string | null; native: string | null; }; /** What this rail needs: the payment amount + the estimated gas, human units. */ need: { token: string; native: string; }; /** How much MORE is needed to clear a funds blocker, human units (omitted when funded). */ shortfall?: { token?: string; native?: string; }; /** Can payTo receive this asset right now, and if not, what fixes it. */ recipient: { ready: boolean | 'n/a' | 'unknown'; reason?: RecipientReason; fix?: string; }; } /** The plan for ONE 402 across every rail this client can pay (its bound network). */ interface PaymentPlan { url: string; /** The network this client is bound to (the rails it can settle). */ network: Caip2; /** Top-level verdict for instant branching. */ status: 'ready' | 'blocked' | 'unknown'; /** True iff at least one rail is payable now (best !== null). */ payable: boolean; /** The rail to use: the cheapest (within native coin) payable option, or null. */ best: PayOption | null; /** Every offered+supported rail, ranked: payable → unknown → blocked. */ options: PayOption[]; /** When NOT payable: one human, actionable sentence on exactly what to do. */ fundingHint: string | null; /** * Read-only TIME envelope — present ONLY when the policy configures one * (`ttlSeconds`/`expiresAt`). Lets a headless (Mode A) agent SEE its remaining * time leash before paying, rather than discovering it by hitting a decline. * * PROCESS-SCOPED: resets to a fresh window on restart; for crash-loop-resistant * limits supply a pluggable durable store (the `isUsed`/`markUsed` analogue). * `secondsRemaining` is a best-effort host wall-clock estimate, clamped ≥ 0. */ session?: { expiresAt: number | null; secondsRemaining: number | null; }; } /** * A read-only view of the spend leash for a Mode-A agent — `client.budget()`. * Composes the in-memory ledger + the configured policy WITHOUT coupling them. * * PROCESS-SCOPED: every figure resets on restart — the session IS the process. * For crash-loop-resistant limits supply a pluggable durable store (the * `isUsed`/`markUsed` analogue). `secondsRemaining` is clamped ≥ 0. */ interface SessionBudget { /** The session's time envelope (null fields when no `ttlSeconds`/`expiresAt`). */ session: { /** Session start, ISO. */ start: string; /** Deadline as ISO, or null when no time limit is configured. */ expiresAt: string | null; /** Seconds until expiry (clamped ≥ 0), or null when no time limit. */ secondsRemaining: number | null; }; /** Per-(network, asset) money leash — ONE row per pair the ledger has seen. */ byAsset: SpendRemaining[]; /** * The cross-token GRAND TOTAL leash, one row per DENOMINATION the policy caps * (`maxTotalPerDenom`). Unlike `byAsset`, these rows are present from the START * (before any spend) because the cap is a single declared number — so an agent can * preview "how much USD can I still spend, across everything" up front. Empty when * no `maxTotalPerDenom` is configured. */ byDenom: DenomRemaining[]; /** * The payment-COUNT leash — settled count so far + the lifetime/window caps and * what's left. `lifetimeCap`/`windowCap` are undefined when not configured. */ counts: CountStatus; } /** * Per-(network, asset) remaining budget — the money half of the leash. One row * per pair the LEDGER already holds (decimals are known only after the first * spend), so a never-spent pair simply isn't a row. `cap`/`remaining` are * `undefined` when no `policy.maxTotal` is set (unbounded). Never a cross-token * sum — there is no price oracle. */ interface SpendRemaining { network: Caip2; asset: string; symbol?: string; decimals: number; /** Base units spent so far on this pair. */ spentBase: string; /** The `maxTotal` cap in base units; undefined when unbounded. */ capBase?: string; /** `max(0, cap − spent)` in base units; undefined when unbounded. */ remainingBase?: string; /** `remainingBase` in human units; undefined when unbounded. */ remainingFormatted?: string; } /** * The cross-token GRAND-TOTAL leash for one DENOMINATION (e.g. `USD`) — the sum of * every stablecoin of that unit across every chain, vs the `maxTotalPerDenom` cap. * NOT a price-converted figure: tokens grouped as one unit, each counted 1:1. */ interface DenomRemaining { /** The denomination, e.g. `'USD'`. */ denom: string; /** Human-readable spend so far in this denomination, e.g. '12.34'. */ spentFormatted: string; /** The cap (human units), e.g. '20.00'. */ capFormatted: string; /** `max(0, cap − spent)` in human units. */ remainingFormatted: string; /** Fraction of the cap used so far (0–1+, clamped ≥ 0). */ fraction: number; } /** The payment-COUNT leash — settled count + the configured lifetime/window caps. */ interface CountStatus { /** Settled payments so far (across every chain + token). */ settled: number; /** Lifetime cap (`maxPayments`), or undefined when unbounded. */ lifetimeCap?: number; /** `max(0, lifetimeCap − settled)`, or undefined when unbounded. */ lifetimeRemaining?: number; /** Rolling-window cap (`maxPaymentsPerWindow`), or undefined when unset. */ windowCap?: number; /** Payments within the current window, or undefined when no window count cap. */ windowSettled?: number; /** `max(0, windowCap − windowSettled)`, or undefined when unset. */ windowRemaining?: number; } interface PipRailClientOptions { /** * Wallet for the chosen chain family. **Optional** — omit it for a READ-ONLY * client that can `quote`, `discover`, `estimateCost`, and `register` (402 Index) * with no key. Paying, planning, or signing then throws {@link WalletRequiredError} * until a wallet is provided. Supplying a wallet is byte-identical to before. */ wallet?: WalletInput; /** Which chain to pay on. EVM ('bnb'|'base'|…), 'solana', 'ton', 'stellar', * 'xrpl', 'tron', 'sui', 'near', 'aptos', or 'algorand'. */ chain: ChainSelector; /** Override the chain's default RPC URL (recommended in production). */ rpcUrl?: string; /** * Spend guardrails for autonomous payment — per-payment + lifetime ceilings * and chain/token/host allowlists. A 402 that violates the policy is refused * with {@link PaymentDeclinedError} BEFORE any on-chain send. Omit for the * (unguarded) default. See {@link PaymentPolicy}. */ policy?: PaymentPolicy; /** * Final approval hook, called with the {@link PipRailQuote} after the policy * passes but before paying. Return `false` (or a rejected promise resolving * false) to refuse — the client throws {@link PaymentDeclinedError} and no * funds move. Use for human-in-the-loop or custom per-payment logic. */ onBeforePay?: (quote: PipRailQuote) => boolean | Promise; /** * After paying, how many times to re-send the request with proof before * giving up. Default 3, with a short backoff between attempts — this * absorbs RPC propagation lag (the server's node briefly trailing the * client's, so it hasn't seen the confirmation yet). If the server still * returns 402 on the last attempt the SDK throws `MaxRetriesExceededError` * (which carries `.ref` — re-verify, never re-pay). * * If the broadcast succeeded but the client's OWN confirmation timed out * (a throttled RPC), the proof is NOT discarded: the client submits it * anyway and automatically uses MORE patient retries (a floor of 6, longer * backoff), since the on-chain tx may still be settling. */ maxPaymentRetries?: number; /** Timeout (ms) for the retry leg after broadcast. Default 30_000. */ retryTimeoutMs?: number; /** * Balance-aware routing: when true, `fetch()` runs `planPayment` on a 402 and * pays the cheapest rail the wallet can ACTUALLY settle (token + native gas + * recipient-ready), instead of the first policy-passing accept. If none is * settleable it throws {@link PaymentDeclinedError} carrying the funding hint — * before any send. Default **false** (defaults never change): the zero-config * path keeps its existing selection. Recommended for multi-rail 402s. Override * per call with `fetch(url, { autoRoute: true })`. */ autoRoute?: boolean; /** * Which payment SCHEMES this client may settle. Default **`['onchain-proof']`** * (defaults never change): the zero-config client pays only PipRail's native * backendless rail, exactly as before. Add `'exact'` to ALSO pay standard x402 * `exact` rails — letting the agent pay ANY standard x402 server (the dominant * `exact`-on-Base-via-CDP web), not just PipRail's own gates: * * new PipRailClient({ chain: 'base', wallet, schemes: ['onchain-proof', 'exact'] }) * * `exact` is **EVM (EIP-3009/Permit2) + Solana (SVM)** today (USDC/EURC); it's * silently ignored on a family without an `exact` rail, for native, or for a token the * SDK can't price — those keep `onchain-proof`. The agent signs the authorization (an * EIP-3009 message on EVM, a partial-signed transaction on Solana) with its OWN wallet * and the server / merchant-chosen facilitator broadcasts it (the buyer pays ~0 * gas; PipRail hosts/settles nothing). The same `policy` + `onBeforePay` gate it * BEFORE signing. **Verify against your target facilitator before production.** * Override per call with `fetch(url, { schemes })`. */ schemes?: PaymentScheme[]; /** Logger hook. Default no-op. */ onEvent?: (event: PipRailEvent) => void; /** * Durable spend store — make the budget SURVIVE a restart. By default the spend * ledger is in-memory and process-scoped (a restart zeroes `maxTotal` / * `maxTotalPerDenom` / the count caps, so a crash-loop could re-spend). Pass a * {@link SpendStore} and the ledger HYDRATES from it at construction and persists * every settled payment, so caps resume where they left off — with NO PipRail * backend (you own the store, like the gate's `isUsed`/`markUsed` replay set). * One line for local persistence: `spendStore: fileSpendStore('./spend.jsonl')` * from `@piprail/sdk/node`. The rolling window + session TTL stay process-scoped. */ spendStore?: SpendStore; /** * Advanced: share ONE {@link SpendLedger} across several single-chain clients so the * cross-token grand total (`maxTotalPerDenom`) and the payment-count caps span ALL of * them. `MultiChainPayer.fromWallets` sets this for you; reach for it directly only when * composing clients by hand. Mutually exclusive with `spendStore` (the shared ledger * owns the store) — passing both throws. Pass at most one client per (network, asset) * into a shared ledger or `spent()` double-counts. */ ledger?: SpendLedger; /** * Fire-and-forget callback after EACH settled payment — the one-liner for "append my * spend to a local log / push it somewhere". Receives the {@link SpendRecord} just * written and the spend leash AFTER it. Isolated like `onEvent`: a throw is swallowed * and can never abort a payment. (Same data also rides the `payment-settled` event.) */ onSpend?: (record: SpendRecord, budget: SessionBudget) => void; } /** Options for {@link PipRailClient.discover}. */ interface DiscoverOptions { /** Free-text query, matched against name/description/resource. */ query?: string; /** * Which network's resources to return: a CAIP-2 id (or a chain slug like * `'base'` — normalized to CAIP-2 before matching), `'self'` (the client's * bound chain — the default, so results are payable by THIS client), or * `'any'` (every chain — the agent filters later). */ network?: Caip2 | 'self' | 'any' | (string & {}); /** * Coarse pre-filter: drop results whose advertised USD price exceeds this. * Results with no advertised price pass through — use `quote()` for the exact * figure before paying. */ maxPrice?: number; /** Keep ONLY this category, e.g. `'ai'` (prefix match) — strict: results the index * didn't categorize are dropped, so real category matches aren't drowned by un-tagged ones. */ category?: string; /** Keep only resources paying in this asset symbol, e.g. `'USDC'` (keeps results whose * asset the index didn't report — confirm with `quote()`). */ asset?: string; /** Drop results whose reliability score (0–100) is below this. Results with no reported * score pass through (Bazaar doesn't measure it); inspect `result.reliabilityScore`. */ minReliability?: number; /** Prefer verified listings (402 Index server-side). Its `verified` flag differs from the * per-record `domain_verified`, so it's applied at the index; inspect `result.verified`. */ verified?: boolean; /** Restrict to listings the index confirmed are payable x402 (402 Index `payment_valid`). */ paymentValid?: boolean; /** * Result ordering. Default `'relevance'` when a `query` is given (best matches first), * else first-seen order. `'reliability'`/`'price'`/`'uptime'`/`'name'` sort by that field. */ sort?: DiscoverySort; /** Direction for a non-relevance `sort`. Default `'desc'`. */ order?: 'asc' | 'desc'; /** Which open indexes to read. Default `['bazaar', '402index']` (both free). */ sources?: DiscoverySource[]; /** Max results to fetch per index request. Default 20. */ limit?: number; } /** Options for {@link PipRailClient.register}. */ interface RegisterOptions { /** Display name for the listing (defaults to the URL's host). */ name?: string; description?: string; /** Advertised price in USD (metadata only). */ priceUsd?: number; /** Payment asset symbol, e.g. `'USDC'` (metadata). */ asset?: string; /** Payment network slug, e.g. `'base'` (defaults to the client's `chain` when it's a slug). */ network?: string; /** HTTP method the resource answers on. Default 'GET'. */ method?: string; /** * A category for the listing, e.g. `'ai'`, `'finance'`, `'data'`. The highest-leverage * findability field — most of 402 Index's catalog is `uncategorized`, so a real category * makes a resource rank + filter where almost nothing else does. */ category?: string; /** * Keywords for the listing. Folded into the description as a searchable tail (402 Index * search is literal — a term must appear in the text to match) and sent as a `tags` field. */ tags?: string[]; /** Who runs the resource (provider/org name). */ provider?: string; /** Contact email for the listing. */ contactEmail?: string; /** A JSON request body the index should send when health-checking a POST/PUT resource. */ probeBody?: unknown; /** * Which open indexes to list on. Default `['402index']` — no auth, no * signature. Add `'x402scan'` for the SIWX path (needs an EVM `discoverySigner` * and a Base/Solana rail). `'bazaar'` can't be written to (facilitator-only). */ targets?: DiscoverySource[]; /** * Attribute the listing to PipRail. **Default ON** (set `false` to opt out). When on, the * listing gets a `via: '@piprail/sdk'` provenance field plus a compact `· Built with * @piprail/sdk` suffix on the description — the same unobtrusive "Made with X" marker as the * `/openapi.json` `x-generator`. Metadata only: it never changes how the resource is paid or * ranked, never double-stamps a description that already names PipRail, and never fabricates * one. The request `User-Agent` carries PipRail regardless. */ attribution?: boolean; } /** * The read-+-pay surface an agent toolkit needs — the methods {@link paymentTools} * calls. BOTH {@link PipRailClient} (one chain) and {@link MultiChainPayer} (many * chains, one per wallet) satisfy it, so `paymentTools` wraps either unchanged: * point an MCP/LLM at one wallet or at a whole bundle without touching the tools. */ interface PayingClient { discover(opts?: DiscoverOptions): Promise; quote(url: string, init?: RequestInit): Promise; planPayment(url: string, init?: RequestInit): Promise; get(url: string, init?: RequestInit): Promise; fetch(url: string, init?: RequestInit): Promise; register(url: string, opts?: RegisterOptions): Promise; spent(): SpendSummary; budget(): SessionBudget; /** The CONFIGURED spend policy, read back (so an agent can self-check its whole leash * without hitting a decline). `undefined` when no policy is set. */ policy(): PaymentPolicy | undefined; } /** * The verdict from {@link PipRailClient.verifyReceipt} — a {@link PipRailReceipt} * re-verified against the chain, never trusting the receipt's claims. `ok` is the * chain's confirmation that the settlement is real (≥`amount` of `asset` moved to * `payTo`); `onChain` is RE-DERIVED from the tx (`payer` genuinely so; `amount` is a * VERIFIED LOWER BOUND — drivers threshold-check `paid >= required` then echo the * accept amount); `matchesClaims` is whether the re-derived `payer` equals the * receipt's claimed `payer` (a forged payer → `false` even when `ok` is `true`). * * Re-verification is **durable** for digest-bound (Template-B) families (EVM, Solana, * Tron, Sui, Aptos, native coins — the driver reads the tx by hash/digest) and * **recency-bounded / best-effort** for the account-watch families (Stellar, XRPL, * Algorand, TON), whose drivers scan only recent merchant-account history — an old * receipt there can return `transfer_not_found` even though it once settled. */ interface ReceiptVerification { /** The chain confirms the settlement (≥`amount` moved to `payTo`). */ ok: boolean; /** Fields RE-DERIVED from the on-chain tx. `payer` is genuinely re-derived; `amount` * is a verified LOWER BOUND (the chain confirms at least this much moved). */ onChain: { payTo: string; asset: string; amount: string; payer: string; }; /** Does the re-derived on-chain `payer` match the receipt's claimed `payer`? */ matchesClaims: boolean; /** Informational age of the receipt (seconds since `verifiedAt`); NOT a validity gate. */ ageSeconds: number; /** The closed verification code when `ok` is false (reuses the driver vocabulary). */ error?: VerifyErrorCode; } declare class PipRailClient { private readonly opts; private readonly maxRetries; private readonly retryTimeoutMs; private readonly onEvent; private readonly ledger; private bound?; private lastReceiptValue; constructor(opts: PipRailClientOptions); /** * Fail LOUDLY at construction on a malformed amount cap — a security boundary * must never silently half-arm, and a misconfigured cap is a programmer error * (→ `TypeError`, no new SDK code). Each cap (`maxAmount` / `maxTotal` / * `windowTotal`) must be a non-negative decimal STRING (the same grammar * {@link floorUnits} accepts), so a typo like `'0.01abc'` fails fast here instead * of lazily throwing a raw `floorUnits` error out of the never-throw read methods. */ private assertPolicyAmountCaps; /** * Fail LOUDLY at construction on a misconfigured count / threshold control (a * programmer error → `TypeError`): payment counts must be positive safe integers, * `maxPaymentsPerWindow` needs `windowSeconds` (it shares the window), and * `warnAtFraction` must be in (0, 1]. */ private assertPolicySpendControls; /** * Fail LOUDLY at construction on a misconfigured time policy — a security * boundary must never silently half-arm. Two invariants (a misconfiguration is * a programmer error → `TypeError`, no new SDK error code): * - the rolling window needs BOTH `windowTotal` and `windowSeconds`, or NEITHER * (one alone is a leash that silently doesn't bite); * - `ttlSeconds` must be a positive, safe integer whose `*1000` deadline stays * within `Number.MAX_SAFE_INTEGER` (else the arithmetic would lose precision). */ private assertPolicyTimeOptions; /** Emit an observability event, never letting a throwing handler break the * payment flow (mirrors the server gate's `onPaid` isolation). */ private safeEmit; /** * Capture the verifiable receipt from a settled response (pure — no chain read), stamping * the resource URL this client actually fetched (authoritative over the gate's default ''). A * settled fetch with no receipt extension sets it to `null` so {@link lastReceipt} reflects the * latest fetch. Never throws — a malformed header just yields `null`. */ private captureReceipt; /** * The verifiable {@link PipRailReceipt} from the most recent settled `fetch` — the * self-contained record the buyer KEEPS and anyone re-verifies against the chain * (see {@link PipRailClient.verifyReceipt}). `null` when the last settled fetch carried * no receipt (the gate's `receipts` option was off) or no payment has settled yet. Pure. */ lastReceipt(): PipRailReceipt | null; /** * Re-verify ANY {@link PipRailReceipt} against the chain — the anyone-can-run primitive. * Re-reads `receipt.transaction` via the receipt's own network driver and re-derives * `payTo`/`asset`/`payer` from the tx, **never trusting the receipt's claims**: a forged * `payTo`/`asset`/over-stated `amount` makes the driver's `verify()` fail (`ok:false`); a * forged `payer` surfaces as `matchesClaims:false`. Static + WALLET-FREE — a third party * verifies with only a chain + RPC, no PipRail account. **Never throws** (an RPC error or a * malformed receipt → `{ ok:false, error }`). Viem-free here — the chain read happens inside * the lazily-mounted family driver (the protocol layer pulls no chain libs). Durable for * digest-bound families; recency-bounded for the account-watch families (see * {@link ReceiptVerification}). */ static verifyReceipt(receipt: PipRailReceipt, opts?: { rpcUrl?: string; }): Promise; /** * Verify the OPTIONAL Tier-2 service-delivery attestation on a {@link PipRailReceipt} * — the merchant's signed proof that the resource was actually SERVED (the one thing * the chain can't attest). For an EVM EIP-712 attestation this re-recovers the signer * from the signature over the official `offer-receipt` typed data and checks * `recover === receipt.payTo` (spec §4.5.1 / §5.5) — the classic EIP-712 footgun made * safe (`recoverTypedDataAddress` returns a WRONG address rather than throwing on a bad * signature, so the equality check is the real verification). A tampered signature → * `{ ok:false }`, never a throw. * * Static + wallet-free. **Never throws** (a malformed/absent attestation, an unsupported * format, or a recovery fault → `{ ok:false, reason }`). Viem-free HERE — the recover runs * inside the lazily-imported EVM receipt driver (a lazy chunk), so the protocol layer pulls * no chain libs. The JWS format defers to R3 (`{ ok:false, reason:'jws-not-loaded' }`). */ static verifyAttestation(receipt: PipRailReceipt): Promise<{ ok: boolean; signer?: string; reason?: string; }>; /** Auto-mount the chain's driver, resolve the network, and bind the wallet — once. */ private ensure; /** Resolve the effective scheme set: a per-call override, else the constructor's * `schemes`, else the `onchain-proof`-only default. */ private resolveSchemes; /** GET that auto-handles 402. Pass a full URL to any x402-gated endpoint. */ get(url: string, init?: RequestInit): Promise; /** * POST that auto-handles 402. * * `body` can be a string/FormData/URLSearchParams/ArrayBuffer/Blob (sent * as-is) or a plain object (serialised as JSON). */ post(url: string, body?: BodyInit | object | undefined, init?: RequestInit): Promise; /** * Price a gated URL WITHOUT paying. Does the initial request, and if it's a * 402, returns a {@link PipRailQuote} — the amount (in the token's TRUE * decimals), token, chain, recipient, and whether the configured `policy` * would allow it. Returns `null` when the URL isn't payment-gated (no 402). * * This is what lets an agent (or its planner) decide *before* spending — * "0.05 USDC on Base, within budget → pay it." No funds move. */ quote(url: string, init?: RequestInit): Promise; /** * Estimate the network fee (gas) to pay a gated URL — WITHOUT paying. Returns * the {@link PipRailQuote} (what the payment is) plus a {@link CostEstimate} * (the gas it will burn, in the chain's NATIVE coin), so an agent can budget * the *total* — payment + gas — before any funds move. Returns `null` when the * URL isn't payment-gated (no 402). * * The estimate is best-effort and labelled (`cost.basis`): live-RPC ('estimated') * where cheap (EVM gas price, XRPL fee), a typical-cost constant ('heuristic') * otherwise. It never throws for a transient RPC issue. Gas is in the native * coin (ETH/SOL/TON/XLM/XRP/TRX), distinct from the payment token — most useful * on Tron, where a USD₮ transfer can cost real TRX. */ estimateCost(url: string, init?: RequestInit): Promise; /** Aggregated snapshot of every payment this client has settled — total * count, cumulative spend per token, cumulative spend per denomination (the * cross-token grand total), and the individual records. */ spent(): SpendSummary; /** The CONFIGURED spend policy, read back unchanged — so an agent can self-check * its WHOLE leash (caps, allowlists, time, denom + count limits) without hitting a * decline. `undefined` when no policy is set. Pure; never throws. */ policy(): PaymentPolicy | undefined; /** * Read-only budget + time leash for a Mode-A (headless) agent — the policy IS * the consent, and this is how the agent SEES what's left of it before paying. * Composes the ledger with the configured policy: the per-asset money leash * (`byAsset`), the cross-token GRAND TOTAL per denomination (`byDenom`, present * from the start), and the payment-COUNT leash (`counts`). Never throws, moves no * funds. The money/count figures persist if a `spendStore` is set; the time * envelope is process-scoped (see {@link SessionBudget}). */ budget(): SessionBudget; /** * The cross-token GRAND-TOTAL leash — one row per denomination capped by * `policy.maxTotalPerDenom`. Unlike `remaining()`, rows exist from the START * (the cap is a single declared number, so headroom is previewable before any * spend). `[]` when no `maxTotalPerDenom` is set. Pure; never throws; never a * price-converted figure (tokens grouped as one unit, each 1:1). */ denomRemaining(): DenomRemaining[]; /** The payment-COUNT leash — settled count so far + the configured lifetime/window * caps and what's left. Pure; never throws. */ countStatus(): CountStatus; /** * Per-(network, asset) remaining budget — ONE row per pair the ledger already * holds (decimals are known only after the first spend), so a fresh client with * a `maxTotal` set returns `[]` until its first payment. `cap`/`remaining` are * `undefined` when no `maxTotal` is configured (unbounded). Pure + in-memory; * never throws, never sums across tokens (no price oracle). PROCESS-SCOPED. */ remaining(): SpendRemaining[]; /** The read-only TIME envelope for the plan/budget surfaces, or `undefined` * when no session deadline (`ttlSeconds`/`expiresAt`) is set. `secondsRemaining` * is clamped ≥ 0 — a best-effort host wall-clock estimate. */ private sessionView; /** * Plan a payment for a gated URL — WITHOUT paying. The read-only completion of * the `quote()` → `estimateCost()` → **`planPayment()`** trio: it surveys every * rail the 402 offers on this client's chain against the wallet's OWN holdings — * token balance, native-coin gas, and recipient-readiness (trustline / ATA / * storage_deposit / ASA opt-in) — and returns, crystal-clear: * - `payable` + `best` — the cheapest rail the wallet can actually settle * - `options[]` — every rail with typed `blockers` + soft `warnings` * - `fundingHint` — one human sentence on exactly what to top up * * NEVER throws for a read problem (a transient/RPC failure surfaces as a rail in * `state: 'unknown'` + a warning, never a false "unaffordable"); returns `null` * when the URL isn't payment-gated (no 402); and when the 402 offers no rail on * this client's chain it EXPLAINS that (status `blocked` + a hint), rather than * throwing. Throws `InvalidEnvelopeError` only on an unparseable challenge. * * Then pay the chosen rail with `fetch(url, { autoRoute: true })`, or branch on * the plan yourself. No funds move. */ planPayment(url: string, init?: RequestInit): Promise; /** * Convenience over {@link planPayment}: can the wallet settle this URL right now? * `true` when at least one rail is payable — or when the URL isn't gated (a free * resource is trivially "affordable"). No funds move. */ canAfford(url: string, init?: RequestInit): Promise; /** * Find payable resources on the OPEN x402 indexes — WITHOUT paying. Reads the * free indexes (CDP Bazaar + 402 Index by default), merges + dedupes them, and * by default returns only resources payable on THIS client's chain * (`network: 'self'`). Each result carries its advertised `rails[]`; feed a * chosen `resource` straight into `quote()` → `planPayment()` → `fetch()`. * * Nothing PipRail-hosted: these are third-party open directories. Never throws * for a read problem — an index that's down or changed simply contributes * nothing. Honest caveats (see {@link DIRECTORY_INFO}): * - Reads **`bazaar` + `402index`** only — **NOT `x402scan`** (its reads are paid). A * resource you registered on x402scan is live there but will NOT appear here; don't * read that absence as failure. (Passing `sources:['x402scan']` explicitly yields `[]`.) * - A resource just listed via {@link register} may not appear yet — 402 Index reviews * before publishing, so retry with a brief backoff if a fresh listing is missing. * - Results are cross-scheme (mostly the mainstream `exact` scheme); `fetch()` pays * `onchain-proof` rails by default, and standard `exact` rails too once you opt in * with `schemes: ['onchain-proof', 'exact']` (EVM EIP-3009/Permit2 + Solana SVM + Algorand * + Aptos + NEAR). */ discover(opts?: DiscoverOptions): Promise; /** * List a resource you run on the OPEN x402 registries, so agents can find it. * Default target is **402 Index** — one POST, no auth, no signature, no payment. * Add `'x402scan'` to also register via SIWX (one wallet signature; EVM + a * Base/Solana rail). Returns one {@link RegisterOutcome} per target — a target the * chain can't satisfy comes back `{ ok:false, detail }`, never a throw. An explicit, * developer-invoked action; it moves no funds, and nothing is PipRail-hosted — * you're listing on third-party open directories. * * **Listing is asynchronous — each outcome carries a `visibility` + `note` so an * agent knows when/where the resource is findable (don't assume `ok:true` means * "searchable now"):** * - **402 Index** → `visibility:'pending-review'`. It probes your URL on submit, then lists it * PENDING REVIEW — not searchable until approved (verify your domain on 402index.io for instant * approval), so `discover()` returns nothing for a fresh listing until then. Retry later. * - **x402scan** → `visibility:'live'`, but **`discover()` does NOT read x402scan** — the * listing is real on x402scan.com yet won't show up in `discover()`. Base/Solana only; * needs a resolvable input schema (`/openapi.json` or the `extensions.bazaar` block). * - **Bazaar** → `visibility:'not-listable'` for PipRail (it lists only what its facilitator * settles; PipRail uses none). You can still READ Bazaar via {@link discover} to find others. * * The per-source facts live in {@link DIRECTORY_INFO} (importable) if you'd rather branch * on them before calling. */ register(url: string, opts?: RegisterOptions): Promise; /** * **402 Index domain verification, step 1 of 2.** A self-registered 402 Index * listing is `pending-review` (see {@link register}); verifying your domain flips * it — and every other pending listing on that domain — to APPROVED/searchable. * Pass the resource URL or a bare domain; returns the `verificationHash` to serve * as the entire body of `verificationUrl` (your `/.well-known/402index-verify.txt`). * Then serve it and call {@link verifyDomain}. Moves no funds; never throws. * * ```ts * const claim = await client.claimDomain('https://api.example.com/report') * // serve claim.verificationHash at claim.verificationUrl, then: * const res = await client.verifyDomain('api.example.com') // → { ok:true, status:'verified' } * ``` */ claimDomain(urlOrDomain: string, opts?: { contactEmail?: string; }): Promise; /** * **402 Index domain verification, step 2 of 2.** After {@link claimDomain} and * serving the hash at your `/.well-known/402index-verify.txt`, this tells 402 Index * to re-fetch + approve. On success the domain's pending listings become searchable * (`{ ok:true, status:'verified', servicesCount }`). Moves no funds; never throws. */ verifyDomain(urlOrDomain: string): Promise; /** * The discovery signer for the bound wallet (its address + a message signer), * or `null` if the chain family doesn't support it (EVM does today). For * discovery only — ownership proofs (sign the bare origin string and pass it to * `buildOpenApi({ ownershipProofs })`) and SIWX registration. Never signs a * payment. */ discoverySigner(): Promise; /** * Lower-level: drive any HTTP method through the 402 flow. * * `init.body` (if any) must be replayable — the SDK may send the request * twice (once to fetch the 402, once with the proof attached). One-shot * streams throw `NonReplayableBodyError`. */ fetch(url: string, init?: RequestInit & { autoRoute?: boolean; schemes?: PaymentScheme[]; }): Promise; /** * From a confirmed-402 response: parse the challenge, mount + bind the * network, pick the accept the client can pay, and build its quote. Shared by * `quote()` (read-only) and `fetch()` (which then authorises + pays). */ private resolveChallenge; /** Match a foreign-supplied network string against the bound driver, tolerating a * SLUG ('bsc', 'base', '56') the SAME way discovery's `railOnNetwork` already does — * normalize to CAIP-2 first, since a foreign/AEON/community 402 may label the network * with a slug (AEON serves v1 duplicate kinds '56'/'bsc'). ADDITIVE: a value that's * already CAIP-2 passes through `normalizeNetwork` UNCHANGED, so every existing * exact-CAIP-2 match is byte-identical; only slugs resolving to the bound chain become * newly matchable (an unknown slug stays unresolved → still unmatched; a different * chain's slug resolves elsewhere → still unmatched). */ private supportsNetwork; /** The candidate accepts this client could pay, on the bound network. Always the * backendless `onchain-proof` rails; PLUS standard `exact` rails when `schemes` * enables them AND the driver can settle them (EVM `payExact` + a recognised * EIP-3009 token). `onchain-proof` is gathered FIRST so default selection is * unchanged when `exact` is off. */ private gatherCandidates; /** Build the full {@link PaymentPlan} from an already-parsed challenge + bound * net/wallet. Shared by `planPayment` (read-only) and `fetch`'s autoRoute. */ private planFromChallenge; /** Analyse ONE rail against the wallet's holdings — quote (existing) + gas * (estimateCost, existing) + balanceOf + recipientReady → a {@link PayOption}. */ private analyzeRail; /** Build the agent-facing quote for an accept: TRUE decimals/symbol (via the * driver's describeAsset) + the policy verdict + a symbol-mismatch flag. */ private buildQuote; /** Enforce the spend policy and the onBeforePay hook — both refuse by * throwing PaymentDeclinedError, before any funds move. Every refusal carries * a typed `reasonCode` so an agent can branch on the cause (and spot a * TERMINAL expiry/approval decline it must not retry) without parsing prose. */ private authorize; /** * Refuse a payment BEFORE any send: emit BOTH the legacy `payment-failed` (so existing * `onEvent` consumers are unaffected) AND the richer, dedicated `payment-declined` * (typed reasonCode + fine PolicyDenyCode + the quote + a budget snapshot), then throw * the typed {@link PaymentDeclinedError}. Returns `never` so callers' control flow is * exhaustive. */ private refuse; /** Record a settled payment in the ledger (TRUE decimals for the running total + the * denomination it counts toward in the grand total). Then fire the `onSpend` callback * with the record + the post-payment budget, and emit any `warnAtFraction` thresholds * this payment just crossed. All observability is isolated — a throwing hook never * affects the (already-settled) payment. * * `settledAmountBase` is the SINGLE upto ledger-reconciliation seam: the quote (and thus * the policy/budget) gates on the MAX, and for the metered `upto` rail the budgeted amount * RECORDED is ALSO the authorized MAX — the only buyer-provable bound. The merchant's * claimed actual is UNTRUSTED (a malicious merchant can settle the MAX on-chain yet report * a tiny `SettleOutcome.amount`); recording it would let an under-report silently loosen a * cumulative cap (`maxTotal`/`maxTotalPerDenom`/`windowTotal`) past the buyer's real on-chain * spend (POL-1). So the cap-bearing `amountBase` is the MAX; the clamped actual is surfaced * separately on `settledBase`/`settledFormatted` for transparency (it equals the receipt's * amount). When absent (onchain-proof/exact) this is byte-identical to before. */ private recordSpend; /** * Emit a `budget-threshold` event for each cap whose used-fraction just reached * `policy.warnAtFraction` — the early warning before a hard decline. Fires ONCE per * crossing per cap (deduped on the shared ledger via `markWarned`, so a cross-chain * threshold fires once for the whole budget, not once per chain). No-op when no * `warnAtFraction` is set. Reads the just-computed {@link SessionBudget}; isolated (safeEmit). */ private emitThresholds; private payAndConfirm; private retryWithProof; /** * Pay a standard x402 `exact` rail — a SEPARATE, fundamentally more conservative * path than {@link retryWithProof}. The buyer SIGNS an EIP-3009 authorization ONCE * (the driver's `payExact`) and the server / merchant-chosen facilitator BROADCASTS * it synchronously, so a blind re-POST of a still-in-flight authorization could * double-BROADCAST it. Hence, unlike the onchain-proof loop: * * • sign exactly once — reuse the SAME header on every retry, never re-sign; * • retry ONLY an explicit 402 (a definitive pre-broadcast rejection), bounded * well under `maxTimeoutSeconds` so the loop can't outlive the authorization; * • a post-POST transport error/timeout → {@link PaymentTimeoutError} carrying the * nonce (the facilitator MAY have settled — verify on-chain, NEVER re-pay); * • a 5xx → return as-is (server settle failure; the authorization stays valid + * its nonce unused) — no settled event, no spend; * • a 200 whose SettleResponse says `success:false` → a rejection, NEVER a spend; * • the spend is recorded EXACTLY ONCE, on an affirmative settlement only. */ private payExactRail; /** * The standard `upto` (metered) buyer path — a near-clone of {@link payExactRail} with TWO * deltas: (1) it signs a Permit2-upto authorization for the MAX via `payUpto` + frames it * with `buildUptoSignatureHeader`; (2) it records the ACTUAL settled amount (read off the * SettleResponse's required `amount` field, via `recordSpend(quote, ref, settle.amount)`) in * the ledger — the budget gated on the MAX, the ledger records the ACTUAL. A server that omits * `settle.amount` FAILS SAFE to the MAX (over-counts, never under-counts). The buyer SIGNS, the * merchant self-settles — the buyer never broadcasts. */ private payUptoRail; } /** * Plan a payment ACROSS several single-chain clients — the cross-chain brain. * A {@link PipRailClient} is bound to one chain (its wallet); give this one client * per chain the agent funds and it runs each client's {@link PipRailClient.planPayment} * in parallel and merges the rails into one plan, ranked payable-first. `best` is a * payable rail. Across different native coins there's no oracle to compare gas costs, * so it does NOT rank chains by fee against each other: `best` is the FIRST chain you * pass in `clients` that can settle (your preference order); within a single chain it * still prefers the cheapest-gas rail. Returns `null` only if the URL isn't gated for * any client. Throws only if EVERY client fails to reach the resource (a total outage), * mirroring a single client — a single chain being down just drops that chain. */ declare function planAcross(clients: PipRailClient[], url: string, init?: RequestInit): Promise; /** * PAY across several single-chain clients — the EXECUTION counterpart to * {@link planAcross}. Plans the URL on every client in parallel (keeping which * client owns which rail), picks the rail `planAcross` names as `best` (the first * funded chain you listed that can settle RIGHT NOW), and pays it on its owning * client. So an agent that holds one wallet * per chain pays whichever chain/token the merchant's 402 asks for — with no * manual routing — while every payment still goes through that client's own * spend policy, `onBeforePay` hook, retries, and replay-protection (this just * calls the chosen client's {@link PipRailClient.fetch}). * * - A URL that needs no payment (no 402) is returned straight through. * - When NO funded chain can settle it, throws {@link PaymentDeclinedError} with a * merged, per-chain funding hint — BEFORE any on-chain send. * * Selection matches {@link planAcross}: payable-first, and across different native * coins (no price oracle) the FIRST chain you pass in `clients` that can settle wins * (your preference); within a chain, the cheapest-gas rail. It normally pays the rail * `planAcross` reports as `best`, but on a BEST-EFFORT basis — the owning client * re-reads its balances/gas at pay time, so a change between planning and paying (a * concurrent payment, RPC drift, the merchant returning a different 402) can make it * pick another settleable rail ON THE SAME CHAIN, or decline; its spend policy + * `onBeforePay` still gate whatever is actually paid. For the ergonomic object form, * see {@link MultiChainPayer}. * * NOTE: this PROBES the URL with the caller's `init` (method + body) on each client to * read the 402, so prefer it for GET / idempotent requests — a non-idempotent POST is * sent once per client before the pay leg (the x402 gate returns 402 without acting, * but the body is re-sent). */ declare function fetchAcross(clients: PipRailClient[], url: string, init?: RequestInit): Promise; /** * MultiChainPayer — one buyer, many wallets, pay whatever the merchant asks. * * A {@link PipRailClient} is bound to exactly ONE chain and ONE wallet (an EVM key * can't sign a Solana tx, and vice-versa — that's enforced at bind time). So a * buyer who wants to pay a 402 *whatever chain/token it demands* holds one key per * chain. This is the ergonomic object that carries that bundle: give it a * `{ chain → wallet }` map and it builds one client per chain, then exposes a single * `fetch`/`get`/`post`/`plan`/`quote` that auto-routes to the first funded chain that * can settle — no manual "which client owns this rail?" plumbing. * * It is a thin, chain-agnostic composition over the existing primitives — it adds * NO new payment logic: * - `planPayment` → {@link planAcross} (merge every chain's plan, payable-first) * - `fetch`/`get`/`post` → {@link fetchAcross} (pay on the first chain that can settle) * Every payment still runs through its owning client's own spend policy, * `onBeforePay` hook, retries, and replay-protection. There is no cross-chain * custody, no price oracle, and no backend: across chains it pays the FIRST one you * list that can settle (your preference order — gas isn't comparable across coins); * within a chain it picks the cheapest-gas rail. * * Because it implements {@link PayingClient}, the agent toolkit ({@link paymentTools}) * and the MCP server wrap it byte-identically to a single client. */ /** * One wallet per chain you fund, keyed by chain selector. The KEY is a chain * string (an EVM preset like `'base'`/`'bnb'`, or a non-EVM family * `'solana'|'ton'|'tron'|'near'|'sui'|'aptos'|'algorand'|'stellar'|'xrpl'`); the * VALUE is that family's {@link WalletInput}: * * every chain → { key } · near → { accountId, key } * (`key` is that chain's secret string — see {@link WalletInput} for the per-chain format) * * One key per family — this map is how a single buyer carries the keys for every * chain it's willing to pay on. (For a CUSTOM EVM chain configured by a viem * `Chain` object, build the {@link PipRailClient} yourself and use * `new MultiChainPayer([...clients])`.) */ interface MultiChainPayerOptions { /** `{ chain → wallet }`. Iteration order is your chain PREFERENCE: across chains the * first one that can settle wins (there's no oracle to compare gas across coins). */ wallets: Record; /** Spend policy applied to EVERY chain's client. Because `fromWallets` gives every * client a SHARED ledger, the cross-token grand total (`maxTotalPerDenom`) and the * payment-count caps (`maxPayments`/`maxPaymentsPerWindow`) span ALL chains as ONE * budget — so `{ maxTotalPerDenom: { USD: '20' } }` means "$20 across every chain", * not $20 per chain. The per-asset `maxTotal` stays per (network, asset). */ policy?: PaymentPolicy; /** Durable spend store for the SHARED ledger — make the whole cross-chain budget * survive a restart (hydrates once, persists every settle). One file backs every * chain. Use `fileSpendStore('./spend.jsonl')` from `@piprail/sdk/node`. */ spendStore?: SpendStore; /** Per-chain RPC overrides, keyed by the same chain selector as `wallets`. */ rpcUrls?: Record; /** Which schemes every client may settle. Default `['onchain-proof']` (unchanged). */ schemes?: PaymentScheme[]; /** Final approval hook applied to every chain's client (fires before any send). */ onBeforePay?: (quote: PipRailQuote) => boolean | Promise; /** Observability hook applied to every chain's client. */ onEvent?: (event: PipRailEvent) => void; /** Retry budget for the post-broadcast leg, per client. Default 3. */ maxPaymentRetries?: number; /** Timeout (ms) for the retry leg, per client. Default 30_000. */ retryTimeoutMs?: number; } declare class MultiChainPayer implements PayingClient { private readonly _clients; /** True when every client shares ONE ledger (the `fromWallets` path) — so spend + * budget are read from that single source, never concatenated (which would * double-count a shared ledger). */ private readonly _shared; /** * Wrap an explicit, ordered set of single-chain clients — use this when a client * needs full control (e.g. a custom EVM chain configured by a viem `Chain`). The * ORDER is your chain preference: across chains the first that can settle wins. Pass * at MOST one client per chain — two clients on the SAME network would double-count in * `spent()`/`budget()` and waste a plan round-trip (`fromWallets` can't produce this). * For the common case, prefer {@link MultiChainPayer.fromWallets}. * * For a cross-chain grand total / count cap via this path, build the clients with a * SHARED `ledger` (`new SpendLedger(store?)` passed to each client's `ledger` option) * and set `opts.shared = true` so reads come from that one ledger. `fromWallets` does * this for you. */ constructor(clients: PipRailClient[], opts?: { shared?: boolean; }); /** * Build one client per funded chain from a `{ chain → wallet }` map — the * ergonomic path. The shared `policy`/`schemes`/`onBeforePay`/`onEvent` apply to * every client; `rpcUrls` are matched per chain. Iteration order of `wallets` is * the chain preference. * * ```ts * const payer = MultiChainPayer.fromWallets({ * wallets: { * base: { key: process.env.EVM_KEY! }, * solana: { key: process.env.SOLANA_SECRET! }, * xrpl: { key: process.env.XRPL_SEED! }, * }, * // ONE budget across all three chains: $20 total + at most 100 payments. * policy: { maxAmount: '1.00', maxTotalPerDenom: { USD: '20.00' }, maxPayments: 100 }, * }) * const res = await payer.get('https://api.example.com/paid') // pays on the first funded chain that can settle * ``` * * Every client shares ONE {@link SpendLedger} (optionally backed by `spendStore`), so * the cross-token grand total + the count caps span all chains as a single budget. */ static fromWallets(opts: MultiChainPayerOptions): MultiChainPayer; /** The underlying single-chain clients, in preference order. Reach for one of * these for chain-specific reads (`estimateCost`, `discoverySigner`, per-chain * `budget()`) that don't make sense merged. */ get clients(): readonly PipRailClient[]; /** Plan a 402 across every funded chain — merged + ranked payable-first. `null` * when the URL needs no payment. (Delegates to {@link planAcross}.) */ planPayment(url: string, init?: RequestInit): Promise; /** Can ANY funded chain settle this URL right now? (A free resource is trivially * "affordable".) No funds move. */ canAfford(url: string, init?: RequestInit): Promise; /** Price a gated URL across funded chains — the chosen rail's quote (the first * funded chain that can settle), else the first offered rail's. `null` when the URL * needs no payment. When it IS * gated but none of your chains are offered, surfaces the same informative * `NoCompatibleAcceptError` a single client would (it names the chains the 402 is * payable on) rather than a misleading `null`. No funds move. */ quote(url: string, init?: RequestInit): Promise; /** Pay the first funded chain (in your listed order) that can settle this URL. * Delegates to {@link fetchAcross} — full policy / approval / retry / replay path on * the owning client. The owner re-reads balances at pay time, so the rail paid is the * surfaced `best` on a best-effort basis (it can pick another rail on the SAME chain, * or decline, if balances shift between plan and pay). PROBES the URL with `init` * (method + body) per client — prefer GET / idempotent requests. */ fetch(url: string, init?: RequestInit): Promise; /** GET that auto-pays across chains. */ get(url: string, init?: RequestInit): Promise; /** * POST that auto-pays across chains. `body` is a string/FormData/URLSearchParams/ * ArrayBuffer/Blob (sent as-is) or a plain object (serialised as JSON) — mirrors * {@link PipRailClient.post}. */ post(url: string, body?: BodyInit | object | undefined, init?: RequestInit): Promise; /** * Find payable resources across every funded chain. With the default * `network: 'self'`, each chain's own results are merged + deduped by URL (so * "self" means "any chain I can pay"). A network-scoped query (a CAIP-2 id or * `'any'`) is chain-independent, so one client answers it. Never throws for a * read problem; moves no funds. */ discover(opts?: DiscoverOptions): Promise; /** List a resource YOU run on the open indexes. Registration is a merchant action * independent of which chain you pay FROM, so it goes through your first chain's * client; pass `opts.network` to advertise a specific chain. Moves no funds. */ register(url: string, opts?: RegisterOptions): Promise; /** * Aggregate spend across every chain. With a SHARED ledger (`fromWallets`), this reads * that single ledger ONCE (its records already span every chain) — so the count and the * cross-token `byDenom` grand total are correct and never double-counted. With * independent clients (the explicit constructor), it concatenates: counts summed, * per-(network,asset) `byAsset` + `byDenom` + records concatenated. */ spent(): SpendSummary; /** * A merged budget view. With a SHARED ledger (`fromWallets`) it's the single source of * truth — `byAsset`/`byDenom`/`counts` already span every chain, so the grand-total and * count leashes read as ONE budget. With independent clients it merges: per-(network, * asset) rows + per-denom rows concatenated, counts summed, and the MOST-RESTRICTIVE * session time envelope (the soonest deadline wins). Per-chain detail is on each * `clients[i].budget()`. */ budget(): SessionBudget; /** The CONFIGURED spend policy (the shared policy `fromWallets` applies to every chain; * the first client's for the explicit constructor). `undefined` when none is set. */ policy(): PaymentPolicy | undefined; } /** * MCP-style tool annotations — optional, advisory hints that let an MCP client or * agent reason about a tool's *nature* (is it safe to call freely? does it move * value?). They mirror the MCP spec's `ToolAnnotations`. NOTE: hints only — a * client must never make a security decision solely on these; the spend policy is * the real boundary. */ interface ToolAnnotations { /** Human-friendly title for the tool. */ title?: string; /** True when the tool only READS — no state change, no funds moved. */ readOnlyHint?: boolean; /** True when the tool may move value or do something not easily undone (only meaningful when not read-only). */ destructiveHint?: boolean; /** True when calling repeatedly with the same args has no additional effect. */ idempotentHint?: boolean; /** True when the tool reaches the open world — external indexes, chains, or arbitrary URLs. */ openWorldHint?: boolean; } /** A framework-agnostic tool definition an agent runtime can register. */ interface AgentTool { /** Unique tool name (snake_case, namespaced `piprail_…`). */ name: string; /** What the tool does — written for an LLM to read. */ description: string; /** JSON Schema (draft-07 object) describing the arguments. */ parameters: Record; /** Advisory MCP-style hints about the tool's nature (read-only, value-moving, …). */ annotations?: ToolAnnotations; /** Optional JSON Schema (draft-07 object) for the tool's RESULT — declared only * on stable read-only tools so a strict client can validate `structuredContent`. * Kept OPEN (no `additionalProperties:false`) so additive fields never break it. */ outputSchema?: Record; /** Execute the tool. Returns a JSON-serialisable result. */ invoke: (args: Record) => Promise; } /** * Eight tools wrapping a configured {@link PipRailClient}: * - `piprail_discover(query?)` — FIND payable resources on the open x402 * indexes, WITHOUT paying (the phone book — solves "what can I buy?"). * - `piprail_quote_payment(url)` — price a gated URL WITHOUT paying. * - `piprail_plan_payment(url)` — check you CAN pay (balance + gas + recipient * readiness) across every rail the URL offers, WITHOUT paying. * - `piprail_pay_request(url, method?, body?)` — pay if needed and return the result. * - `piprail_register(url, …)` — LIST a resource you run on the open indexes so * other agents can find it (402 Index, no signature). * - `piprail_budget()` — read the remaining spend budget + time leash (Mode A self-check). * - `piprail_guide()` — read the agent contract (how to quote/plan/pay + read a refusal). * - `piprail_verify_receipt(receipt, rpcUrl?)` — re-verify a verifiable receipt against the * chain WITHOUT a wallet (anyone-verifiable; never throws — returns a structured verdict). * * The first five are byte-identical in name + order to before; the three read-only * tools are appended LAST. EVERY failure the pay tool sees comes back as a * STRUCTURED object (`{ ok:false, code, reason, explain, ref?, reasonCode?, * declined? }`) — never a thrown error — so the model reasons about it (and never * re-pays a broadcast-but-unconfirmed payment) instead of crashing. */ declare function paymentTools(client: PayingClient): AgentTool[]; /** * One line summarising a {@link PaymentPlan} for a model: what's payable, on which * chain, the gas, and how many other rails aren't settleable. `null` (the URL * isn't gated) → "no payment required". */ declare function summarizePlan(plan: PaymentPlan | null): string; /** * One line a model can act on for any failure. For a {@link PipRailError} it * switches on the stable `.code`; the broadcast-but-unconfirmed codes * (`PAYMENT_TIMEOUT`, `MAX_RETRIES_EXCEEDED`, `CONFIRMATION_TIMEOUT`) carry the * load-bearing recovery rule — **recover via `.ref`, never re-pay** (a fresh * payment would double-spend). A non-PipRailError → `'Payment failed: '`. */ declare function explainDecline(err: unknown): string; /** * One line summarising spend so far: a per-(network, asset) breakdown, plus — when the * caller grouped tokens into a denomination (`maxTotalPerDenom`) — the cross-token GRAND * TOTAL per unit (e.g. "$12.34 USD total"). The grand total is a sum of tokens declared as * one unit, each 1:1 — NEVER a price-converted figure (no oracle). Count 0 → "no payments yet". * * NOTE: spend totals are in-memory unless a `spendStore` is configured (then they survive * a restart); without one they reset per process — a convenience, not a durable ledger. */ declare function formatSpendReport(summary: SpendSummary): string; /** * One line a human or model can act on for a 402 challenge: what it is, the first * rail's amount/token/chain + recipient, and how to pay it programmatically. Composes * only shipped challenge fields (same `amountFormatted ?? amount` / `symbol ?? asset` * convention as the renderers above) — pure, no I/O. Used as the human `instruction` * inside the self-describe block and as a landing page's headline. An empty `accepts` * (shouldn't happen for a valid v2 challenge) degrades to a generic pointer. */ declare function describeChallenge(challenge: X402Challenge): string; /** * The PipRail agent contract, distilled into one string an LLM can read once and * use the tools correctly with near-zero other docs. PURE — a static constant, no * imports, no I/O. Exposed to MCP clients as a prompt + resource, and reachable * from the tool layer so a headless (non-MCP) agent can prepend it to its system * prompt. * * Keep it tight, concrete, and tool-name-accurate — an agent will trust it * literally, so a wrong name or order actively misleads. A test pins the load- * bearing phrases. */ declare const PIPRAIL_AGENT_GUIDE = "# Paying with PipRail \u2014 the agent contract\n\nYou can pay for x402 \"402 Payment Required\" resources autonomously. Money moves\nstraight from your wallet to the server; PipRail custodies nothing. Follow this.\n\n## Landing cold \u2014 read the self-description\nEvery PipRail 402 self-describes. Read challenge.extensions.piprail for { name, what, pay[]\n(each rail's how-to-pay), sdk.install, mcp, docs } \u2014 never guess what an endpoint is. If your\ntooling can't pay a rail (e.g. a stock x402 client can't pay the onchain-proof scheme), the\nblock says how: install @piprail/sdk (npm i @piprail/sdk) or run the MCP (npx -y @piprail/mcp)\nand pay with the tools below.\n\n## The loop: quote \u2192 plan \u2192 pay\n1. piprail_quote_payment(url) \u2014 PRICE it. Returns the amount, token, chain, and\n whether it is within your spend policy. No funds move. Use it to decide if a\n resource is worth buying.\n2. piprail_plan_payment(url) \u2014 can I afford it NOW? Reads your balance, native gas,\n and recipient-readiness across every rail, and returns { payable, best,\n fundingHint, session? }. If payable is false, do NOT attempt the payment \u2014\n fundingHint says exactly what to fix.\n3. piprail_pay_request(url, method?, body?) \u2014 PAY (only if the plan was payable)\n and return the result.\nAlways plan before you pay so you never commit to a payment you cannot finish.\n\n## Gasless \u2014 the exact rail (zero gas for you)\nA 402 may offer up to three rails; you don't choose per payment \u2014 the client does, automatically:\n- onchain-proof (PipRail's default): you broadcast the payment yourself and pay the network gas\n (the native coin \u2014 ETH/SOL/\u2026). Works on every chain.\n- exact (the ratified x402 rail, opt-in): you only SIGN; the server \u2014 or a facilitator it chose\n (e.g. PayAI) \u2014 broadcasts it, so you pay ZERO gas (you need only the token, no native coin). It\n works on EVM, Solana, Algorand, Aptos + NEAR, and the on-chain method (EIP-3009 / Permit2 / SVM /\n Algorand fee-pooled group / Aptos fee-payer / NEAR SignedDelegateAction) is picked automatically.\n- upto (the metered/variable x402 rail, opt-in, EVM): the amount you see is a MAXIMUM \u2014 you sign\n a ceiling, the server meters real usage and settles the ACTUAL (<= the max). BUDGET AGAINST THE MAX:\n the plan/policy treat the ceiling as the spend (a server may charge up to it), so a payable plan\n means the MAX fits your budget; the settled actual is recorded for reconciliation.\nWhen the exact scheme is enabled AND balance-aware routing is on, paying picks the cheapest\nsettleable rail \u2014 i.e. the gasless exact one. Nothing changes in your loop: quote \u2192 plan \u2192 pay is\nidentical. The exact/upto schemes are OPT-IN by the operator (MCP: PIPRAIL_SCHEMES=onchain-proof,exact,upto);\nyou can't enable them yourself, but you can report when a 402 needs one (see UNSUPPORTED_SCHEME below).\n\n## Reading a refusal \u2014 never crash, never double-spend\nA failed pay returns a STRUCTURED object, never a thrown error you must catch:\n { ok:false, code, reason, explain, ref?, reasonCode?, declined? }\nBranch on `code` (always reliable). Key cases:\n- declined:true with reasonCode:'SESSION_EXPIRED' \u2014 your time budget is over. This\n is TERMINAL: STOP. Do not retry ANY payment this process; it cannot be undone\n without a restart / a longer TTL.\n- declined:true with reasonCode:'APPROVAL' \u2014 a human (or hook) declined this\n payment. Terminal for this pay: do NOT auto-retry \u2014 they said no, or no one\n answered.\n- declined:true with reasonCode:'OUTSIDE_WINDOW' \u2014 your rolling rate-limit is\n exhausted. Wait for it to free, then retry; do not raise the amount.\n- declined:true with reasonCode:'POLICY' or 'BUDGET' \u2014 a spend cap or allowlist\n refused it. Don't retry the same payment; pick a cheaper/allowed one.\n- code:'INSUFFICIENT_FUNDS' \u2014 top up the wallet (token and/or native gas), retry.\n- code:'PAYMENT_TIMEOUT' / 'MAX_RETRIES_EXCEEDED' / 'CONFIRMATION_TIMEOUT' \u2014 the\n payment may ALREADY be on-chain. Recover using the proof on `.ref` (re-verify\n or re-submit it); never re-pay \u2014 a fresh payment would double-spend. On a gasless\n exact rail `.ref` is the authorization NONCE, not a tx hash: re-present the SAME\n signed authorization, never sign a fresh one (that would risk a double-spend).\n- code:'NO_COMPATIBLE_ACCEPT' / 'UNSUPPORTED_SCHEME' \u2014 the 402 isn't payable on\n your chain/scheme; `explain` says whether it's the wrong chain or a scheme to enable.\n If it's a standard x402 server offering an exact rail, that's a config fix the operator makes\n once (enable the exact scheme); report it, don't retry the same call blindly.\n\n## Knowing your leash \u2014 call piprail_budget\npiprail_budget tells you how much budget and time you have left: per (network,\nasset) remaining, the cross-token GRAND TOTAL per denomination (e.g. how much\nUSD you can still spend across every stablecoin and chain), the payment-count\nleash, the session time envelope, your spend so far, and the configured policy\nread back. Read-only; moves no funds. Use it in Mode A to self-check before paying.\n\n## Two modes\n- Mode A (headless, default): you run FREE inside a pre-set budget + time\n envelope. The policy IS the consent \u2014 there is no per-payment prompt. Stay\n inside it; piprail_budget shows what's left.\n- Mode B (supervised): the host may ask a human to approve each payment. A\n decline/cancel/timeout comes back as declined:true (reasonCode:'APPROVAL') \u2014\n do NOT retry it as if it were a transient error.\n\n## Hard facts\n- Per-payment + per-(network, asset) caps always apply. A cross-token GRAND TOTAL per\n denomination (maxTotalPerDenom, e.g. \"$20 across every USD stablecoin + chain\") is\n OPTIONAL \u2014 it sums tokens declared as one unit, each 1:1; it is NOT a price oracle and\n never prices a volatile native coin. Payment-COUNT caps (maxPayments / per-window) also\n span every chain + token.\n- The time envelope lives IN-MEMORY for THIS process (resets on restart). The money + count\n totals also reset on restart UNLESS a durable spend store is configured \u2014 then they resume.\n- A refusal arrives as declined:true with a reasonCode; 'BUDGET' covers the lifetime, denom,\n and count caps; 'OUTSIDE_WINDOW' covers both the rolling money and rolling count windows.\n"; /** Returns {@link PIPRAIL_AGENT_GUIDE} (a parity accessor for callers that prefer a function). */ declare function agentGuide(): string; /** * Scheme/chain triage for an x402 challenge — a pure, never-throwing read that * tells an agent WHY a 402 might be unpayable: "wrong chain" vs "the scheme isn't * enabled" vs "no rail at all". Stops those three very different fixes from being * conflated into one opaque `NO_COMPATIBLE_ACCEPT`. * * PURE: imports only types (`./x402.js`, `./client.js`) — zero chain libraries, * zero I/O. Feed it a challenge you already parsed (`parseChallenge`) plus your * client's bound network + enabled schemes. */ /** The verdict of a challenge triage — what's standing between you and paying. */ type ChallengeVerdict = 'PAYABLE_RAIL' | 'UNPAYABLE_SCHEME' | 'WRONG_CHAIN' | 'NO_RAIL'; interface ChallengeTriage { /** Does any offered rail sit on the client's bound network? */ onClientChain: boolean; /** Does any rail on the client's network use an enabled scheme? */ payableScheme: boolean; /** The distinct schemes the challenge offers. */ offeredSchemes: PaymentScheme[]; /** The distinct networks the challenge offers. */ offeredNetworks: Caip2[]; /** The one-word verdict. */ verdict: ChallengeVerdict; } /** * Bucket `challenge.accepts[]` by (network === `opts.network`) and * (scheme ∈ `opts.schemes`) and return the verdict. Never throws. */ declare function classifyChallenge(challenge: X402Challenge, opts: { network: Caip2; schemes: readonly PaymentScheme[]; }): ChallengeTriage; /** * Self-description — make a PipRail 402 announce WHAT it is and HOW to pay it, to * humans, AI agents, and crawlers alike, on BOTH schemes. PURE: a builder that turns * the `accepts[]` a gate already resolved into an inert, additive metadata block; it * does NO I/O and imports NO chain library (protocol layer — STANDARDS §1, viem-free). * * The block rides at `challenge.extensions.piprail`, ALONGSIDE the rejection * `{ code, detail }` (the gate deep-merges so neither clobbers the other). x402 v2 * treats `extensions` as an opaque bag a standard client ignores, so this is * purely-additive: the pay path, `accepts[]`, headers, and status stay byte-identical. * * Its whole point: even an `onchain-proof`-only endpoint that a stock x402 client * CANNOT pay is no longer invisible — a stranger reads `sdk.install`, runs * `npm i @piprail/sdk`, pastes the snippet, and pays. For the non-EVM families that * have NO standard `exact` rail, this block is the ENTIRE interop story, so the * `onchain-proof` instruction is deliberately chain-agnostic. * * NOT WIRED here — this is the pure builder (discoverability plan, Phase 1). The gate * wires it into every challenge in Phase 5 (default-on, with a `selfDescribe:false` * opt-out that restores the byte-identical default). */ /** * The canonical PipRail brand strings — the SINGLE source of truth for the install * command, the paste-ready snippet, and the docs links, read by the self-describe * block here, and (later phases) the landing page, the `llms.txt` entry, and the * agent guide. `` / `` are deliberate placeholders — never guess * the reader's chain or URL. */ declare const BRAND: { readonly name: "PipRail"; readonly home: "https://piprail.com"; readonly docs: "https://docs.piprail.com"; readonly payDocs: "https://docs.piprail.com/paying"; readonly sdkInstall: "npm i @piprail/sdk"; readonly sdkSnippet: string; readonly mcpRun: "npx -y @piprail/mcp"; }; /** * One payable rail as the self-describe block presents it — the static, agent- and * human-readable view of an `accepts[]` entry (no nonce; this is long-lived metadata). */ interface SelfDescribeRail { scheme: 'onchain-proof' | 'exact' | 'upto'; network: string; asset: string; payTo: string; /** Amount in the token's base units (already scaled by decimals). */ amount: string; /** Human-readable amount, e.g. "0.01", when the gate resolved one. */ amountFormatted?: string; symbol?: string; /** A one-line instruction for paying THIS rail — chain-agnostic for `onchain-proof`. */ how: string; } /** * What the endpoint DOES — the agent-readability payload. Present only when the merchant * described their resource (a `description`/`mimeType` on the gate, or a `discovery` * descriptor with a `summary`/`queryParams`/`output`); absent on a zero-config gate, so * the default 402 stays byte-identical. Lets an AI agent understand the endpoint's purpose, * inputs, and output shape from the 402 alone — no paid call to find out what it returns. */ interface SelfDescribeEndpoint { /** One human sentence: what this endpoint does. */ summary?: string; /** HTTP method it answers on. */ method?: string; /** The response content-type, e.g. 'application/json'. */ mimeType?: string; /** Query params it reads, as a JSON-Schema `properties` object (name → schema). */ input?: Record; /** Output hint — shape/type and a concrete example (examples ground an LLM far better * than a schema alone). */ output?: { type?: string; example?: unknown; }; } /** The `extensions.piprail` self-description block. Inert, purely-additive metadata. */ interface SelfDescription { name: 'PipRail'; protocol: 'x402'; version: '2'; /** One sentence: what this endpoint is. */ what: string; /** What the endpoint DOES (purpose · inputs · output) — see {@link SelfDescribeEndpoint}. * Only present when the merchant described the resource; absent on a zero-config gate. */ endpoint?: SelfDescribeEndpoint; /** Every rail the 402 offers, in the same order as `accepts[]`. */ pay: SelfDescribeRail[]; /** How to pay programmatically with the SDK. */ sdk: { install: string; snippet: string; }; /** How to pay via the MCP server (for AI agents). */ mcp: { run: string; tool: string; }; docs: { home: string; agents: string; pay: string; }; /** Where the open discovery artifacts live on this origin. */ discovery: { openapi: string; wellKnown: string; }; /** Present (and `true`) ONLY when this gate issues verifiable receipts (`receipts` on) — * so a crawler/agent reading the 402 knows the 200 will carry a self-verifiable * `extensions['offer-receipt']` receipt. Absent by default (byte-identical). */ verifiableReceipts?: true; /** A one-line human summary (the gate sets it from `describeChallenge`). */ instruction?: string; } /** * Build the `extensions.piprail` self-describe block from a challenge's resolved * `accepts[]`. PURE — every rail is derived from data the gate already has (no new * data, no I/O). `instruction` is the optional one-line human summary the gate computes * via `describeChallenge` (in `render.ts`) and passes in. * * INVARIANT — this block emits NO per-response DYNAMIC field (no nonce, timestamp, or * other per-call value). It is long-lived, static metadata: brand-string constants plus * values derived from the (static) resolved `accepts[]`. This matters for the x402 * `dynamicInfoFields` mechanism (x402-foundation #2655): an official client that * echo-validates `extensions` does a SUBSET check — an UNDECLARED field that changes per * response would trip `extension_echo_mismatch` and false-reject the buyer. If you EVER * add a per-call value to this block, you MUST either (1) keep it out, or (2) declare it * in a `dynamicInfoFields` list the echo check honours. See * `.claude/plans/x402-maxout/05-additive-conformance.md` §4.3 — and the regression guard * in `test/selfdescribe.test.ts` that fails the day a dynamic field appears here. */ declare function buildSelfDescription(input: { accepts: X402AnyAccept[]; instruction?: string; /** What the endpoint DOES — included only when non-empty (keeps the zero-config 402 * byte-identical). Built from the gate's `description`/`mimeType`/`discovery` descriptor * via {@link buildEndpointInfo}. */ endpoint?: SelfDescribeEndpoint; /** True when the gate's `receipts` option is on — stamps the additive `verifiableReceipts` * flag so a reader knows the 200 will carry a self-verifiable receipt. */ verifiableReceipts?: boolean; }): SelfDescription; /** * Assemble a {@link SelfDescribeEndpoint} from the pieces a gate knows — its * `description`/`mimeType` and an optional `discovery` descriptor. Pure. Returns * `undefined` when nothing was described, so the self-describe block (and thus the 402) * stays byte-identical on a zero-config gate. The descriptor's `summary` wins over the * gate `description` for the one-line "what it does". */ declare function buildEndpointInfo(input: { description?: string; mimeType?: string; descriptor?: { summary?: string; method?: string; queryParams?: Record; output?: { type?: string; example?: unknown; }; }; }): SelfDescribeEndpoint | undefined; /** * Discovery — make a gated resource FINDABLE, by emitting the open-standard * artifacts a crawler/index reads. PURE: this file turns the config a gate * already has into static metadata; it does NO network I/O and imports NO chain * library (protocol layer — STANDARDS §1). The runtime side (registering with / * searching the open indexes) lives in `indexes.ts` + the client. * * Three artifacts, three open conventions (see `.claude/research/x402-discovery.md`): * - OpenAPI-first `/openapi.json` (`x-payment-info` per op) — the live convention * the open indexes parse today (x402scan via `@agentcash/discovery`). * - `/.well-known/x402` origin file — x402scan's legacy bulk-register format. * - `_x402` DNS TXT line — the (experimental) DNS pointer draft. * * A merchant serves the emitted file on THEIR OWN origin (a static asset — no * backend). Nothing is PipRail-hosted. All opt-in; the pay path is untouched. * * Honesty note (held everywhere): there is no single ratified discovery * standard — OpenAPI-first is an emerging multi-vendor convention. Emit it, but * treat it as a moving target. And PipRail's own rails use `scheme: * 'onchain-proof'`; a merchant who also wants to be USEFULLY listed on the open * indexes should additionally offer a standard `exact` USDC rail on Base/Solana. */ /** * The static (nonce-free) shape of one payment option — the discoverable part of * an `X402AcceptEntry`. A live challenge adds a single-use `nonce`; discovery * metadata is long-lived, so it carries none. Produced by `gate.describe()`, * consumed by every emitter below. */ interface PaymentRail { /** `onchain-proof` (PipRail's default), the standard `exact` rail, or the standard `upto` * (metered) rail (dual-advertise). */ scheme: 'onchain-proof' | 'exact' | 'upto'; network: Caip2; asset: AssetId; payTo: AddressId; /** Amount in the token's base units (already scaled by decimals). */ amount: string; /** Human-readable amount, e.g. "0.05". */ amountFormatted: string; decimals: number; symbol?: string; maxTimeoutSeconds: number; /** Rail-IDENTITY extra a discoverer needs to reconstruct/route the rail, mirrored from the * live 402's `accept.extra` — the `upto` rail's MANDATORY `facilitatorAddress`, the `exact` * rail's EIP-712 domain (`name`/`version`), and the `assetTransferMethod`. Omitted for the * `onchain-proof` rail (it needs none). The base pricing fields stay top-level on the rail. */ extra?: Record; } /** One discoverable resource: its URL, how to call it, and how to pay it. */ interface ResourceDescription { /** The full URL of the gated resource. */ url: string; /** HTTP method the resource answers on. Default 'GET'. */ method?: string; /** Human description (shown to agents browsing an index). */ description?: string; /** Response content-type, e.g. 'application/json' (v2 ResourceInfo `mimeType`). */ mimeType?: string; /** The payment options the gate offers (its resolved `accepts`, nonce-free). */ accepts: PaymentRail[]; } /** Shared input for the emitters that describe a whole origin. */ interface ManifestInput { /** The origin the merchant controls, e.g. 'https://api.example.com' (no path). */ origin: string; /** The discoverable resources at that origin. */ resources: ResourceDescription[]; /** * Optional ownership proofs — each a signature of the BARE ORIGIN STRING by a * `payTo` key (EVM: eip191 hex). A trust badge on indexes that verify it * (x402scan: `recoverMessageAddress(origin, sig) === payTo`); never required to * be listed. Produce one via `client.discoverySigner()`, then `signer.signMessage(origin)` * (single-arg — it signs the bare origin string). */ ownershipProofs?: string[]; /** OpenAPI `info.title`. Default 'PipRail x402 resources'. */ title?: string; /** OpenAPI `info.version`. Default '1.0.0'. */ version?: string; /** * Stamp the doc with a `x-generator: "@piprail/sdk"` attribution — a standard, * unobtrusive "built with" marker (à la Swagger/Hugo) that rides along wherever * an index crawls your `/openapi.json`, so the tech spreads as you get found. * Default **true**; set `false` to omit it entirely. It's metadata only — it * changes nothing about how the resource is paid or listed. */ attribution?: boolean; } /** What `x-generator` stamps when attribution is on — see {@link ManifestInput.attribution}. */ declare const GENERATOR = "@piprail/sdk \u00B7 https://piprail.com"; /** * The "powered by" marker for the `x-powered-by` HTTP header — the response-side twin of the * `/openapi.json` {@link GENERATOR} stamp. Inert metadata; opt out with `attribution:false`. * **ASCII-only on purpose:** this is an HTTP header value, and Node's `setHeader` writes header * values as latin1 — a non-ASCII separator (e.g. `·` U+00B7) would arrive mangled (`·`) and is * RFC-7230-discouraged. {@link GENERATOR} keeps its `·` because it only lands in UTF-8 JSON bodies. */ declare const POWERED_BY = "PipRail x402 | https://piprail.com"; /** * Machine-readable discovery pointers for a gated endpoint's HTTP responses — a `Link` header * (RFC 8288) pointing crawlers/agents at the discovery docs, plus a tasteful `x-powered-by` * marker. Spread the result into BOTH the 402 challenge response AND the 200 settlement * response, so a payer / agent / crawler learns what served them on every hit (this is also * how the 200 "receipt" self-advertises — no change to the `X402Receipt` body needed). PURE — * returns a header bag the merchant sets; the SDK serves nothing. `attribution:false` omits * `x-powered-by` (parity with the `x-generator` opt-out on the OpenAPI doc). If you already set * a `Link` header, comma-merge it with this one rather than blindly spreading (object spread * would replace it). */ declare function discoveryHeaders(opts?: { attribution?: boolean; }): Record; /** A minimal, valid OpenAPI 3.1 document carrying `x-payment-info` per paid op. */ interface OpenApiDocument { openapi: '3.1.0'; info: { title: string; version: string; }; servers: { url: string; }[]; paths: Record>; /** "Built with @piprail/sdk" attribution (unless `attribution: false`). */ 'x-generator'?: string; /** Provenance block the open indexes read (x402scan / agentcash). */ 'x-agentcash-provenance'?: { ownershipProofs: string[]; }; } interface OpenApiOperation { summary?: string; responses: Record; /** The discovery payload an x402 index reads: the rails for this operation. */ 'x-payment-info': { x402Version: 2; accepts: PaymentRail[]; }; } /** x402scan's legacy origin file (`/.well-known/x402`). */ interface WellKnownX402 { version: 1; resources: string[]; ownershipProofs?: string[]; } /** * The official `/.well-known/x402.json` discovery manifest (x402-foundation PR #2646, * "Well known x402 discovery" — **DO NOT MERGE YET / for discussion**). A RICHER origin * file than the legacy {@link WellKnownX402}: per-item resource metadata + the resolved * `accepts` + a lifted input/output contract. PipRail emits this as a SECOND, * forward-compatible artifact — {@link buildWellKnownX402} (the x402scan legacy file) is * left untouched, so nothing is pinned to an unstable spec. The merchant hosts the result * at `/.well-known/x402.json`. */ interface WellKnownX402Manifest { x402Version: 2; /** Unix timestamp (seconds) the manifest was generated. */ lastUpdated: number; items: WellKnownX402Item[]; } /** One resource in a {@link WellKnownX402Manifest}. The live 402 stays authoritative — * `accepts` here is advisory, nonce-free discovery metadata. */ interface WellKnownX402Item { resource: { url: string; description?: string; mimeType?: string; serviceName?: string; tags?: string[]; }; /** The transport the resource is paid over. PipRail emits `'http'` today. */ type: 'http' | 'mcp'; /** The payment options the gate offers (its resolved rails, nonce-free). */ accepts: PaymentRail[]; /** How to call the resource (lifted from the route config). */ input?: { method: string; routeTemplate?: string; pathParams?: Record; queryParams?: Record; }; /** Output hint for a richer listing. */ output?: { mimeType?: string; example?: unknown; }; /** Capability hints (extension keys) the resource requires, when any. */ requires?: string[]; } /** * Describes a resource's INPUT for discovery. The open indexes that REQUIRE an * input schema (x402scan rejects a listing without one) read this from a * `extensions.bazaar` block. Pass it to a gate's `discovery` option (emits the * block in the 402 challenge) or build it directly with {@link buildBazaarExtension}. */ interface DiscoveryDescriptor { /** One human sentence: WHAT this endpoint does (e.g. "Current USD price for any * crypto ticker"). Surfaced in the `extensions.piprail` self-describe block so an * agent understands the endpoint at a glance, without a paid call. */ summary?: string; /** HTTP method the resource answers. Default `'GET'`. */ method?: string; /** Query params the resource reads, as a JSON-Schema `properties` object * (name → schema). Default `{}` — a no-input GET. */ queryParams?: Record; /** Optional output hint (shape/example) for a richer listing. */ output?: { type?: string; example?: unknown; }; } /** The `extensions.bazaar` discovery block (the x402 "bazaar" convention the open * indexes parse: `info.input` describes the request, `schema` is its JSON Schema). */ interface BazaarExtension { info: { input: { type: 'http'; method: string; queryParams: Record; }; output?: { type?: string; example?: unknown; }; }; schema: Record; } /** * Build the `extensions.bazaar` block that satisfies x402scan's mandatory input-schema * check, from a {@link DiscoveryDescriptor}. Pure. Defaults to a no-input GET — the * minimal shape a live x402scan listing accepts. */ declare function buildBazaarExtension(descriptor?: DiscoveryDescriptor): BazaarExtension; /** The `_x402` DNS TXT pointer record (experimental draft). */ interface X402DnsRecord { name: string; type: 'TXT'; value: string; } /** * Build a `/openapi.json` document — the OpenAPI-first discovery doc the live * open indexes parse. One path per resource (grouped by pathname, keyed by * method), each paid operation carrying `x-payment-info` with the resource's * rails. Ownership proofs (if any) ride at `x-agentcash-provenance`. * * The merchant serves the result at `https:///openapi.json`. Pure — no * network, no chain library, fully deterministic. */ declare function buildOpenApi(input: ManifestInput): OpenApiDocument; /** * Build the `/.well-known/x402` origin file (x402scan's legacy bulk-register * format): the list of discoverable resource URLs + optional ownership proofs. * `/openapi.json` is the primary doc; this is a compatibility breadcrumb. */ declare function buildWellKnownX402(input: ManifestInput): WellKnownX402; declare function buildWellKnownX402Manifest(input: ManifestInput & { lastUpdated?: number; }): WellKnownX402Manifest; /** * Build the `_x402` DNS TXT record (experimental draft) that POINTS at a * discovery doc. `host` is the exact host the agent talks to (no inheritance); * `discoveryUrl` is the HTTPS manifest (typically `https:///openapi.json`). * Returns the record name + value to paste into the zone — PipRail never touches DNS. */ declare function buildX402DnsTxt(input: { host: string; discoveryUrl: string; descriptor?: string; }): X402DnsRecord; /** * The accept side: gate any endpoint behind a payment with one function. * * import { requirePayment } from '@piprail/sdk' * * app.get('/report', * requirePayment({ chain: 'base', token: 'USDC', amount: '0.05', payTo: '0xMerchant…' }), * (req, res) => res.json({ secret: 42 }) * ) * * One parameter picks the chain family: 'base'/'bnb'/… → EVM, 'solana' → * Solana, 'ton' → TON. Non-EVM drivers auto-mount on first use — no setup * call. The payment is verified against the chain's RPC, in-process — no * backend, no database. * * Replay protection is an in-memory used-tx set scoped to the gate — * single-process by design; pass your own `isUsed`/`markUsed` to share it. */ /** * One payment option a gate offers. Pass several to `requirePayment({ accept: * [...] })` and the challenge offers them all in `accepts[]` — the agent pays on * whichever chain it holds funds for (USDC on Base OR Solana OR …). `payTo`/ * `rpcUrl` fall back to the top-level option when omitted (per-family payTo is * usually given here, since address shapes differ across chains). */ interface AcceptOption { /** Which chain. EVM ('bnb'|'base'|…), 'solana', 'ton', 'stellar', 'xrpl', * 'tron', 'near', 'sui', 'aptos', or 'algorand'. */ chain: ChainSelector; /** Token to be paid in (symbol / 'native' / custom descriptor). */ token: TokenInput; /** Human-readable amount for THIS option, e.g. "0.05". */ amount: string; /** Recipient for this chain (defaults to the top-level `payTo`). */ payTo?: AddressId; /** RPC override for this chain (defaults to the top-level `rpcUrl`). */ rpcUrl?: string; } /** * Opt into ALSO advertising a standard x402 `exact` rail beside the default * `onchain-proof` rail, so ANY standard x402 client can pay this gate (dual-advertise). * Supported on **EVM ERC-20** — **EIP-3009** (USDC, EURC) or, for tokens without it, **Permit2** * (any ERC-20 — e.g. Binance-Peg USDC on BNB, settled via the canonical x402ExactPermit2Proxy) — * and on **Solana** (any SPL token; the buyer partial-signs a `TransferChecked` and your relayer * is the fee payer). NOT native coins, NOT families without a standard `exact` scheme. Omitting * `exact` leaves the gate byte-identical to today (onchain-proof only). * * Two settlement modes, both backendless (PipRail hosts nothing): * - `settle: 'self'` — your own `relayer` key broadcasts the settle (EVM EIP-3009's * `transferWithAuthorization` / the proxy's `settle` for Permit2; on Solana, co-signing the * buyer's transaction as the fee payer). You pay gas to RECEIVE (the inverse of onchain-proof) * and keep the relayer funded. The signature binds the recipient, so there's no redirect risk. * The on-brand backendless default for the rail. * - `settle: { facilitator }` — delegate verify+settle to a third-party x402 facilitator YOU * choose. **The facilitator pays the gas, so neither the buyer nor the merchant pays any** — * fully gasless end to end. On **EVM** use Coinbase CDP, x402.org, PayAI, …; on **Solana** use a * facilitator that sponsors the fee payer (e.g. PayAI's `https://facilitator.payai.network`, * no API key) — the gate reads its fee-payer pubkey from `GET /supported` automatically. No * relayer key needed. (EVM facilitators are also the path onto Coinbase's Bazaar directory.) */ interface ExactRailOption { /** How the gate settles an inbound `exact` payment. `'self'` = your own `relayer` broadcasts * (you pay gas). `'keyless'` = auto-pick a known KEYLESS facilitator for the chain (it sponsors * gas — zero-config; the same resolution as the top-level `exact: true` shorthand). `{ facilitator }` * = a specific facilitator you name — pin this in production rather than relying on the auto-pick. */ settle: 'self' | 'keyless' | { facilitator: string; authHeaders?: () => Promise>; /** Solana only — the facilitator's fee-payer pubkey, if you'd rather set it than have the * gate read it from the facilitator's `GET /supported`. Optional: omitted, the gate * discovers it automatically (e.g. PayAI). Ignored on EVM. */ feePayer?: string; }; /** Required for `settle: 'self'` — the gas-paying relayer wallet: a `{ key }` (or a * bring-your-own EVM `{ walletClient }` / Solana `{ signer }`). (Distinct from `payTo`, the * receive address — on Solana they MUST be different keys, a scheme MUST-rule.) */ relayer?: unknown; /** Which exact transfer method to advertise (EVM). `'auto'` (default) uses EIP-3009 when the * token supports it, else Permit2 — so a non-EIP-3009 token like Binance-Peg USDC on BNB * "just works". Force `'eip3009'` or `'permit2'` to pin one. Ignored on Solana (always SVM). */ method?: 'eip3009' | 'permit2' | 'auto'; } /** * Opt into ALSO advertising a standard x402 `upto` (metered / variable-amount) rail beside the * default `onchain-proof` rail — for usage-billed APIs (pay-per-LLM-token, per-byte, per-query). * The buyer signs a Permit2 authorization for `amount` as a **MAXIMUM**; the merchant serves the * resource, meters the **actual** usage, then **self-settles** `actual ≤ max` through the * canonical `x402UptoPermit2Proxy` from its own `relayer` (which IS the bound `witness.facilitator` * — backendless, no third-party facilitator). **EVM-Permit2 ONLY** (the upto spec bans EIP-3009 and * has no non-EVM variant); native coins and non-Permit2 chains never carry it. Self-settle ONLY in * v1 (no facilitator mode). Omitting `upto` leaves the gate byte-identical to today. * * **⚠ UNSUPPORTED through the Express `requirePayment` middleware** — it settles BEFORE the route * handler serves, so the metered usage isn't known yet; constructing `requirePayment({ upto })` * THROWS. The supported handler shape is a **direct `gate.verify(header)` call** where the metering * happens INSIDE the `settleAmount` callback: serve/compute enough to know usage → `await * gate.verify(header)` whose `settleAmount` returns that usage → write the body + the receipt header. */ interface UptoRailOption { /** REQUIRED — the gas-paying relayer wallet that settles AND is the bound `witness.facilitator` * (a `{ key }` or a bring-your-own EVM `{ walletClient }`). Distinct from `payTo`, the receive * address — though here they MAY be the same key (the proxy binds `witness.to` separately). */ relayer: unknown; /** * REQUIRED — the merchant callback that picks the ACTUAL charge AFTER serving (the * deferred-settle lifecycle). Called with the metered context; return a `bigint` (base units), * or a string in the x402 SettlementOverrides forms — raw atomic (`"1858"`), `"NN%"` of the max * (floored), or `"$X"` (rounded to the token's decimals). `"0"`/`0n` ⇒ a zero-charge receipt with * NO on-chain tx. The gate clamps to ≤ the advertised max and rejects-over-max defensively * (`upto_settle_exceeds_max`). On the direct `gate.verify()` path this is where you meter usage. */ settleAmount: (ctx: { maxAmount: bigint; asset: string; network: Caip2; decimals: number; request?: unknown; }) => bigint | string | Promise; } /** * The merchant-side mirror of {@link PaidReceipt}: what an {@link RequirePaymentOptions.onFailed} * hook receives when a SUBMITTED payment proof is REJECTED. It carries the SAME machine-readable * `code` the buyer's client is given for that rejection, so both sides are notified of one * consistent reason. (A rejection has no settlement, so — unlike a receipt — there is no tx hash * or settled amount to report.) */ interface FailedPayment { /** The canonical rejection reason — the same {@link VerifyErrorCode} surfaced to the buyer * (e.g. `amount_too_low`, `payment_expired`, `tx_already_used`, `transfer_not_found`). */ code: VerifyErrorCode; /** Human-readable detail, e.g. `"Paid 40000, required 500000."`. */ detail: string; /** * `true` for a **transient** rejection (`tx_not_found` / `insufficient_confirmations`): the proof * may still be settling and the buyer's client **retries automatically** — you'll get `onPaid` if * it then succeeds. `false` for a **definitive** rejection the buyer must fix (wrong amount, * expired, replayed, bad signature, wrong recipient). Alert on `!transient` to avoid false alarms * on normal RPC lag; nothing is hidden — every rejected attempt still fires `onFailed`. */ transient: boolean; } interface RequirePaymentOptions { /** * Single-chain form: which chain to accept payment on. EVM ('bnb'|'base'|…), * 'solana', 'ton', 'stellar', 'xrpl', 'tron', 'near', 'sui', 'aptos', or * 'algorand'. Provide `chain` + `token` + `amount`, OR use the multi-chain * `accept` array below. */ chain?: ChainSelector; /** Override the chain's default RPC URL (recommended in production). */ rpcUrl?: string; /** * What to be paid in (single-chain form). Use a symbol the chain ships * (`'USDC'` / `'USDT'`), the chain's coin (`'native'`), or a custom token: * `{ address, decimals }` on EVM/Tron, `{ mint, decimals }` on Solana, * `{ master, decimals }` on TON, `{ issuer, code, decimals }` on Stellar, * `{ issuer, currencyHex, decimals }` on XRPL, `{ contractId, decimals }` on * NEAR, `{ coinType, decimals }` on Sui, `{ metadata, decimals }` on Aptos, or * `{ assetId, decimals }` on Algorand. You name the token; the SDK fills in * the contract + decimals for built-in symbols. (Note: native USDC doesn't * exist on TON/Tron — USDT does; native NEAR is supported via `'native'`.) */ token?: TokenInput; /** Human-readable amount, e.g. "0.05" (single-chain form). */ amount?: string; /** * Multi-chain form: offer several payment options in ONE challenge. The agent * picks the chain/token it can pay. Mutually exclusive with the single-chain * `chain`/`token`/`amount` fields above; provide one form or the other. */ accept?: AcceptOption[]; /** Address that receives the payment (0x… EVM/Sui, base58 Solana, EQ…/UQ… TON, * G… Stellar, r… XRPL, T… Tron, account id on NEAR, 0x… Aptos, base32 Algorand). * Required for the single form; the per-option fallback for the multi form. */ payTo?: AddressId; /** Shown to the agent in the challenge. Also the one-line "what this endpoint does" * in the self-describe block (a `discovery` descriptor's `summary` overrides it). */ description?: string; /** Response content-type, e.g. 'application/json'. Emitted at the v2 root `resource.mimeType` * and in the self-describe `endpoint` so an agent knows how to parse the paid response. */ mimeType?: string; /** Confirmations required before access is granted. Default 1. */ minConfirmations?: number; /** Max age of an accepted payment, in seconds. Default 600. */ maxTimeoutSeconds?: number; /** Nonce generator. Default `crypto.randomUUID()`. */ generateNonce?: () => string; /** Replay hook — return true if this proof was already redeemed. */ isUsed?: (ref: string) => boolean | Promise; /** Replay hook — record a redeemed proof. */ markUsed?: (ref: string) => void | Promise; /** * Fired when a payment verifies successfully, with the enriched {@link PaidReceipt}. * May be **sync or async** — a throw OR a rejected promise is isolated (routed to * `onPaidError`), so the hook can never break the request or crash the process. * Fire-and-forget by default (the response is not blocked on it); set `awaitOnPaid` * to record the receipt before the resource is served. `onPaid` is **at-least-once** * across instances — dedupe on `receipt.idempotencyKey`. See {@link PaidReceipt}. */ onPaid?: (receipt: PaidReceipt) => void | Promise; /** * Observe a failure inside `onPaid` (sync throw or async rejection). Without it, * a failing receipt hook is swallowed silently — set this to log/alert/queue the * dropped receipt. Its own throws are also swallowed (it can never break a request). */ onPaidError?: (error: unknown, receipt: PaidReceipt) => void; /** * Await `onPaid` before returning the paid result (and thus before the gated * resource is served), so "receipt recorded" is guaranteed on the happy path. * Default `false` (fire-and-forget — lower latency). A rejection is still isolated * via `onPaidError`; it never turns a settled payment into a 402. */ awaitOnPaid?: boolean; /** * The merchant-side mirror of `onPaid`: fired when a SUBMITTED payment proof is REJECTED — a * `kind:'invalid'` verdict (wrong amount, expired, replayed, unknown asset, …). Receives a * {@link FailedPayment} carrying the SAME machine `code` the buyer's client is given, so the * merchant and the buyer are notified of the same failure with the same reason. * * Fires ONLY on a rejected attempt — NOT on a normal first-request 402 `challenge` (no proof * yet), and NOT on a transient/settlement error that throws (an RPC blip, or a 5xx * `SettlementError`): those aren't payment verdicts. Like `onPaid`, it may be **sync or async** * and is fully isolated — a throw OR a rejected promise is caught and routed to `onFailedError`, * so it can never break the request or crash the process. Fire-and-forget by default; set * `awaitOnFailed` to run it before the 402 is returned. * * NOTE: a failure the merchant never receives a request for — the buyer can't afford it, an * `onBeforePay`/`policy` declines it, or the buyer abandons before paying — cannot reach a * backendless gate (only the buyer's client sees it). `onFailed` covers every rejection that * DOES reach the gate. */ onFailed?: (failure: FailedPayment) => void | Promise; /** * Observe a failure inside `onFailed` (sync throw or async rejection) — the mirror of * `onPaidError`. Without it, a throwing `onFailed` is swallowed silently. Its own throws are * also swallowed (it can never break a request). */ onFailedError?: (error: unknown, failure: FailedPayment) => void; /** * Await `onFailed` before the 402 rejection is returned (mirror of `awaitOnPaid`), so * "failure recorded" is guaranteed before the caller is told. Default `false` (fire-and-forget). * A rejection inside the hook is still isolated via `onFailedError`. */ awaitOnFailed?: boolean; /** * ALSO advertise a standard x402 `exact` rail so any standard x402 client can pay this * gate — opt-in, EVM (EIP-3009/Permit2) + Solana (SVM). See {@link ExactRailOption}. * Shorthand **`exact: true`** === `{ settle: 'keyless' }`: the gate auto-picks a known KEYLESS * facilitator for each offered chain (from `KNOWN_FACILITATORS`), so neither buyer nor merchant * pays gas, zero-config. It is a SOFT, best-effort flag — a chain with no available keyless * facilitator DEGRADES GRACEFULLY to the always-present `onchain-proof` rail (the buyer pays gas, * the only option left when no facilitator can sponsor) with a LOUD warning; it never bricks the * gate. For guaranteed gasless, pin `settle: { facilitator }` (recommended in production) or * self-settle `settle: 'self'`; an EXPLICIT `settle` that can't carry exact throws loudly (a config * error you should fix). `false`/omitted keeps the gate exactly as today (`onchain-proof` only — * byte-identical). */ exact?: boolean | ExactRailOption; /** * ALSO advertise a standard x402 `upto` (metered / variable-amount) rail — for usage-billed * APIs (pay-per-LLM-token, per-byte, …). Opt-in, EVM-Permit2 ONLY, self-settle ONLY. See * {@link UptoRailOption}. The buyer signs for a MAX; you meter + settle the ACTUAL after serving * via the `settleAmount` callback. **UNSUPPORTED through `requirePayment` (the Express middleware * settles before the route handler serves) — constructing `requirePayment({ upto })` THROWS;** use * a direct `gate.verify()` call and meter inside `settleAmount`. Omitted ⇒ byte-identical to today. */ upto?: UptoRailOption; /** * Make this gate's 402 self-describing for the open indexes — **x402scan REQUIRES * an input schema or it won't list the resource.** Set `true` for a no-input GET, * or pass a {@link DiscoveryDescriptor} to describe the request. Emits an * `extensions.bazaar` block in the 402 challenge. Opt-in; omitting it leaves the * challenge byte-identical to before. */ discovery?: boolean | DiscoveryDescriptor; /** * Self-describe every 402 — stamp an `extensions.piprail` block (identity · per-rail * how-to-pay · `npm i @piprail/sdk` + snippet · MCP · docs + discovery pointers) so the * instant any human, AI agent, or crawler lands on this endpoint — even the default * `onchain-proof` scheme a stock x402 client can't pay — it knows what it is and how to * pay it. **Default `true`.** It is purely-additive metadata a standard client ignores * (the spec treats `extensions` as opaque), so the pay path, `accepts[]`, headers, and * status are byte-identical. Set `false` to omit the block entirely (the literal * byte-identical default of before this feature). See {@link buildSelfDescription}. */ selfDescribe?: boolean; /** * Advertise + honor the standard x402 **`payment-identifier`** extension — an OPTIONAL * idempotency `id` (16–128 chars `[A-Za-z0-9_-]`) the client attaches to its payload at * `extensions['payment-identifier'].info.id`. When on, the gate advertises the extension on * every 402 and, on submission, **dedupes the id on its existing used-proof set** (namespaced * `pid:`): a reused id bound to a different/already-settled payment is rejected * (`tx_already_used`), a malformed id is re-challenged (`signature_invalid`), and the id is * echoed back on the settled `PAYMENT-RESPONSE`. The id is ADDITIVE to — never a replacement * for — the proof-set replay protection (a payment with no id is protected exactly as today). * **Default off** (the challenge + verdict are byte-identical). Backendless: the dedupe rides * the same in-memory / pluggable `isUsed`/`markUsed` store as a tx ref — no new state. */ paymentIdentifier?: boolean; /** * Emit a **verifiable receipt** on every settled payment — a self-contained * {@link PipRailReceipt} that the buyer keeps and **anyone** re-verifies against the * chain with only an RPC (no key, no backend, no PipRail account). It rides in an * `extensions['offer-receipt'].info` block on the `PAYMENT-RESPONSE` header (the * chain-grounded record at `info.settlement`; the optional Tier-2 SignedReceipt at the * spec's `info.receipt` slot) and stamps the challenge `nonce` onto the receipt so the five Template-A * (memo/nonce-bound) families re-verify off-chain. **Default off → byte-identical.** * * - `true` / `{}` → Tier-1 chain-grounded receipt (the settlement tx is the * authority — re-verified via the driver's own `verify()`). * - `{ includeTxHash }` → see {@link ReceiptOption}. Default `true` (PipRail inverts * the official privacy-default; the open chain is the pitch). * * Purely additive metadata a standard x402 client ignores; the `accepts[]`, status, * and pay path are unchanged. {@link PipRailClient.verifyReceipt} re-verifies it. */ receipts?: boolean | ReceiptOption; } /** Tuning for the {@link RequirePaymentOptions.receipts} verifiable-receipt emission. */ interface ReceiptOption { /** * Put the settlement tx hash in the receipt. **Default `true`** — PipRail inverts the * official x402 privacy-default (which omits it) because the whole pitch is the open, * self-verifiable chain. Set `false` for a privacy-minded merchant: the wire * `transaction` becomes the empty string `''` (per spec §5.3, never a missing key) and * the Tier-1 receipt is no longer third-party on-chain-verifiable (it carries no tx to * re-read) — use Tier-2 attestation for that case. */ includeTxHash?: boolean; /** * The canonical resource URL to embed in the merchant-emitted receipt (what was paid * for). The buyer's {@link PipRailClient.lastReceipt} also fills this from the URL it * fetched, so set it here only to ground the raw header a third party reads directly. * Default `''` (the buyer's client fills it). */ resource?: string; /** * **Tier-2 — service-delivery attestation (OPTIONAL, EVM-only).** When set, the gate * ALSO signs the official x402 `offer-receipt` EIP-712 receipt with the merchant's own * wallet, attesting the one thing the chain can't: that the resource was actually * **served**. A verifier checks `recover(sig) === payTo` — zero new infra, the * merchant's existing `payTo` key. The {@link SignedReceipt} rides at the official * spec slot `extensions['offer-receipt'].info.receipt` (so a stock `@x402/extensions` * reader reads it unchanged); verify with {@link PipRailClient.verifyAttestation}. * * - `{ wallet }` → the merchant's existing payTo key (EIP-712). EVM-only: on a non-EVM * rail it **degrades to Tier-1 + a one-time warning**, never throws. * - `{ jws }` → a managed JWS signer (did:web/key) — **R3, not yet implemented** * (the type slot is reserved). Supplying it today degrades to Tier-1 + a warning. * * Signing failures degrade to the unsigned Tier-1 receipt (isolated exactly like * `onPaid`) — they never fail the 200. */ attest?: { wallet: unknown; } | { jws: unknown; }; } type VerifyPaymentResult = { kind: 'paid'; receipt: X402Receipt; receiptHeader: string; } | { kind: 'challenge'; challenge: X402Challenge; requiredHeader: string; statusCode: 402; } | { /** * A submitted proof was rejected. Conformant: this carries a FRESH * re-`challenge` (full v2 PaymentRequired with `accepts[]` + the reason in * `error` + the machine code in `extensions.piprail`) so a standard x402 * client can immediately retry. Adapters emit `challenge` + the * `PAYMENT-REQUIRED` header — NOT the legacy {@link toInvalidBody}. */ kind: 'invalid'; error: string; detail: string; challenge: X402Challenge; requiredHeader: string; statusCode: 402; }; /** A minimal 402 'invalid' JSON body. @deprecated — see {@link toInvalidBody}. */ interface X402InvalidBody { x402Version: 2; status: 'invalid'; error: string; detail: string; } /** * @deprecated LEGACY minimal rejection body. The gate now returns a fully * **conformant** rejection: `gate.verify()`'s `kind:'invalid'` result carries a full * v2 PaymentRequired re-`challenge` (with `accepts[]` so a standard x402 client can * retry, the reason in `error`, and the machine code in `extensions.piprail`). The * built-in `requirePayment` adapter emits `result.challenge` + the `PAYMENT-REQUIRED` * header. PREFER that. This helper (a bare `{ status:'invalid', error, detail }` with * NO `accepts[]`) remains only for back-compat with hand-rolled adapters; a standard * client that receives it can't retry. Migrate to `result.challenge`. */ declare function toInvalidBody(result: { error: string; detail: string; }): X402InvalidBody; /** * The result of {@link PaymentGate.selfTest} — a read-only config check. NEVER throws, and never * touches the network beyond the same lazy driver/token resolution the first `challenge()` does * (no signing, no sending). `ok:true` with the resolved `rails` when the config is sound; `ok:false` * with a human `error` when something's wrong (no payTo, a malformed address for the family, an * unknown token, an unresolvable chain). Powers a scaffolder's "✅ your endpoint is configured" * step and a merchant's `npm run verify`. */ interface GateSelfTest { ok: boolean; /** One entry per resolved payment option (empty when `ok:false`). */ rails: Array<{ /** CAIP-2 network, e.g. `eip155:8453`. */ network: string; asset: string; symbol?: string; decimals: number; /** Human amount the gate charges (for a tip jar, the minimum/floor). */ amount: string; payTo: AddressId; /** The schemes this rail offers, e.g. `['onchain-proof']` or `['exact', 'onchain-proof']`. */ schemes: string[]; }>; /** Non-fatal nudges (e.g. a custom token with no built-in symbol to double-check). */ warnings: string[]; /** Present only when `ok:false` — the human reason the config can't serve a payment. */ error?: string; } interface PaymentGate { /** Build a fresh 402 challenge (new nonce) for a resource URL. */ challenge(resourceUrl?: string): Promise<{ challenge: X402Challenge; requiredHeader: string; }>; /** Verify an incoming `payment-signature` header value. */ verify(paymentSignature: string | string[] | undefined): Promise; /** * Verify an already-decoded PaymentPayload OBJECT (raw JSON, not base64) — the * additive seam for non-HTTP transports (A2A carries the payload as raw JSON * `metadata`, never a base64 header). Runs the EXACT SAME dispatch as {@link verify} * (upto → exact → onchain-proof), reading `sig.payload.nonce` from the object exactly * as the HTTP path does — so it adds ZERO new verification state and shares the gate's * one replay set. `verify(b64)` is the base64 path and stays byte-identical; this only * skips the decode. The same `onPaid`/`onFailed` hooks fire. */ verifyObject(payload: unknown): Promise; /** * Describe this gate's payment options as static, nonce-free discovery * metadata — feed it to the emitters in `discovery.ts` (`buildOpenApi` / * `buildWellKnownX402`) to make the resource findable. Reuses the same * resolved options the challenge is built from (so a `0x…` payTo / decimals * are already correct); unlike `challenge()`, it mints no nonce, because * discovery metadata is long-lived. Read-only — moves nothing on-chain. */ describe(resourceUrl?: string): Promise; /** * Render a self-describing HTML page for a challenge — for the HUMAN who opens the gated * URL in a browser. Pass the challenge you already built with {@link PaymentGate.challenge}; * serve the result with `content-type: text/html` when the request's `Accept` is `text/html`. * The SDK never serves it itself (headless by charter). Agents/crawlers keep the JSON 402. */ landingPage(challenge: X402Challenge): string; /** * Read-only config check — resolve the gate's rails WITHOUT signing or sending, and report what * it would charge (or why it can't). Never throws; runs the same lazy resolution as the first * `challenge()`. The merchant's "did I wire this right?" and a scaffolder's post-deploy smoke * step. See {@link GateSelfTest}. */ selfTest(): Promise; } declare function createPaymentGate(options: RequirePaymentOptions): PaymentGate; interface ExpressLikeRequest { headers: Record; originalUrl?: string; url?: string; } interface ExpressLikeResponse { setHeader: (name: string, value: string) => void; status: (code: number) => unknown; json: (body: unknown) => unknown; } type ExpressLikeNext = (err?: unknown) => void; type ExpressLikeMiddleware = (req: ExpressLikeRequest, res: ExpressLikeResponse, next: ExpressLikeNext) => Promise | void; /** * Express/Connect-style middleware. The 80% case — drop it in front of a * route handler and the route is paid-only. The driver auto-mounts on the * first request; config/driver errors are forwarded to `next(err)`. */ declare function requirePayment(options: RequirePaymentOptions): ExpressLikeMiddleware; /** * The advanced gate options a preset forwards verbatim — every {@link RequirePaymentOptions} field * EXCEPT the `chain`/`token`/`amount`/`payTo` quartet (each preset re-declares those with its own * shape) and the multi-chain `accept` array (use {@link createPaymentGate} directly for a multi-rail * gate). So `onPaid`, `exact`, `receipts`, `discovery`, `isUsed`/`markUsed`, … all pass through. */ type GateExtras = Omit; /** Options for {@link createPaywall}. */ interface PaywallOptions extends GateExtras { /** Which chain to be paid on. EVM (`'base'`|`'bnb'`|…) or a non-EVM family name. */ chain: ChainSelector; /** Token to charge in. Defaults to **USDC**. */ token?: TokenInput; /** The fixed price, human-readable, e.g. `'0.05'`. */ amount: string; /** Your receiving wallet address — no private key (receiving needs only the address). */ payTo: AddressId; } /** * Gate one resource behind a fixed price — the API / SaaS / premium-content case. Sugar over * {@link createPaymentGate} with `token` defaulting to USDC; every other gate option is forwarded * unchanged, so the resulting gate (and its 402) is identical to the hand-written equivalent. */ declare function createPaywall({ token, ...rest }: PaywallOptions): PaymentGate; /** Options for {@link createTipJar}. */ interface TipJarOptions extends GateExtras { /** Which chain to be paid on. */ chain: ChainSelector; /** Token to accept. Defaults to **USDC**. */ token?: TokenInput; /** * The MINIMUM tip, human-readable, e.g. `'1.00'`. The gate accepts any payment **≥ min** — the * on-chain verify rejects only an under-payment (`amount_too_low`), so a payer can always give * more. There is no upper bound; the minimum is a floor, not a fixed price. */ min: string; /** Your receiving wallet address. */ payTo: AddressId; } /** * An open "pay what you want (≥ a minimum)" gate — the creator / tip / donation case. Sugar over * {@link createPaymentGate} that sets the challenge `amount` to `min`; because the gate accepts an * over-payment, the minimum is a floor. Everything else (`onPaid`, etc.) forwards unchanged. */ declare function createTipJar({ token, min, ...rest }: TipJarOptions): PaymentGate; /** * Built-in framework adapters — turn a {@link PaymentGate} into a request handler for any * WHATWG-`fetch` runtime. There are only TWO real shapes among fetch runtimes, so there are two * adapters: a plain handler **function** (Next.js, Netlify, Bun, Deno, Vercel Edge, Hono, Lambda), * and the `{ fetch }` **export object** (Cloudflare / Service Workers). Both run the same contract: * read the proof header, switch on the {@link VerifyPaymentResult} `kind`, write the right status + * headers back out — so a merchant's route is a single call instead of a switch. * * import { createPaywall, toFetchHandler, toWorker } from '@piprail/sdk' * const gate = createPaywall({ chain: 'base', amount: '0.05', payTo: '0xYourWallet' }) * * export const GET = toFetchHandler(gate, () => Response.json({ secret: 42 })) // Next / Netlify / Bun / Deno / Hono … * export default toWorker(gate, () => Response.json({ secret: 42 })) // a Cloudflare Worker * * Pure + browser-safe — Web `Request`/`Response`/`Headers` only (no viem, no `node:`). Express keeps * its dedicated {@link requirePayment} middleware; a Node-native framework with its own `req`/`reply` * (Fastify, …) drives `gate.verify()` directly (see the framework-adapters docs). {@link proxyTo} is a * ready-made `serve` that forwards paid requests to an existing backend — gate any API, any language. */ /** * What to serve once a payment is verified — your protected resource, as a Web `Response`. It * receives the original `request` **plus whatever extra arguments the runtime passed the handler** * (a Cloudflare Worker's `env`/`ctx`, a Next.js route `context` with `params`, …), forwarded * untouched — so a protected handler can reach framework context without a second wrapper. */ type Serve = (request: Request, ...rest: unknown[]) => Response | Promise; /** * The universal adapter: wrap a gate as a `fetch` handler `(request, ...rest) => Response`. Drop the * result into **any** runtime that hands a handler a `Request` and wants a `Response` back — Next.js * route handlers (`export const GET = …`), Netlify Functions, `Bun.serve({ fetch })`, * `Deno.serve(…)`, Vercel Edge, Hono (`(c) => handler(c.req.raw)`), an AWS Lambda Web adapter, Fastly * Compute. Any extra runtime arguments are forwarded to `serve` untouched. * * - **settled payment** → calls `serve`, returns its `Response` with the `payment-response` headers (v2 + v1) added; * - **missing / rejected proof** → a conformant `402` carrying the full challenge (so a standard x402 client can retry); * - **server-side settle failure** ({@link SettlementError}) → `502` — NEVER `402` (the buyer's authorization is still valid + unused). */ declare function toFetchHandler(gate: PaymentGate, serve: Serve): (request: Request, ...rest: unknown[]) => Promise; /** * The `{ fetch }` export object for runtimes that take one — Cloudflare Workers / Service Workers: * `export default toWorker(gate, serve)`. Identical behaviour to {@link toFetchHandler}; the * runtime's `fetch(request, env, ctx)` arguments are forwarded to `serve` (so it can read bindings / * `ctx.waitUntil`). (Other runtimes that take an object — `Bun.serve`, `Deno.serve` — can use either * this or `toFetchHandler` in their `fetch` field.) */ declare function toWorker(gate: PaymentGate, serve: Serve): { fetch: (request: Request, ...rest: unknown[]) => Promise; }; /** * A {@link Serve} that forwards the (already-paid) request to an upstream `origin`, untouched — so you * can put a payment gate in FRONT of an existing API in any language, without changing it. Preserves * the method, path, query, body, and headers; strips the x402 proof headers so they don't leak * upstream. The origin NEVER sees an unpaid request — the gate rejects those before `serve` runs. * Compose with the adapters: `toWorker(gate, proxyTo('https://my-api.example.com'))`. */ declare function proxyTo(origin: string): Serve; /** * Mode-B settlement: delegate a standard `exact` payment to a THIRD-PARTY x402 * facilitator the MERCHANT chooses (Coinbase CDP, x402.org, or any). PipRail hosts * nothing — this is two HTTP POSTs to the merchant's configured facilitator URL. * * PROTOCOL LAYER — pure `fetch`, ZERO chain libraries (STANDARDS §1). The other * settlement mode (self-settle with the merchant's own relayer key) lives in the * EVM driver; this one is chain-agnostic because the facilitator does the chain work. * * The wire contract (x402 v2, verified against coinbase/x402 core): * POST {url}/verify body { x402Version, paymentPayload, paymentRequirements } → { isValid, invalidReason?, payer? } * POST {url}/settle SAME body → { success, transaction, network, payer?, errorReason? } * Both protocol outcomes are HTTP 200 (the boolean flips); a non-200 is a * transport/auth failure. There is no registration and no idempotency key — so a * SELF-settling merchant must guard replay itself (the gate's used-proof set does). */ /** Standard x402 `exact` PaymentRequirements, built from the gate's TRUSTED rail. `extra` * carries the scheme's chain-specific fields: EVM EIP-3009 → `{ name, version }` (the token's * EIP-712 domain), Solana SVM → `{ feePayer }` (the facilitator's fee-payer pubkey). */ interface FacilitatorPaymentRequirements { scheme: 'exact'; network: string; asset: string; amount: string; payTo: string; maxTimeoutSeconds: number; extra: Record; } /** One (scheme, network) pair a facilitator's `GET /supported` advertises. */ interface FacilitatorSupportedKind { scheme: string; /** As the facilitator reports it — a CAIP-2 id or a slug. */ network: string; /** The fee-payer pubkey when the kind carries one (SVM rails). */ feePayer?: string; /** The kind's x402 envelope version when the facilitator reports it per-kind — e.g. * AEON's `/supported` serves `{ x402Version, scheme, network }`, letting a reader tell * a v1 from a v2 BNB rail. Optional — absent when the facilitator doesn't advertise it. */ x402Version?: number; /** The EVM exact transfer method (`eip3009` / `permit2`) when the facilitator advertises * it in the kind's `extra` — so coverage can tell whether a BNB exact kind is gasless * EIP-3009 or Permit2. Optional — most facilitators (AEON included) omit it. */ assetTransferMethod?: string; } /** * Parse a facilitator `/supported` body into its advertised (scheme, network) kinds. * PURE + tolerant: a malformed body yields `[]`. Mirrors the `{ kinds: [...] }` shape * {@link fetchFacilitatorFeePayer} reads. Useful for verifying coverage before wiring a gate, * and for generating the coverage doc from live reads. */ declare function parseFacilitatorSupported(body: unknown): FacilitatorSupportedKind[]; /** * Read a facilitator's LIVE coverage from `GET /supported`. Best-effort + bounded (an * `AbortController` timeout): NEVER throws — returns `[]` on any failure (same posture as * {@link fetchFacilitatorFeePayer}). Lets an operator/agent ask "does this facilitator * cover my network?" before wiring a gate. Pure `fetch`, no chain libraries (STANDARDS §1). */ declare function facilitatorCoverage(url: string, timeoutMs?: number): Promise; /** A merchant-chosen facilitator: its base URL + optional per-request auth headers. */ interface FacilitatorConfig { /** Base URL, e.g. 'https://x402.org/facilitator' (trailing slash stripped). */ url: string; /** Optional async auth-header provider (e.g. CDP JWT). Omit for the free, no-auth facilitators. */ authHeaders?: () => Promise>; } interface SettleViaFacilitatorInput extends FacilitatorConfig { x402Version: number; /** The decoded PaymentPayload the client sent — forwarded verbatim. */ paymentPayload: Record; /** PaymentRequirements built from the gate's trusted rail (never the client echo). */ paymentRequirements: FacilitatorPaymentRequirements; /** The receipt's network (CAIP-2) + asset/amount/payTo, for building the X402Receipt on success. */ receipt: { network: Caip2; asset: AssetId; payTo: AddressId; amount: string; }; /** authorization.from, for the receipt's `payer`. */ payerHint?: string; } /** * Verify-then-settle a standard `exact` payment through a third-party facilitator. * Returns a {@link VerifyResult}: `{ ok:false, error }` for a client-fixable * facilitator rejection (verify isValid:false, or settle success:false) → 402; * `{ ok:true, receipt }` on a settled payment. THROWS {@link SettlementError} on a * transport/auth failure (non-200) — the gate replies 5xx, never a misleading 402. */ declare function settleViaFacilitator(input: SettleViaFacilitatorInput): Promise; /** * Facilitator coverage — the honest, chain-agnostic DATA map of which third-party x402 * facilitators settle the `exact` scheme on which networks. PURE DATA: imports only the * `Caip2` type from `x402.ts` — zero chain libraries (protocol layer, STANDARDS §1). * * Why it exists: the `exact: true` shorthand (discoverability plan, Phase 7) and an * operator picking a facilitator both need to know "is there a known KEYLESS facilitator * for my chain?" — WITHOUT a hosted registry (charter: no backend). This map is the SEED; * the live truth is `facilitatorCoverage(url)` in `facilitator.ts`, which reads a * facilitator's `GET /supported`. The map is grown ONLY from a verified `/supported` * read — every entry carries a dated verification comment, never a guess. */ /** One facilitator known to settle `exact` on a given network. */ interface KnownFacilitator { /** Base URL (no trailing slash), e.g. `https://facilitator.payai.network`. */ url: string; /** True when it needs NO API key — buyer AND merchant pay zero gas (the facilitator sponsors it). */ keyless: boolean; /** The x402 schemes it settles (today only `exact`). */ schemes: ReadonlyArray<'exact'>; /** The exact transfer methods it can settle on this network. */ settles: ReadonlyArray<'eip3009' | 'permit2' | 'svm' | 'algorand' | 'aptos' | 'near'>; /** A short human note (who it is / caveat). */ note?: string; } /** * Seed map: CAIP-2 network → facilitators that settle `exact` there. Deliberately * CONSERVATIVE — only endpoint-verified entries. Extend it only after a live * `facilitatorCoverage()` read confirms a new (facilitator, network) pair. A merchant on * a network not listed here passes an explicit `exact: { settle: { facilitator } }`. * * NOTE: `x402.org/facilitator` is intentionally ABSENT — it is a Base **Sepolia** testnet * facilitator (verified), not a mainnet rail; seeding it would be a false coverage claim. */ declare const KNOWN_FACILITATORS: Readonly>>; /** Known facilitators for a network — an empty array when none is seeded. */ declare function knownFacilitatorsFor(network: Caip2): ReadonlyArray; /** * The first KEYLESS facilitator that settles `exact` on `network` (optionally for a * specific transfer `method`). Returns `undefined` when none is known — the `exact: true` * shorthand branches on that to throw a coverage-specific guidance error. */ declare function firstKeylessFacilitator(network: Caip2, method?: 'eip3009' | 'permit2' | 'svm' | 'algorand' | 'aptos' | 'near'): KnownFacilitator | undefined; /** * Reliable receipt delivery — the durable webhook a stateless gate can't be. * * `deliverReceipt(receipt, { url, secret })` POSTs a settled {@link PaidReceipt} to * YOUR OWN endpoint, with retries + exponential backoff, an HMAC-SHA256 signature, * and an idempotency key. It is the recommended body of an `onPaid` hook when you * want at-least-once delivery to a webhook instead of a fire-and-forget callback: * * createPaymentGate({ * chain, token, amount, payTo, * awaitOnPaid: true, // record before serving * onPaid: (r) => deliverReceipt(r, { * url: process.env.RECEIPTS_WEBHOOK, * secret: process.env.RECEIPTS_SECRET, // signs the body * }), * }) * * Charter note: PipRail hosts nothing — `url` is **your** endpoint. This is a pure * transport primitive (no chain libraries), isomorphic (global `fetch` + Web Crypto), * and it **never throws**: failures come back as `{ delivered: false, … }`, because a * delivery helper that threw would reintroduce the very crash `onPaid` isolation * prevents. The receiver verifies the signature and dedupes on the idempotency key. */ interface DeliverReceiptOptions { /** Your receiving endpoint. PipRail hosts nothing — this is your server. */ url: string; /** * Shared secret. When set, the raw JSON body is signed HMAC-SHA256 and sent as * `: sha256=` so the receiver can verify authenticity. Signing uses Web * Crypto (`crypto.subtle`) — always present on the supported runtimes (Node ≥ 20, modern * browsers). On a runtime that lacks it the body is sent **unsigned, with no signature header**, * so a receiver must treat a *missing* signature as unauthenticated (reject it), never silently * accept an unsigned body. */ secret?: string; /** Extra retry attempts after the first send (default 5 → up to 6 POSTs total). */ retries?: number; /** Per-attempt timeout in ms (default 10000). An attempt that exceeds it is aborted + retried. */ timeoutMs?: number; /** Extra request headers (merged; never override the signature/idempotency headers). */ headers?: Record; /** Header carrying the signature. Default `piprail-signature`. */ signatureHeader?: string; /** Header carrying the dedupe key (= `receipt.idempotencyKey`). Default `idempotency-key`. */ idempotencyHeader?: string; /** Backoff before retry N (1-based) in ms. Default jittered exponential (cap 30s). */ backoff?: (attempt: number) => number; /** Injected `fetch` (tests / non-global-fetch runtimes). Defaults to the global. */ fetchImpl?: typeof fetch; /** Observe each attempt (logging/metrics). Its own throws are ignored. */ onAttempt?: (info: DeliverAttempt) => void; } /** One delivery attempt's outcome (passed to `onAttempt`). */ interface DeliverAttempt { /** 1-based attempt number. */ attempt: number; /** Did the endpoint return a 2xx? */ ok: boolean; /** HTTP status, when a response was received. */ status?: number; /** Transport/abort error message, when no response was received. */ error?: string; /** Will another attempt follow? */ willRetry: boolean; } /** The terminal result of `deliverReceipt` — it never throws, so always inspect this. */ interface DeliverResult { /** True iff the endpoint returned a 2xx within the attempt budget. */ delivered: boolean; /** Total POSTs made (1 + retries used). */ attempts: number; /** Final HTTP status, when the last attempt got a response. */ status?: number; /** Final error, when delivery failed (last transport error or a non-2xx summary). */ error?: string; } /** * POST a settled receipt to your endpoint with retries, a timeout, an HMAC signature, * and an idempotency key. Never throws — returns a {@link DeliverResult}. */ declare function deliverReceipt(receipt: PaidReceipt, options: DeliverReceiptOptions): Promise; /** * The driver registry — the ONLY place the families meet. Routing decides a * family from the `chain` value (synchronously), then asks that family's * driver to bind the network. Add a family = register a driver here. */ declare function registerDriver(driver: PaymentDriver): void; /** * ── EVM SECTION: x402 `exact` scheme (EIP-3009) — BUYER + SELLER ─────────── * * PipRail's own gates default to the `onchain-proof` scheme (client pays first, * proves with a tx ref, server verifies locally). This module is the standard * x402 `exact` interop, in BOTH directions, EVM + EIP-3009 only, on the existing * `viem` peer (no new dep): * * • BUYER side (client) — {@link payExactEvm} re-derives the token's EIP-712 domain * ON-CHAIN, signs the EIP-3009 authorization, and is wired into `PipRailClient`'s * pay path via the EVM driver's `payExact` SPI (OPT-IN through `schemes: ['exact']`; * the default is unchanged). The lower-level `buildExactAuthorization` / * `encodeXPaymentHeader` codecs remain for hand-rolled/v1 clients. See * `.claude/plans/exact-client/IMPLEMENTATION.md`. * * • SELLER side (gate) — {@link readExactDomain} (read the token's true EIP-712 * domain so the gate can advertise + verify it) and {@link verifyAndSettleExactEvm} * (verify an inbound EIP-3009 authorization locally, then SELF-SETTLE it by * broadcasting `transferWithAuthorization` from the merchant's own relayer key — * no third-party facilitator). This is what lets a PipRail gate get PAID by ANY * standard x402 client. See `.claude/plans/compliance/`. * * Self-settle uses `transferWithAuthorization` (NOT `receiveWithAuthorization`, * which requires `msg.sender == payTo` and so can't be broadcast by a separate * relayer key). The signature binds `to`=payTo, so a front-runner can only push * the same funds to the same payTo and waste their own gas — no redirect risk. */ /** x402 network slug → EVM chain id, for the chains PipRail ships an exact-payable * stablecoin on — EIP-3009 USDC/EURC on most, and **Permit2** on BNB (Binance-Peg * USDC isn't EIP-3009). This is a public REFERENCE/helper, NOT the runtime gate: the * gate offers `exact` on ANY EVM chain whose token is EIP-3009 (detected live via * `exactDomain`) or whose chain has the Permit2 proxy — so keep this list in sync with * what we've VERIFIED, but the rail isn't limited to it. An unknown slug → `null`. * (Matching uses CAIP-2 via `net.supports`.) */ declare const EXACT_NETWORK_SLUGS: Readonly>; /** Resolve an x402 `exact` network slug (e.g. "base") to its EVM chain id. */ declare function chainIdForExactNetwork(slug: string): number | null; /** A parsed x402 `exact` PaymentRequirements entry (the fields we consume). */ interface ExactAccept { scheme: 'exact'; network: string; /** Amount in base units (x402 names this `maxAmountRequired`). */ maxAmountRequired: string; /** EIP-3009 token contract. */ asset: `0x${string}`; payTo: `0x${string}`; maxTimeoutSeconds: number; /** EIP-712 domain of the token (USDC: name 'USD Coin', version '2'). */ extra?: { name?: string; version?: string; }; description?: string; resource?: string; } /** The EIP-3009 authorization the payer signs. */ interface ExactAuthorization { from: `0x${string}`; to: `0x${string}`; value: string; validAfter: string; validBefore: string; nonce: Hex; } /** EIP-712 type set for `transferWithAuthorization` (EIP-3009). */ declare const EIP3009_TYPES: { readonly TransferWithAuthorization: readonly [{ readonly name: "from"; readonly type: "address"; }, { readonly name: "to"; readonly type: "address"; }, { readonly name: "value"; readonly type: "uint256"; }, { readonly name: "validAfter"; readonly type: "uint256"; }, { readonly name: "validBefore"; readonly type: "uint256"; }, { readonly name: "nonce"; readonly type: "bytes32"; }]; }; /** * Parse a standard x402 challenge body into its `exact` requirements. Tolerant * of x402Version 1 or 2 and of the `maxAmountRequired`/`amount` field name. * Returns `[]` when there are no `exact` entries, `null` when the body isn't a * recognisable x402 challenge. */ declare function parseExactRequirements(body: unknown): ExactAccept[] | null; interface BuildExactParams { /** A viem account able to sign EIP-712 typed data. */ account: Account; accept: ExactAccept; /** EVM chain id (must match the token's deployment / EIP-712 domain). */ chainId: number; /** Unix seconds 'now' — injectable for deterministic tests. */ now: number; /** 32-byte hex authorization nonce — injectable for deterministic tests * (use a CSPRNG value in production). */ nonce: Hex; } /** * Build + EIP-712-sign an EIP-3009 `transferWithAuthorization` for an `exact` * requirement. Returns the authorization and its signature; pass both to * {@link encodeXPaymentHeader} to produce the `X-PAYMENT` header value. * * @deprecated Low-level primitive — prefer {@link payExactEvm}, which the client's * `payExact` SPI uses. This helper TRUSTS the server-supplied `accept.extra.{name, * version}` for the EIP-712 domain (a lying/absent value yields a silently-invalid * signature) and calls `account.signTypedData` directly (which is `undefined` on a * bring-your-own JsonRpcAccount). `payExactEvm` re-derives the domain on-chain and * signs via the wallet client. Kept exported as a deterministic test/codec building block. */ declare function buildExactAuthorization(params: BuildExactParams): Promise<{ authorization: ExactAuthorization; signature: Hex; }>; /** * Encode an x402 `exact` PaymentPayload into an `X-PAYMENT` header value — the * **v1 flat shape** (`{ x402Version, scheme, network, payload }`). This is a * client-side UTILITY (PipRail's own client pays only `onchain-proof`, never this); * the v1 flat shape is what Coinbase's original reference emits and is still accepted * by every x402 gate + facilitator, so `x402Version` defaults to `1` to stay * INTERNALLY CONSISTENT with that shape (emitting `2` here would mislabel a v1 body — * a proper v2 `exact` payload is the nested `accepted`-envelope shape instead). See * the version-posture note in `x402.ts`. */ declare function encodeXPaymentHeader(input: { network: string; authorization: ExactAuthorization; signature: Hex; /** x402 envelope version. Defaults to `1` — consistent with this flat shape. */ x402Version?: number; }): string; /** * The minimal EIP-3009 ABI the seller needs: both `transferWithAuthorization` * overloads (the 65-byte `(v,r,s)` form and the `(bytes signature)` ERC-1271 form), * the `authorizationState` replay check, and `name()`/`version()` for the EIP-712 * domain. Mirrors Circle's deployed FiatToken. */ declare const eip3009Abi: readonly [{ readonly type: "function"; readonly name: "transferWithAuthorization"; readonly stateMutability: "nonpayable"; readonly outputs: readonly []; readonly inputs: readonly [{ readonly name: "from"; readonly type: "address"; }, { readonly name: "to"; readonly type: "address"; }, { readonly name: "value"; readonly type: "uint256"; }, { readonly name: "validAfter"; readonly type: "uint256"; }, { readonly name: "validBefore"; readonly type: "uint256"; }, { readonly name: "nonce"; readonly type: "bytes32"; }, { readonly name: "v"; readonly type: "uint8"; }, { readonly name: "r"; readonly type: "bytes32"; }, { readonly name: "s"; readonly type: "bytes32"; }]; }, { readonly type: "function"; readonly name: "transferWithAuthorization"; readonly stateMutability: "nonpayable"; readonly outputs: readonly []; readonly inputs: readonly [{ readonly name: "from"; readonly type: "address"; }, { readonly name: "to"; readonly type: "address"; }, { readonly name: "value"; readonly type: "uint256"; }, { readonly name: "validAfter"; readonly type: "uint256"; }, { readonly name: "validBefore"; readonly type: "uint256"; }, { readonly name: "nonce"; readonly type: "bytes32"; }, { readonly name: "signature"; readonly type: "bytes"; }]; }, { readonly type: "function"; readonly name: "authorizationState"; readonly stateMutability: "view"; readonly inputs: readonly [{ readonly name: "authorizer"; readonly type: "address"; }, { readonly name: "nonce"; readonly type: "bytes32"; }]; readonly outputs: readonly [{ readonly type: "bool"; }]; }, { readonly type: "function"; readonly name: "name"; readonly stateMutability: "view"; readonly inputs: readonly []; readonly outputs: readonly [{ readonly type: "string"; }]; }, { readonly type: "function"; readonly name: "version"; readonly stateMutability: "view"; readonly inputs: readonly []; readonly outputs: readonly [{ readonly type: "string"; }]; }, { readonly type: "function"; readonly name: "DOMAIN_SEPARATOR"; readonly stateMutability: "view"; readonly inputs: readonly []; readonly outputs: readonly [{ readonly type: "bytes32"; }]; }]; /** * Read an EIP-3009 token's true EIP-712 domain `{ name, version }` from the contract — what a * payer must sign over. Returns `null` if the asset is NOT an EIP-3009 token (no * `authorizationState` — e.g. USDT, native coin, a plain ERC-20), so the caller falls back to * permit2 / onchain-proof. * * The name is NEVER assumed from the symbol: canonical USDC's domain name is "USD Coin" (not * "USDC"), and EURC's is "Euro Coin" on Ethereum/Avalanche but "EURC" on Base — only the on-chain * read is authoritative. * * The version comes from `version()` when the token exposes it (canonical FiatToken — USDC is * "2"). Many EIP-3009 tokens HARDCODE the domain version and DON'T expose `version()` — e.g. * **FDUSD and USD1 on BNB Chain** (both "1"). For those we DERIVE the version by matching the * token's on-chain `DOMAIN_SEPARATOR()` against the standard 4-field domain for a small set of * common versions; a token with a non-standard / salted domain returns `null` (→ permit2 fallback). */ declare function readExactDomain(publicClient: PublicClient, asset: string): Promise<{ name: string; version: string; } | null>; /** * ── EVM SECTION: x402 `exact` scheme, `permit2` variant — BUYER + SELLER ────── * * The companion to `exact.ts` for ERC-20 tokens that do NOT implement EIP-3009 * `transferWithAuthorization` — most notably **Binance-Peg USDC/USDT on BNB Chain** * (no native Circle USDC exists on BNB). The x402 `exact` EVM scheme defines a second * `assetTransferMethod`, `"permit2"`, for exactly these tokens: * * • The payer signs an EIP-712 **`PermitWitnessTransferFrom`** over the canonical * **Permit2** contract, with `spender` = the canonical **x402ExactPermit2Proxy** * and a **witness** that binds the recipient (`to`) + an activation time * (`validAfter`). The buyer never broadcasts (and, after a one-time Permit2 approval, * spends ~0 gas) — the merchant/facilitator broadcasts. * • The merchant/facilitator settles by calling the **proxy's `settle(...)`**, which * calls `Permit2.permitWitnessTransferFrom`. Because the signed `spender` is the * proxy and the proxy forces `transferDetails.to == witness.to`, a relayer can only * push the signed funds to the signed recipient — no redirect risk (same guarantee * EIP-3009's `to`-binding gives us). * * Spec: github.com/coinbase/x402 `specs/schemes/exact/scheme_exact_evm.md` (the Permit2 * method). Both contracts are canonical CREATE2 deployments (same address every EVM * chain) and are verified deployed on BNB. This module mirrors `exact.ts` file-for-file * (buyer {@link payPermit2Evm}; seller {@link verifyAndSettlePermit2Evm}); the EVM * driver routes to it on `accept.extra.assetTransferMethod === 'permit2'`. * * One-time setup: the payer must `approve(Permit2, max)` on the token ONCE * ({@link ensurePermit2Allowance} does this lazily) — after that, every payment is a * gasless signature. This is the only way `permit2` differs operationally from EIP-3009. */ /** Canonical Permit2 (Uniswap), same address on every EVM chain incl. BNB. */ declare const PERMIT2_ADDRESS: "0x000000000022D473030F116dDEE9F6B43aC78BA3"; /** Canonical x402ExactPermit2Proxy — the SAME CREATE2 address on every chain where it's been * deployed (see {@link PERMIT2_PROXY_CHAIN_IDS}; it is NOT on every EVM chain). It is the * `spender` the buyer signs over, and the contract the seller settles through; it enforces * `transferDetails.to == witness.to`, so funds can only reach the signed payTo. */ declare const X402_EXACT_PERMIT2_PROXY: "0x402085c248EeA27D92E8b30b2C58ed07f9E20001"; /** EVM chain ids where BOTH the canonical Permit2 AND the x402ExactPermit2Proxy are deployed — * i.e. where the **Permit2** transfer method of `exact` can actually settle. Verified on-chain * (`eth_getCode`, 2026-06-11). EIP-3009 needs NO proxy, so this gates only the Permit2 fallback; * on a non-EIP-3009 token on a chain absent here, the gate offers `onchain-proof` only rather * than an unsettleable Permit2 rail. The proxy is a permissionless CREATE2 deploy, so extend * this as it lands on more chains (re-verify before adding). */ declare const PERMIT2_PROXY_CHAIN_IDS: ReadonlySet; /** Whether a chain has the x402 Permit2 proxy deployed (→ can settle the Permit2 exact method). */ declare function isPermit2ProxyChain(chainId: number): boolean; /** * EIP-712 type set for the x402 `permit2` exact method. MUST encode to exactly the type * string Permit2 reconstructs for `permitWitnessTransferFrom`: * `PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,Witness witness)TokenPermissions(address token,uint256 amount)Witness(address to,uint256 validAfter)` * (viem orders referenced structs alphabetically → TokenPermissions before Witness ✓.) */ declare const PERMIT2_WITNESS_TYPES: { readonly PermitWitnessTransferFrom: readonly [{ readonly name: "permitted"; readonly type: "TokenPermissions"; }, { readonly name: "spender"; readonly type: "address"; }, { readonly name: "nonce"; readonly type: "uint256"; }, { readonly name: "deadline"; readonly type: "uint256"; }, { readonly name: "witness"; readonly type: "Witness"; }]; readonly TokenPermissions: readonly [{ readonly name: "token"; readonly type: "address"; }, { readonly name: "amount"; readonly type: "uint256"; }]; readonly Witness: readonly [{ readonly name: "to"; readonly type: "address"; }, { readonly name: "validAfter"; readonly type: "uint256"; }]; }; /** * ── EVM SECTION: x402 `upto` scheme (Permit2) — BUYER + SELLER ──────────────── * * The metered / variable-amount counterpart to `permit2.ts`. The x402 `upto` scheme lets * a buyer authorize a MAXIMUM (sign a Permit2 `PermitWitnessTransferFrom` for the ceiling), * and the merchant settle the ACTUAL (≤ max) AFTER serving — billing exactly what was * consumed (LLM tokens, bytes, compute). EVM-Permit2 ONLY (the spec bans EIP-3009, which * fixes the amount at sign time, and has no non-EVM variant). * * • The payer signs an EIP-712 `PermitWitnessTransferFrom` over the canonical **Permit2** * contract, with `spender` = the canonical **x402UptoPermit2Proxy** and a **witness** * `{ to, facilitator, validAfter }` — `facilitator` is the **MIDDLE** field, the one * delta from the exact Permit2 witness. The buyer never broadcasts (and, after a * one-time Permit2 approval, spends ~0 gas) — the merchant broadcasts. * • The merchant SELF-SETTLES by calling the proxy's `settle(permit, amount, owner, * witness, signature)` — `amount` (position 1) is the ACTUAL (≤ the signed max), and the * proxy enforces `msg.sender == witness.facilitator` on-chain (`UnauthorizedFacilitator`) * PLUS `amount <= permit.permitted.amount` (`AmountExceedsPermitted`). So the merchant's * own relayer IS the bound facilitator — backendless, no third-party facilitator. * * Spec: `specs/schemes/upto/scheme_upto_evm.md` + `contracts/evm/src/x402UptoPermit2Proxy.sol` * (witness type string `Witness(address to,address facilitator,uint256 validAfter)` and the * `settle(...)` arg order both verdict-confirmed against the cloned source). This module * mirrors `permit2.ts` file-for-file (buyer {@link payUptoEvm}; seller * {@link verifyAndSettleUptoEvm}); the EVM driver routes to it via the new upto SPI trio. * * One-time setup is identical to the exact Permit2 rail — the payer `approve(Permit2, max)` * ONCE ({@link ensurePermit2Allowance}, re-used from `permit2.ts`). * * NOT the sponsor-fee-drain class (the relayer broadcasts on EVM and sets gas at broadcast; * the buyer's signed witness carries NO fee field) — add NO MAX_FEE cap, exactly like the * exact-EIP-3009 / exact-Permit2 rails. */ /** Canonical x402UptoPermit2Proxy — the SAME CREATE2 address on every chain where it's been * deployed (see {@link UPTO_PROXY_CHAIN_IDS}; vanity `…0002`, vs the exact proxy's `…0001`). * It is the `spender` the buyer signs over and the contract the seller settles through; it * enforces `transferDetails.to == witness.to`, `msg.sender == witness.facilitator`, and * `amount <= permit.permitted.amount` on-chain. */ declare const X402_UPTO_PERMIT2_PROXY: "0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002"; /** EVM chain ids where BOTH the canonical Permit2 AND the x402UptoPermit2Proxy are deployed — * i.e. where the `upto` scheme can actually settle. Verdict-confirmed on-chain (`eth_getCode`, * ~3142-byte byte-identical code, 2026-06-20): Ethereum / Base / Arbitrum / Optimism / Polygon * / BNB. **Avalanche (43114) is NOT deployed (`0x`)** — deliberately OUT. This is INDEPENDENT of * the exact `PERMIT2_PROXY_CHAIN_IDS` (a separate proxy). The proxy is a permissionless CREATE2 * deploy, so extend this as it lands on more chains (re-verify each with `eth_getCode` first). */ declare const UPTO_PROXY_CHAIN_IDS: ReadonlySet; /** Whether a chain has the x402UptoPermit2Proxy deployed (→ can settle the `upto` scheme). */ declare function isUptoProxyChain(chainId: number): boolean; /** * EIP-712 type set for the x402 `upto` scheme. Identical to {@link PERMIT2_WITNESS_TYPES} * (the exact Permit2 rail) EXCEPT the `Witness` struct carries a `facilitator` field as its * **MIDDLE** member. MUST encode to exactly the type string the proxy reconstructs: * `PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,Witness witness)TokenPermissions(address token,uint256 amount)Witness(address to,address facilitator,uint256 validAfter)` * (viem orders referenced structs alphabetically → TokenPermissions before Witness ✓.) * **`facilitator` is the MIDDLE field** — get the order wrong and `recoverTypedDataAddress` * silently returns the wrong address → every signature rejects. Verdict-confirmed against * `x402UptoPermit2Proxy.sol` WITNESS_TYPEHASH + the reference `uptoPermit2WitnessTypes`. */ declare const PERMIT2_UPTO_WITNESS_TYPES: { readonly PermitWitnessTransferFrom: readonly [{ readonly name: "permitted"; readonly type: "TokenPermissions"; }, { readonly name: "spender"; readonly type: "address"; }, { readonly name: "nonce"; readonly type: "uint256"; }, { readonly name: "deadline"; readonly type: "uint256"; }, { readonly name: "witness"; readonly type: "Witness"; }]; readonly TokenPermissions: readonly [{ readonly name: "token"; readonly type: "address"; }, { readonly name: "amount"; readonly type: "uint256"; }]; readonly Witness: readonly [{ readonly name: "to"; readonly type: "address"; }, { readonly name: "facilitator"; readonly type: "address"; }, { readonly name: "validAfter"; readonly type: "uint256"; }]; }; /** * Landing page — a tiny, self-contained HTML representation of a 402, for the HUMAN * who opens a gated URL in a browser (agents/crawlers still get the unchanged JSON 402). * PURE string templating; imports only the `SelfDescription` type — zero I/O, zero chain * libraries (protocol layer, STANDARDS §1). * * The SDK NEVER serves this itself (it's headless by charter). The merchant opts in by * branching on the request's `Accept` header in their own handler and returning this * string with `content-type: text/html`. See `gate.landingPage(challenge)`. * * SECURITY: every interpolated field is HTML-escaped (a `payTo` / instruction can be * merchant- or rail-influenced) — global rule: escape all user-/data-influenced content. */ /** * Render a self-describing HTML 402 landing page from a {@link SelfDescription}. Leads with the * one-line instruction and a prominent CAUTION — payment must go through an x402 client; a manual * send to the address won't unlock the resource and isn't matched to the request (it lands in the * merchant's wallet uncredited). Then the primary "How to pay" (the `npm i @piprail/sdk` install + * paste-ready snippet + MCP command), and finally the per-rail "Payment details" table (scheme · * chain · amount · settles-to) for transparency. Fully static — no script, no external asset. */ declare function renderLandingPage(sd: SelfDescription): string; /** * Minimal, DUCK-TYPED Agent2Agent (A2A) shapes — the structural surface the PipRail A2A * transport reads/writes, and NOTHING more. Like `ExpressLikeRequest` in `server.ts`, these * declare only the fields the adapter touches, so any A2A runtime's objects (the official * `@a2a-js/sdk` — `a2aproject` org, the Linux Foundation A2A project — ADK, or a hand-rolled * JSON-RPC handler) satisfy them structurally — **with ZERO `@a2a` dependency**. * * x402-over-A2A carries PipRail's existing `PaymentRequired`/`PaymentPayload`/ * `SettlementResponse` envelopes inside A2A `Task`/`Message` `metadata`, keyed off five * namespaced keys (`x402.payment.{status,required,payload,receipts,error}`), so a * standard A2A reader ignores them and they coexist with any other metadata. * * Verified verbatim against the upstream reference `x402_a2a/types/state.py` and the * x402-foundation `specs/transports-v2/a2a.md` mapping table. */ /** The coarse A2A Task state (the A2A framework's own lifecycle). */ type A2ATaskState = 'submitted' | 'working' | 'input-required' | 'completed' | 'failed' | 'canceled'; /** The granular `x402.payment.status` lifecycle field (the payment state machine atop the * Task state). Per `transports-v2/a2a.md:217-224` + the reference `PaymentStatus` enum. */ type A2APaymentStatus = 'payment-required' | 'payment-submitted' | 'payment-verified' | 'payment-completed' | 'payment-failed' | 'payment-rejected'; /** A part of an A2A Message — text, data, or file. Only the fields the adapter reads/writes. */ interface A2APart { kind?: 'text' | 'data' | 'file' | string; text?: string; data?: unknown; [extra: string]: unknown; } /** An A2A Artifact — the merchant's served work attached to a completed Task. The adapter * attaches `fulfill()`'s output here, plus (B7) an optional post-settle error annotation. */ interface A2AArtifact { artifactId?: string; name?: string; parts?: A2APart[]; /** Free-form annotation bag. The B7 fulfill-throws-after-settle edge stamps a post-settle * error here (NEVER re-challenge / failed) so the buyer sees the work failed AFTER paying. */ metadata?: Record; [extra: string]: unknown; } /** * The five canonical x402 metadata keys (namespaced `x402.payment.*`) plus structural * tolerance for any other A2A metadata. The shapes are PipRail's EXISTING wire objects — * `x402.payment.required` is a raw `X402Challenge` (NOT base64), `x402.payment.payload` is * a raw PaymentPayload object (fed to the tolerant object-cores via `gate.verifyObject`), * and `x402.payment.receipts` is the append-only history. */ interface A2AMetadata { 'x402.payment.status'?: A2APaymentStatus; 'x402.payment.required'?: X402Challenge; /** A raw `X402PaymentSignature` / exact / upto payload object — fed straight into * `gate.verifyObject` (the tolerant object-cores absorb v1+v2). */ 'x402.payment.payload'?: unknown; /** * B7: append-only settlement history. The element type is the UNION because the array * MUST include FAILED attempts (`spec v0.1:281`) — and `X402Receipt.success` is the literal * `true`, so a failed entry can only be a `SettleOutcome { success:false, errorReason }`. */ 'x402.payment.receipts'?: (X402Receipt | SettleOutcome)[]; /** The error code on a settlement-side failure (`x402.payment.error`). */ 'x402.payment.error'?: string; [extra: string]: unknown; } /** An inbound/outbound A2A Message. The submission carries the payload + the original * `taskId` for correlation (`v0.1 spec.md:163`). Only the fields the adapter touches. */ interface A2AMessage { kind?: 'message'; role?: string; /** Correlates a follow-up payment submission to its in-flight payment Task. */ taskId?: string; messageId?: string; parts?: A2APart[]; metadata?: A2AMetadata; [extra: string]: unknown; } /** An A2A Task — the merchant's response carrying the payment state in `status.message.metadata`. */ interface A2ATask { kind?: 'task'; id: string; status: { state: A2ATaskState; message?: A2AMessage; }; artifacts?: A2AArtifact[]; [extra: string]: unknown; } /** * An AgentCard extension declaration — what the merchant stamps into its * `AgentCard.capabilities.extensions[]` to advertise the x402 extension (§2.4). A client * opts in by sending `X-A2A-Extensions: `; `required: true` rejects un-activated callers. */ interface A2AExtensionDeclaration { uri: string; description?: string; required?: boolean; params?: Record; } /** * A bounded, pluggable TRANSPORT-LIFECYCLE store — NOT a backend, NOT on the verification * path, holds NO security state (B4). It correlates a follow-up `message/send` to its * in-flight Task and accumulates the append-only `receipts[]` history. The buyer carries * the challenge nonce in its payload; `gate.verify`/`gate.verifyObject` re-derive every * trusted field from the merchant's own config (`server.ts` client-echoed nonce), so even * a poisoned/empty store can never redirect funds or reject a valid payment. Same posture * as the gate's in-memory replay set — a bounded Map inside the merchant's own process. */ interface A2ATaskStore { get(taskId: string): A2ATaskRecord | undefined; set(taskId: string, value: A2ATaskRecord, ttlMs: number): void; } /** What a {@link A2ATaskStore} holds per task — the receipt history ONLY (no nonce-as-security). */ interface A2ATaskRecord { receipts?: (X402Receipt | SettleOutcome)[]; } /** * x402-over-A2A — the SELLER-side transport adapter for the Agent2Agent (A2A) protocol. * * A2A is an open protocol (created by Google, now a Linux Foundation project — a2a-protocol.org; * official JS runtime `@a2a-js/sdk` from the `a2aproject` org). It is x402's third official transport * (alongside HTTP and MCP). It carries PipRail's * BYTE-IDENTICAL `PaymentRequired`/`PaymentPayload`/`SettlementResponse` envelopes inside * A2A `Task`/`Message` JSON-RPC `metadata` (five namespaced `x402.payment.*` keys) keyed * off a coarse A2A Task state — instead of base64 HTTP headers. It is a thin codec + adapter * ABOVE the `PaymentDriver` boundary: ZERO driver changes, ZERO scheme changes, ZERO chain * changes — all 10 chain families work over A2A for free, exactly as they work over HTTP. * * This module imports ONLY `server.ts`/`x402.ts` types + `errors.ts` (the same surface as * the Express adapter) — NO `viem`, NO chain SDK. It never touches a chain. * * ── Charter-critical invariants ─────────────────────────────────────────────── * • Defaults byte-identical: a root install that never imports A2A loads nothing extra. * • B4 — NO new verification state: the buyer carries the challenge nonce in its payload; * `gate.verifyObject` reads `sig.payload.nonce` exactly as HTTP does, and the gate * re-derives every trusted field from the merchant's own config. The `A2ATaskStore` is * transport task-lifecycle ONLY (receipt history), never on the verification path. * • B5 — HTTP and A2A MUST share ONE replay set: `createA2APaymentHandler` takes an * EXISTING `PaymentGate` (the primary, ergonomic form). A co-resident HTTP gate that * builds a SEPARATE gate must share `isUsed`/`markUsed`, or a proof settled over one * transport can be replayed over the other. * • B7 — `receipts[]` is the UNION `(X402Receipt | SettleOutcome)[]` (it carries FAILED * attempts); a fulfill-throws-AFTER-settle ends the Task `completed` with the successful * receipt + an error annotation (NEVER re-challenge / failed — the money already moved). * • Emit `x402Version: 2` — RESOLVED 2026-06-21: v2 IS the live x402 standard. A live cross-check * against Google's official libs (reproducible harness in `examples/a2a-interop/`) showed the canonical * `x402` lib (2.13.1, V2: `amount` / CAIP-2 / nested `accepted`) parses our PaymentRequired + * PaymentPayload byte-identically, while the legacy v0.1 `x402_a2a` package (x402Version 1, * `maxAmountRequired`, chain slugs) is bitrotted. Inbound v1 object-cores are absorbed for the * standard `exact` scheme; PipRail-native `onchain-proof` is always v2 (never sent v1-flat). * • Merchant statuses are spec-bounded: `payment-required` (a genuine first/no-proof challenge) / * `payment-completed` / `payment-failed`. A SUBMITTED proof that fails verification (expired / * wrong-amount / replayed) emits `payment-failed` — the a2a.md §9 rule ("If a payment fails, the * server MUST set x402.payment.status to payment-failed") and its EXPIRED-signature example, which * Google's reference executor matches verbatim (`is_valid == false → record_payment_failure → * PAYMENT_FAILED`). Retryability rides on the A2A Task `state` (`input-required`), NOT the status — * so a rejection is `payment-failed` + `input-required` (retry) while a terminal settlement error is * `payment-failed` + `failed`. `payment-rejected` + `payment-submitted` are CLIENT→merchant statuses * we never emit. * * ── DEFERRED (NOT built here — see the x402-parity/03-a2a-transport plan) ────── * • Phase 4 — the A2A BUYER (`A2APayer`): the HTTP buyer mints the payload that rides A2A today. * • Phase 5 — AP2 `CartMandate`/`PaymentMandate` carriage (the Embedded Flow; no mandate-trust machinery). * • B8 ship-gate — RESOLVED at the wire level (above). A live `adk-demo` AGENT run is blocked by that * package's dependency bitrot (won't import on current `x402`/`a2a-sdk`), not by PipRail. */ /** The x402 A2A extension URI the x402-foundation doc + ALL Google reference code cite * (`config.py:23`, `version="0.1"`). The seller's default — activation is exact-string match. */ declare const A2A_X402_EXTENSION_URI_V01 = "https://github.com/google-a2a/a2a-x402/v0.1"; /** The newer v0.2 URI (`spec/v0.2/spec.md:11`) — for AP2 Embedded-Flow targets (Phase 5, deferred). */ declare const A2A_X402_EXTENSION_URI_V02 = "https://github.com/google-agentic-commerce/a2a-x402/blob/main/spec/v0.2"; /** The five canonical metadata keys (verbatim from `x402_a2a/types/state.py:33-37`). */ declare const A2A_STATUS_KEY = "x402.payment.status"; declare const A2A_REQUIRED_KEY = "x402.payment.required"; declare const A2A_PAYLOAD_KEY = "x402.payment.payload"; declare const A2A_RECEIPTS_KEY = "x402.payment.receipts"; declare const A2A_ERROR_KEY = "x402.payment.error"; /** The HTTP activation header a client sends to opt into the extension (§2.4). */ declare const A2A_EXTENSIONS_HEADER = "X-A2A-Extensions"; /** * Map PipRail's lowercase {@link VerifyErrorCode} to the spec's screaming-snake error enum * (`errors.py:148-157` / spec §8.1) for `x402.payment.error`. We ALSO emit the raw PipRail * code (in the re-challenge's `extensions.piprail`), so nothing is lost and a buyer agent * branches identically across transports. Codes with no spec analogue map to `SETTLEMENT_FAILED` * only when they're settlement-side; rejection codes keep their nearest enum member. */ declare const VERIFY_CODE_TO_A2A_ERROR: Record; /** Translate a PipRail verify/settle code to the A2A error enum (falls back to the raw code). */ declare function toA2AErrorCode(code: string): string; /** * Build the `input-required` payment-request Task carrying the challenge as RAW JSON * (NOT base64) in `x402.payment.required` + `x402.payment.status = 'payment-required'`. * `parts` are any human-readable message parts the merchant wants alongside it. * * Returns a **transport-metadata** Task: it carries the x402 `payment` state, NOT the host-owned * ids the `@a2a-js/sdk` runtime requires (`Task.contextId`, `status.message.messageId`/`parts`, * `artifacts[].artifactId` are all REQUIRED in the SDK's types). The host adapter stamps those * before publishing — see the `@a2a-js/sdk` mount in the docs / `examples/a2a-server/lib.mjs`. * This keeps the protocol layer chain- and runtime-agnostic (zero `@a2a` dependency). */ declare function toA2APaymentRequired(taskId: string, challenge: X402Challenge, parts?: A2APart[]): A2ATask; /** Build the `x402.payment.receipts` metadata block (an ARRAY, append-only, B7 union). */ declare function toA2APaymentReceipts(receipts: (X402Receipt | SettleOutcome)[]): A2AMetadata; /** * Build the `payment-failed` metadata for a SETTLEMENT-side failure (the money never moved): * the A2A error code + a `{ success:false, errorReason }` receipt appended to the history (B7). */ declare function toA2APaymentFailed(code: VerifyErrorCode | string, detail: string, receipts?: (X402Receipt | SettleOutcome)[], network?: string): A2AMetadata; /** Read the `X402Challenge` back out of a payment-request Task's metadata (raw JSON). */ declare function fromA2APaymentRequired(task: A2ATask): X402Challenge | null; /** * Read the inbound RAW payment payload object + its correlating `taskId` out of a * `message/send`. The raw object is fed straight into `gate.verifyObject` (the tolerant * object-cores absorb v1+v2). Returns `null` when there's no payload metadata. */ declare function fromA2APaymentPayload(message: A2AMessage): { raw: unknown; taskId: string; } | null; /** Options for {@link createA2APaymentHandler}. */ interface A2APaymentHandlerOptions extends Partial { /** * B5 (MANDATORY cross-transport replay): pass the SAME {@link PaymentGate} instance the * HTTP path uses, so both transports share ONE replay set (`localUsed` / injected * `isUsed`/`markUsed`). This is the PRIMARY, ergonomic form. If omitted, a fresh gate is * built from the inline `RequirePaymentOptions` — and a co-resident HTTP gate MUST then * share `isUsed`/`markUsed`, or a proof settled over one transport can be replayed over * the other. */ gate?: PaymentGate; /** * A bounded, pluggable TRANSPORT-lifecycle store (B4) — correlate a follow-up * `message/send` to its in-flight Task + accumulate the append-only `receipts[]`. It is * NOT on the verification path and holds NO nonce-as-security-state (the buyer carries the * nonce in its payload). Default = an in-memory TTL Map (same bound as the replay set). * NOT a backend PipRail hosts. */ taskStore?: A2ATaskStore; /** TTL (ms) for the default task store. Default = `maxTimeoutSeconds * 1000` (the replay window). */ taskTtlMs?: number; /** * Produce the served result Artifact(s) for a settled task — the merchant's own work (the * image, the JSON, …). Charter-safe: like an Express route handler that runs after `next()`. * Omit for a metadata-only "payment accepted" completion. **B7: if this THROWS after a * successful settle, the Task still completes `completed` carrying the success receipt + an * error annotation — the buyer is NEVER told to re-pay an already-settled proof.** */ fulfill?: (ctx: { taskId: string; receipt: X402Receipt; message: A2AMessage; }) => Promise | A2AArtifact[]; } /** The seller handler returned by {@link createA2APaymentHandler}. */ interface A2APaymentHandler { /** * Process one inbound A2A message and return the next Task. Outcomes mirror * `gate.verify()`'s `VerifyPaymentResult` exactly: * - no payload yet → Task `input-required` + `x402.payment.required` * - payload, verified+settled → Task `completed` + `x402.payment.receipts` + artifacts * - payload, rejected → Task `input-required` re-challenge, status `payment-failed` (RETRYABLE) * - settle threw (relayer) → Task `failed` + `x402.payment.error` (NOT retryable) * - settle OK but fulfill threw → Task `completed` + receipt + error annotation (B7) * * The returned Task is **transport metadata only** — it carries the x402 `payment.*` state but NOT * the host-owned ids the `@a2a-js/sdk` runtime requires (`Task.contextId`, `status.message.messageId` * /`parts`, `artifacts[].artifactId`). The host adapter (the `AgentExecutor`) stamps those before * `bus.publish` — see the mount in the docs / `examples/a2a-server/lib.mjs`. This keeps the protocol * layer free of any `@a2a` dependency. */ handleMessage(message: A2AMessage, taskId?: string): Promise; /** Stamp the x402 extension into an AgentCard's `capabilities.extensions` (§2.4). */ agentCardExtension(opts?: { required?: boolean; version?: 'v0.1' | 'v0.2'; }): A2AExtensionDeclaration; /** The underlying gate — escape hatch for advanced flows (`describe()`, `landingPage()`). */ readonly gate: PaymentGate; } /** * The A2A analogue of `requirePayment` — wrap a {@link PaymentGate} and map A2A messages ⇄ * x402 task metadata. The merchant plugs `handleMessage` into their A2A agent's * `message/send` handler and pushes `agentCardExtension()` into their AgentCard. * * @example * ```ts * import { createPaymentGate, createA2APaymentHandler } from '@piprail/sdk' * * const gate = createPaymentGate({ chain: 'base', token: 'USDC', amount: '0.05', payTo: '0x…' }) * const pay = createA2APaymentHandler({ gate, fulfill: async () => [{ name: 'result', parts: [{ kind: 'text', text: 'done' }] }] }) * a2aAgent.on('message/send', ({ message, taskId }) => pay.handleMessage(message, taskId)) * ``` */ declare function createA2APaymentHandler(options: A2APaymentHandlerOptions): A2APaymentHandler; /** * Minimal, DUCK-TYPED MCP message shapes — the structural surface the PipRail MCP transport * reads/writes, and NOTHING more. Like `a2a-types.ts`, these declare only the fields the adapter * touches, so any MCP runtime's objects (the `@modelcontextprotocol/sdk` low-level `Server`, a * hand-rolled tool handler) satisfy them structurally — **with ZERO `@modelcontextprotocol/sdk` * dependency** (the SDK transport stays chain- AND runtime-agnostic; the `@piprail/mcp` wiring may * use the real MCP types). * * x402-over-MCP carries PipRail's EXISTING `X402Challenge` / PaymentPayload / `SettleOutcome` * envelopes over MCP **tool calls**: a 402 challenge as an `isError` tool result (the * PaymentRequired in `structuredContent` AND a byte-equal `content[0].text`), the payment under * the call's `params._meta["x402/payment"]`, the settlement under the result's * `_meta["x402/payment-response"]`. Verified against the x402-foundation * `specs/transports-v2/mcp.md` binding. */ /** The `_meta` key strings — EXACTLY as the spec defines them (a SLASH, not a dot — distinct from * A2A's `x402.payment.*` dotted keys). Exported so the conformance test pins them. */ declare const MCP_PAYMENT_META_KEY = "x402/payment"; declare const MCP_PAYMENT_RESPONSE_META_KEY = "x402/payment-response"; /** A single MCP content block (only the text block the binding uses). */ interface McpContentBlock { type: 'text' | string; text?: string; [extra: string]: unknown; } /** An inbound tool-call's params — only the fields the seller reads. `_meta["x402/payment"]` * carries the raw PaymentPayload object (fed straight to `gate.verifyObject`). */ interface McpToolCallParams { name?: string; arguments?: Record; _meta?: { 'x402/payment'?: unknown; [extra: string]: unknown; }; [extra: string]: unknown; } /** A tool result — the seller produces it; the buyer reads it. The 402 challenge is an `isError` * result with `structuredContent` + a byte-equal `content[0].text`; a settlement carries * `_meta["x402/payment-response"]`. */ interface McpToolResult { content: McpContentBlock[]; isError?: boolean; structuredContent?: Record; _meta?: { 'x402/payment-response'?: SettleOutcome; [extra: string]: unknown; }; [extra: string]: unknown; } /** The PaymentPayload value carried under `params._meta["x402/payment"]` (raw JSON, not base64) — * `{ x402Version, resource?, accepted, payload }`, where `accepted` is the chosen `accepts[]` * entry and `payload` is the scheme payload (both consumed by `gate.verifyObject`). */ interface McpPaymentMeta { x402Version: number; resource?: { url: string; description?: string; mimeType?: string; }; accepted: unknown; payload: unknown; } /** * x402-over-MCP — the third official x402 transport (after HTTP and A2A), carrying PipRail's * EXISTING x402 envelopes over MCP **tool calls** instead of HTTP headers or A2A task metadata: * * - a 402 challenge is an `isError` tool result with the `X402Challenge` (the spec's * PaymentRequired) in `structuredContent` AND a byte-equal `content[0].text`; * - the payment rides in the call's `params._meta["x402/payment"]`; * - the settlement rides in the result's `_meta["x402/payment-response"]`. * * It is the conspicuous missing twin of the A2A transport. A thin re-keying of the SDK's verify * onto the MCP message shape — the SELLER calls the EXISTING `gate.verifyObject` (shared replay * set, re-derive-every-field-from-the-trusted-accept), so this transport writes NO crypto and * touches NO driver: every family rides it for free, exactly as over HTTP/A2A. * * PURE protocol layer (STANDARDS §1): imports only `server.ts`/`x402.ts`/`errors.ts` + the * duck-typed `./mcp-types.js` + the A2A error map — ZERO chain libs, ZERO `@modelcontextprotocol/sdk`. * * Scope: this ships the SELLER (`createMcpPaymentTool`) + the full pure codec + the buyer-side * READ/FRAME helpers (`fromMcpPaymentRequired` / `fromMcpPaymentResponse` / `buildMcpPaymentMeta`). * A buyer pays the challenge via the existing policy-gated client/gate machinery and frames the * result with `buildMcpPaymentMeta`; a fully-automatic `McpPayer` (driving the client pay path) is * a documented fast-follow, exactly as A2A shipped seller-first. */ /** * Build the 402 challenge tool result: `isError: true` + the `X402Challenge` in * `structuredContent` AND a **byte-equal** `content[0].text` (`text === JSON.stringify(challenge)` * — the spec requires the two to match; a divergence is a silent interop break, guarded by the * conformance test). PipRail's `X402Challenge` IS the spec's PaymentRequired object — no transform. */ declare function toMcpPaymentRequired(challenge: X402Challenge): McpToolResult; /** * Build the settled tool result: the tool's REAL output in `content` + the settlement under * `_meta["x402/payment-response"]`, projected to EXACTLY the spec's 4 fields * `{ success, transaction, network, payer }` (the richer {@link X402Receipt} is never leaked verbatim). */ declare function toMcpPaymentResponse(content: McpContentBlock[], receipt: X402Receipt): McpToolResult; /** * Read the inbound RAW payment-payload object out of a tool call's `params._meta["x402/payment"]`. * The value is `{ x402Version, resource?, accepted, payload }`; `gate.verifyObject` reads * `accepted`/`payload` and ignores the extra `resource` sibling — so it's handed in as-is. Returns * `null` when there's no payment (→ the seller emits a fresh 402 challenge). */ declare function fromMcpPayment(params: McpToolCallParams): unknown | null; /** * Read the `X402Challenge` (PaymentRequired) back out of an `isError` tool result — the BUYER side. * Reads `structuredContent` first; falls back to parsing `content[0].text`. Returns `null` when the * result is not a 402 (a plain tool failure, or a success) so a buyer never mistakes a non-402 * `isError` for a payment request. */ declare function fromMcpPaymentRequired(result: McpToolResult): X402Challenge | null; /** Is this tool result a 402 challenge (an `isError` result carrying a parseable PaymentRequired)? */ declare function isMcpPaymentRequired(result: McpToolResult): boolean; /** * Read the settlement {@link SettleOutcome} back out of a settled tool result's * `_meta["x402/payment-response"]` — the BUYER side. Returns `null` when absent. The buyer records * the spend only on `success: true` (the existing `parseSettleResponse` rule). */ declare function fromMcpPaymentResponse(result: McpToolResult): SettleOutcome | null; /** * Frame an already-produced payment (the SAME `{ accepted, payload }` a PipRail buyer builds for * HTTP/A2A, after paying through the policy-gated client/gate machinery) into the * `_meta["x402/payment"]` entry to attach to the RETRY tool call's `params._meta`. Pure codec — it * mints no signature and grants no spend authority; the buyer signs/broadcasts via the existing * client, then frames the result here for the MCP carrier. */ declare function buildMcpPaymentMeta(input: { accepted: unknown; payload: unknown; resource?: { url: string; description?: string; mimeType?: string; }; x402Version?: number; }): { 'x402/payment': McpPaymentMeta; }; /** Options for {@link createMcpPaymentTool}. */ interface McpPaymentToolOptions extends Partial { /** * Pass the SAME {@link PaymentGate} the HTTP/A2A paths use → ONE shared replay set * (`localUsed` / injected `isUsed`/`markUsed`), so a proof settled over one transport and * replayed over MCP is caught once. If omitted, a fresh gate is built from the inline gate * config (`chain`/`token`/`amount`/`payTo`/…) on these same options. */ gate?: PaymentGate; /** Produce the tool's REAL output AFTER a verified settle — the merchant's own work. */ fulfill: (ctx: { receipt: X402Receipt; params: McpToolCallParams; }) => Promise | McpContentBlock[]; /** Resource URL stamped into the challenge (cosmetic; MCP tools have no inherent URL). */ resourceUrl?: string; } /** A paid MCP tool — turns one inbound tool call into the next tool result. */ interface McpPaymentTool { /** * Process one inbound tool call → the next tool result: * - no `_meta` payment → `isError` + a PaymentRequired challenge * - payment, verified+settled → the `fulfill` output + `_meta` payment-response * - settled but `fulfill` threw → STILL a `_meta` payment-response (success) + an error note in * content — never a re-challenge (the proof already settled; B7) * - payment, rejected/malformed → `isError` + a FRESH re-challenge PaymentRequired (retry) * - settle threw (relayer) → `isError` + "settlement failed" (onchain-proof fallback noted) */ handleToolCall(params: McpToolCallParams): Promise; /** The underlying gate (the shared replay set lives here). */ readonly gate: PaymentGate; } /** * Wrap an x402 {@link PaymentGate} as a paid MCP tool — the MCP analogue of `requirePayment` / * `createA2APaymentHandler`. All verify/settle/replay is the gate's `verifyObject` (the same seam * A2A uses): the trusted-accept re-derivation, the bounded replay set, self/facilitator settlement * — none of it is re-implemented here. Backendless: the merchant self-verifies and self-settles. */ declare function createMcpPaymentTool(options: McpPaymentToolOptions): McpPaymentTool; export { type A2AArtifact, type A2AExtensionDeclaration, type A2AMessage, type A2AMetadata, type A2APart, type A2APaymentHandler, type A2APaymentHandlerOptions, type A2APaymentStatus, type A2ATask, type A2ATaskRecord, type A2ATaskState, type A2ATaskStore, A2A_ERROR_KEY, A2A_EXTENSIONS_HEADER, A2A_PAYLOAD_KEY, A2A_RECEIPTS_KEY, A2A_REQUIRED_KEY, A2A_STATUS_KEY, A2A_X402_EXTENSION_URI_V01, A2A_X402_EXTENSION_URI_V02, type AcceptOption, AddressId, type AgentTool, type AlgorandToken, type AptosToken, AssetId, BRAND, BUILTIN_DENOMS, type BazaarExtension, type BuildExactParams, CHAINS, Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ChallengeTriage, type ChallengeVerdict, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, type CountStatus, DENOM_PRECISION, DIRECTORY_INFO, type DeclineReasonCode, type DeliverAttempt, type DeliverReceiptOptions, type DeliverResult, type DenomRemaining, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoveryDescriptor, type DiscoverySigner, type DiscoverySort, type DiscoverySource, type DomainClaim, type DomainVerification, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, ExactPaymentPayloadAny, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, type FacilitatorSupportedKind, type FailedPayment, GENERATOR, type GateSelfTest, InsufficientFundsError, InvalidConfigError, InvalidEnvelopeError, KNOWN_FACILITATORS, type KnownFacilitator, type ListingVisibility, MCP_PAYMENT_META_KEY, MCP_PAYMENT_RESPONSE_META_KEY, type ManifestInput, MaxRetriesExceededError, type McpContentBlock, type McpPaymentMeta, type McpPaymentTool, type McpPaymentToolOptions, type McpToolCallParams, type McpToolResult, MissingDriverError, MultiChainPayer, type MultiChainPayerOptions, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, PERMIT2_ADDRESS, PERMIT2_PROXY_CHAIN_IDS, PERMIT2_UPTO_WITNESS_TYPES, PERMIT2_WITNESS_TYPES, PIPRAIL_AGENT_GUIDE, POWERED_BY, PaidReceipt, type PayBlocker, type PayOption, type PayWarning, type PayingClient, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, type PaymentScheme, PaymentTimeoutError, type PaywallOptions, Permit2UptoPaymentPayload, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, PipRailReceipt, type PolicyDecision, type PolicyDenyCode, REGISTER_ATTRIBUTION, type ReceiptInput, type ReceiptOption, type ReceiptVerification, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SelfDescribeEndpoint, type SelfDescribeRail, type SelfDescription, type Serve, type SessionBudget, SettleOutcome, type SettleViaFacilitatorInput, SettlementError, SignedReceipt, type SolanaToken, SpendLedger, SpendRecord, type SpendRemaining, SpendStore, SpendSummary, type StellarToken, type SuiToken, type TipJarOptions, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UPTO_PROXY_CHAIN_IDS, UnknownTokenError, UnsupportedNetworkError, UnsupportedSchemeError, type UptoRailOption, VERIFY_CODE_TO_A2A_ERROR, VerifyErrorCode, type VerifyPaymentResult, VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, WalletRequiredError, type WellKnownX402, type WellKnownX402Item, type WellKnownX402Manifest, WrongChainError, WrongFamilyError, X402AcceptEntry, X402AnyAccept, X402Challenge, type X402DnsRecord, X402ExactAcceptEntry, type X402InvalidBody, X402Receipt, X402UptoAcceptEntry, X402_EXACT_PERMIT2_PROXY, X402_UPTO_PERMIT2_PROXY, type XrplToken, agentGuide, appendAttribution, appendKeywords, buildBazaarExtension, buildEndpointInfo, buildExactAuthorization, buildMcpPaymentMeta, buildOpenApi, buildSelfDescription, buildWellKnownX402, buildWellKnownX402Manifest, buildX402DnsTxt, chainIdForExactNetwork, claim402IndexDomain, classifyChallenge, createA2APaymentHandler, createMcpPaymentTool, createPaymentGate, createPaywall, createTipJar, decorateOutcome, deliverReceipt, denomOf, describeChallenge, discoveryHeaders, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, explainDecline, facilitatorCoverage, fetchAcross, firstKeylessFacilitator, formatSpendReport, fromA2APaymentPayload, fromA2APaymentRequired, fromMcpPayment, fromMcpPaymentRequired, fromMcpPaymentResponse, getDirectoryInfo, isMcpPaymentRequired, isPermit2ProxyChain, isUptoProxyChain, knownFacilitatorsFor, normalizeNetwork, parseExactRequirements, parseFacilitatorSupported, paymentTools, planAcross, proxyTo, rankResources, readExactDomain, register402Index, registerDriver, registerX402Scan, renderLandingPage, requirePayment, resolveChain, scoreResource, searchOpenIndexes, settleViaFacilitator, summarizePlan, toA2AErrorCode, toA2APaymentFailed, toA2APaymentReceipts, toA2APaymentRequired, toFetchHandler, toInsufficientFundsError, toInvalidBody, toMcpPaymentRequired, toMcpPaymentResponse, toWorker, verify402IndexDomain };