Skip to main content
A wallet adapter signs payment proofs on behalf of the client. It takes a payment request and produces a signed payload that proves the client authorized the payment. Wallets never submit transactions directly — they only sign. The facilitator handles on-chain submission.

The wallet adapter pattern

Every wallet adapter in faremeter follows the same pattern: a factory function that creates a wallet object.
import { Keypair } from "@solana/web3.js"
import { createLocalWallet } from "@faremeter/wallet-solana"

const keypair = Keypair.fromSecretKey(new Uint8Array(keypairBytes))
const wallet = await createLocalWallet("devnet", keypair)
The wallet object is then passed to a payment handler, which uses it to sign payments when a 402 response is received.

Available adapters

PackageWallet typeUse case
@faremeter/wallet-owsOWS vault (Solana + EVM)Vault-backed signing via Open Wallet Standard
@faremeter/wallet-solanaSolana keypairLocal development, server-side agents
@faremeter/wallet-evmEVM private keyLocal development, server-side agents
@faremeter/wallet-crossmintCrossmint custodialManaged wallets, no private keys
@faremeter/wallet-solana-squadsSquads multisigDAOs, shared treasuries
@faremeter/wallet-ledgerLedger hardwareHigh-security signing

Choosing a wallet adapter

  • Local keypair / private key — Simplest setup. Keys live in files or environment variables. Good for local development, scripts, and single-machine agents.
  • OWS — Keys encrypted in a vault, never exposed to your application. Better for production agents, shared machines, or environments where raw key files feel too exposed.
  • Crossmint — No private keys at all. Signing is delegated to a custodial API.
  • Squads — Multi-party approval before any payment is signed.
  • Ledger — Physical device required for each signature. Maximum security, interactive only.
All adapters produce the same wallet interface, so switching between them requires no changes to payment handlers or fetch wrappers.

How wallets plug in

Wallets are passed to payment handlers, which are passed to the fetch wrapper:
import { Keypair, PublicKey } from "@solana/web3.js"
import { wrap } from "@faremeter/fetch"
import { createPaymentHandler } from "@faremeter/payment-solana/exact"
import { createLocalWallet } from "@faremeter/wallet-solana"
import { lookupKnownSPLToken } from "@faremeter/info/solana"

const keypair = Keypair.fromSecretKey(new Uint8Array(keypairBytes))
const wallet = await createLocalWallet("devnet", keypair)

const splTokenInfo = lookupKnownSPLToken("devnet", "USDC")
if (!splTokenInfo) throw new Error("Unknown SPL token")
const mint = new PublicKey(splTokenInfo.address)
const handler = createPaymentHandler(wallet, mint)

const fetchWithPayment = wrap(fetch, {
  handlers: [handler],
})
Or with @faremeter/rides, wallets are added directly and everything else is wired up automatically:
import { payer } from "@faremeter/rides"

await payer.addLocalWallet(process.env.PAYER_KEYPAIR_PATH)
await payer.addLocalWallet(process.env.EVM_PRIVATE_KEY)
Rides detects the wallet type from the input. A file path is treated as a Solana keypair. A hex string starting with 0x is treated as an EVM private key.

Signing flow

  1. Client receives a 402 response with payment requirements.
  2. The payment handler checks if the wallet matches the required scheme/network/asset.
  3. If compatible, the handler asks the wallet to sign the payment.
  4. The wallet produces a signed payload (chain-specific format).
  5. The handler wraps the payload into an X-PAYMENT header.
The wallet never sees the HTTP request or response. It only receives the payment data to sign.

Further reading