Skip to main content

@faremeter/payment-solana

@faremeter/payment-solana brings Coinbase’s x402 exact scheme to Solana. It includes:
Source: GitHub › packages/payment-solana
  • A wallet-side payment handler that builds and signs SPL-token transfer transactions.
  • A facilitator handler that validates transactions, signs as fee payer, and broadcasts them.
  • Runtime validators and matchers for guarding request payloads.
Use it alongside @faremeter/fetch and @faremeter/facilitator to orchestrate non-custodial USDC payments on Solana.

Quick start

import { createPaymentHandler } from "@faremeter/payment-solana/exact";
import { createLocalWallet } from "@faremeter/wallet-solana";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";

const wallet = await createLocalWallet("mainnet-beta", Keypair.generate());

const handler = createPaymentHandler(
  wallet,
  new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC mint
  new Connection("https://api.mainnet-beta.solana.com"),
);
Merchant / facilitator:
import { createFacilitatorHandler } from "@faremeter/payment-solana/exact";
import { createFacilitatorRoutes } from "@faremeter/facilitator";
import { Keypair, PublicKey } from "@solana/web3.js";
import { createSolanaRpc } from "@solana/kit";

const handler = createFacilitatorHandler(
  "mainnet-beta",
  createSolanaRpc("https://api.mainnet-beta.solana.com"),
  Keypair.fromSecretKey(feePayerBytes),
  new PublicKey("EPjFWd..."), // USDC mint
);

const routes = createFacilitatorRoutes({ handlers: [handler] });

Modules

exact/createPaymentHandler(wallet, mint, connection?, options?)

Returns a PaymentHandler for the client.
  • wallet: object with { network, publicKey } plus optional buildTransaction, updateTransaction, sendTransaction. Matches the shape produced by @faremeter/wallet-solana, @faremeter/wallet-crossmint, or custom implementations.
  • mint: SPL token mint PublicKey.
  • connection: optional Connection. Required to auto-fetch blockhashes and mint decimals unless the facilitator provides them via the requirements extra field.
  • options.token: advanced config forwarded to getAssociatedTokenAddressSync (e.g., allowOwnerOffCurve).
For each matching requirement the handler:
  • Validates the extra payload using PaymentRequirementsExtra (fee payer, decimals, blockhash).
  • Builds compute budget and transferChecked instructions sending the full maxAmountRequired to the merchant.
  • Uses wallet hooks (buildTransaction, updateTransaction, sendTransaction) to customize signing/broadcasting.
  • Returns payload { transaction: base64EncodedWireTransaction }.

exact/createFacilitatorHandler(network, rpc, feePayerKeypair, mint, config?)

Creates a FacilitatorHandler for the server side.
  • network: "devnet" | "testnet" | "mainnet-beta" (or custom cluster names recognized by @faremeter/info/solana).
  • rpc: Solana RPC client compatible with @solana/kit.
  • feePayerKeypair: payer authority that signs outgoing transactions.
  • mint: USDC mint PublicKey.
  • config.maxRetries (default 30): how many times to poll confirmation.
  • config.retryDelayMs (default 1000): delay between status checks.
  • config.maxPriorityFee (lamports): upper bound for priority fee; mismatches reject the transaction.
Handler responsibilities:
  • getSupported: advertises x402 networks returned by lookupX402Network(network) with fee payer info in extra.
  • getRequirements: filters and enriches requirements with asset, recentBlockhash, decimals, and feePayer.
  • handleSettle: validates the incoming base64 transaction, ensures it matches the expected transfer, partially signs with the fee payer, simulates & sends, and polls confirmation.
Rejections (e.g., wrong destination, facilitator as spender, excessive priority fee) return { success: false, error } to prompt the client to retry.

Validators & helpers

  • PaymentRequirementsExtra: arktype schema ensuring feePayer, optional decimals, recentBlockhash.
  • PaymentPayload: arktype schema for { transaction: base64EncodedWireTransaction }.
  • isValidTransaction(transactionMessage, requirements, facilitatorPublicKey, maxPriorityFee?): asserts the transaction instructions match the expected compute budget + transfer (and optional ATA creation).
  • generateMatcher(network, asset): builds a requirements matcher using lookupX402Network(network) and the mint address.
  • x402Scheme: exported literal "exact".
  • logger: Logtape logger under ["faremeter","payment-solana"].

Usage patterns

  • Managed wallets: Pass wallets from @faremeter/wallet-crossmint or a custodial provider. As long as they expose network, publicKey, and an updateTransaction or sendTransaction hook, the handler works.
  • Cold wallets: For air-gapped flows, provide buildTransaction to write instructions to disk, have the user sign offline, then return the signed transaction through updateTransaction.
  • Priority fees: Set maxPriorityFee to avoid draining SOL from payers if competing transactions spike the compute budget price.
  • Observability: Facilitator logs helpful context when validation fails (e.g., mismatched destination). Capture ["faremeter","payment-solana"] logs.
  • @faremeter/info/solana: registries for clusters and assets, plus lookupX402Network.
  • @faremeter/middleware & @faremeter/fetch: orchestrate the HTTP 402 handshake using these handlers.
  • @faremeter/wallet-*: wallet adapters that return the required wallet shape.