Documentation Index
Fetch the complete documentation index at: https://docs.faremeter.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Faremeter’s plugin architecture allows community developers to create custom wallet adapters, payment schemes, and middleware integrations. Every layer of the stack is replaceable.
What can be extended
| Plugin type | Interface | Use case |
|---|
| Wallet adapter | createLocalWallet() pattern | Support a new wallet type (hardware, custodial, multisig) |
| Payment handler | PaymentHandler | Support a new blockchain or payment scheme on the client |
| Facilitator handler | FacilitatorHandler | Support a new blockchain or scheme on the facilitator |
| Middleware adapter | handleMiddlewareRequest() | Integrate with a framework beyond Express and Hono |
Building a custom wallet adapter
A wallet adapter is a factory function that returns an object capable of signing payment proofs. The exact shape depends on the chain, but the pattern is the same: accept credentials, return a signer.
import type { Keypair } from "some-chain-sdk"
interface MyWallet {
address: string
network: string
sign(data: Uint8Array): Promise<Uint8Array>
}
export async function createMyWallet(
network: string,
keypair: Keypair,
): Promise<MyWallet> {
return {
address: keypair.publicKey.toString(),
network,
sign: async (data) => {
return keypair.sign(data)
},
}
}
The wallet is then passed to a payment handler. See Wallets & Signing for the full adapter pattern.
Building a custom payment handler
A payment handler bridges a wallet and a payment scheme. It receives payment requirements from a 402 response and returns PaymentExecer objects for any requirements it can fulfill.
import type { PaymentHandler, PaymentExecer } from "@faremeter/types/client"
export function createMyPaymentHandler(wallet: MyWallet): PaymentHandler {
return async (context, accepts) => {
const execers: PaymentExecer[] = []
for (const req of accepts) {
// Only handle requirements this wallet can fulfill
if (req.scheme !== "my-scheme" || req.network !== wallet.network) {
continue
}
execers.push({
requirements: req,
exec: async () => {
// Build and sign the payment proof
const proof = buildProof(req, wallet)
const signature = await wallet.sign(proof)
return {
payload: {
signature: Buffer.from(signature).toString("base64"),
address: wallet.address,
},
}
},
})
}
return execers
}
}
Register the handler with the fetch wrapper:
import { wrap } from "@faremeter/fetch"
const wallet = await createMyWallet("my-network", keypair)
const handler = createMyPaymentHandler(wallet)
const fetchWithPayment = wrap(fetch, {
handlers: [handler],
})
See Payment Handlers for the full interface and composition model.
Building a custom facilitator handler
To support a new chain on the facilitator side, implement the FacilitatorHandler interface:
import type { FacilitatorHandler } from "@faremeter/types/facilitator"
export async function createMyFacilitatorHandler(
network: string,
rpcUrl: string,
signerKey: string,
): Promise<FacilitatorHandler> {
const client = createChainClient(rpcUrl, signerKey)
return {
getSupported: () => [
Promise.resolve({
x402Version: 2,
scheme: "my-scheme",
network,
}),
],
getRequirements: async ({ accepts }) => {
// Enrich requirements with chain-specific fields
return accepts
.filter((r) => r.scheme === "my-scheme" && r.network === network)
.map((r) => ({
...r,
// Add facilitator-specific enrichment here
extra: { signerAddress: client.address },
}))
},
handleSettle: async (requirements, payment) => {
if (requirements.scheme !== "my-scheme") return null
// Verify and submit the transaction on-chain
const txHash = await client.submitTransaction(payment.payload)
return {
success: true,
transaction: txHash,
network,
}
},
}
}
Wire it into a facilitator:
import { createFacilitatorRoutes } from "@faremeter/facilitator"
import { Hono } from "hono"
const handler = await createMyFacilitatorHandler("my-network", rpcUrl, signerKey)
const app = new Hono()
app.route("/", createFacilitatorRoutes({ handlers: [handler] }))
Handlers return null from handleSettle and handleVerify for schemes they don’t support, allowing multiple handlers to coexist. See Facilitator for the full setup.
Building a custom middleware adapter
If you use a framework other than Express or Hono, use handleMiddlewareRequest from @faremeter/middleware/common to wire up the payment flow manually. You provide functions to read headers, send responses, and handle the request body.
import {
handleMiddlewareRequest,
createPaymentRequiredResponseCache,
resolveSupportedVersions,
} from "@faremeter/middleware/common"
const { getPaymentRequiredResponse } = createPaymentRequiredResponseCache({})
// Example: Fastify integration
fastify.addHook("preHandler", async (request, reply) => {
return await handleMiddlewareRequest({
facilitatorURL: "https://facilitator.corbits.dev",
accepts,
supportedVersions: resolveSupportedVersions(),
resource: request.url,
getHeader: (key) => request.headers[key] as string | undefined,
getPaymentRequiredResponse,
sendJSONResponse: (status, body, headers) => {
if (headers) {
for (const [key, value] of Object.entries(headers)) {
reply.header(key, value)
}
}
reply.status(status).send(body)
},
body: async ({ settle }) => {
const result = await settle()
if (!result.success) return result.errorResponse
// Request continues to the route handler
},
})
})
See Middleware Overview for the full API reference.
Publishing plugins
When publishing a faremeter plugin:
- Use the naming convention
faremeter-* or @your-scope/faremeter-* for discoverability.
- Declare
@faremeter/types as a peer dependency for type compatibility.
- Export your factory functions as the package’s public API.
- Include a README with a working example.
Plugin registry
Visit the faremeter plugin registry to browse community-contributed packages.
Further reading