# FareSide

FareSide is a hosted x402 facilitator and infrastructure service for AI agent payments, it lets API providers accept autonomous machine-to-machine payments using the [x402 protocol](https://x402.org) — an open standard built on HTTP status code 402. When an AI agent requests a paid resource, the server responds with 402, the agent signs a payment voucher, and a facilitator (FareSide) verifies and settles it on-chain. The server never manages blockchain complexity; FareSide handles it.

What FareSide Provides:
- Hosted Facilitator — Verifies payment payloads and settles transactions on-chain on your behalf, never holds funds, only executes signed transactions
- Reliable Settlement — Smart nonce management, automatic gas bumping for stuck transactions, and multi-RPC failover, stayed online during ecosystem-wide surges, briefly handling 77% of all x402 traffic
- Omni-Chain — Accept payments on all EVM chains (Base, Polygon, Avalanche, Sei, XDC...), Solana, Aptos... Users pay on any chain, you settle where you want
- Open Protocol, No Lock-in — Standard x402 spec. Switching facilitators is just changing an endpoint. Built on [x402-rs](https://github.com/x402-rs/x402-rs), a fully open-source Rust implementation

Use Cases:
- API Monetization — Per-call payments for any metered service (data, AI queries, storage) without accounts or API keys
- Multi-Party Payments — Revenue splits, referral fees, marketplace commissions routed to multiple recipients automatically
- Cross-Chain Services — Accept on any chain, settle where profitable, no manual bridging
- Custom Settlement Logic — Token issuance, loyalty points, escrow, refunds

# Documentation

## Welcome to FareSide [Getting Started]: x402 infrastructure: open-source tools and managed services for AI agent payments
Source: /docs/welcome.md

FareSide is an x402 infrastructure project. We build open-source tools and managed services for the [x402 protocol](https://x402.org)&nbsp;&mdash; the payment standard that lets AI agents pay for services autonomously.

**What we offer:**

- **[x402-rs](https://github.com/x402-rs/x402-rs)**&nbsp;&mdash; Modular open-source Rust ecosystem (types, middleware, clients, chain integrations)
- **Hosted Facilitator**&nbsp;&mdash; Production-ready payment verification and settlement
- **Dashboard**&nbsp;&mdash; Monitor payments, manage organizations, and track multi-chain balances
- **Advanced Features**&nbsp;&mdash; Payment splits, cross-chain settlement, discovery (coming soon)

### What is x402?

The [x402 protocol](https://docs.x402.org) is an open payment standard that enables machine-to-machine payments using HTTP status code `402 Payment Required`. It allows:

- **API monetization**&nbsp;&mdash; Charge per request without accounts or API keys
- **AI agent payments**&nbsp;&mdash; Agents autonomously pay for services they need
- **Micropayments**&nbsp;&mdash; Sub-cent transactions that aren't viable with traditional payments
- **Better human UX**&nbsp;&mdash; No accounts, no forms—just approve and pay
- **Extensible protocol**&nbsp;&mdash; Supports human-sized payments and even card payments through protocol extensions

### What is a Facilitator?

When accepting x402 payments, your service is responsible for landing the user's transaction onchain to receive funds. You can handle this directly within your service, or offload it to a specialized 3rd party. The x402 protocol standardizes this role as a [facilitator](https://docs.x402.org/core-concepts/facilitator).

A facilitator:

- **Verifies payments**&nbsp;&mdash; Confirms that client payment payloads meet your declared requirements
- **Settles payments**&nbsp;&mdash; Submits validated payments to the blockchain on your behalf
- **Returns results**&nbsp;&mdash; Provides verification and settlement responses so your code can decide whether to fulfill the request

The facilitator **never holds funds** or acts as a custodian&nbsp;&mdash; it only executes onchain transactions based on signed payloads provided by clients.

You can [self-host a facilitator](/docs/self-hosting) for full control, but it comes with operational burden: maintaining blockchain connectivity, managing nonces and gas prices, handling RPC reliability, and ensuring correct protocol behavior. Using a hosted facilitator like FareSide lets you skip all that.

### Why FareSide?

#### Production-Proven Infrastructure

FareSide is built on [x402-rs](https://github.com/x402-rs/x402-rs), our open-source Rust implementation that served **up to 77% of global x402 transactions** during peak usage. We've learned what works at scale and built infrastructure that handles it.

#### Reliable Settlement

Our proprietary (for now) transaction relayer goes beyond vanilla blockchain libraries:

- Smart nonce management that doesn't break under load
- Automatic gas bumping for stuck transactions
- Optimized transactions for lower latency
- Multi-RPC failover for reliability

#### Multi-Chain Acceptance

Accept payments on the chains your users prefer. Users pay where convenient, you accumulate where profitable:

- **EVM (`eip155`)**: Base, Polygon, Avalanche, Sei, XDC, and more
- **Solana (`solana`)**: Mainnet and Devnet
- **Aptos (`aptos`)**: Mainnet and Testnet

#### Open Protocol, No Lock-in

FareSide implements the standard x402 protocol. Your integration works with any x402-compatible client, and you can self-host using [x402-rs](https://github.com/x402-rs/x402-rs) if you prefer.

### How It Works

```mermaid
sequenceDiagram
    participant Buyer as Agent (Buyer)
    participant Seller as Service (Seller)
    participant Facilitator as FareSide Facilitator

    Buyer->>Seller: 1. Request resource
    Seller-->>Buyer: 2. 402 Payment Required
    Note over Buyer: 3. Create & sign payment voucher
    Buyer->>Seller: 4. Retry request with payment attached
    Seller->>Facilitator: 5. Verify payment
    Facilitator-->>Seller: 6. Payment valid
    Note over Seller: 7. Do the work
    Seller->>Facilitator: 8. Settle payment
    Facilitator-->>Seller: 9. Settlement confirmed
    Seller-->>Buyer: 10. Return result
```

### Getting Started

1. **[Sign up](https://app.fareside.com)** - Create an organization and get your API key
2. **[Quickstart](/docs/quickstart)** - Accept your first payment in 5 minutes

### Coming Soon

We're building additional features for production use cases:

- Payment Splits&nbsp;&mdash; Same-chain revenue sharing and multi-party payments
- Cross-Chain Settlement&nbsp;&mdash; Accept on any chain, settle where you want
- Discovery Service&nbsp;&mdash; Help AI agents find your services via the Bazaar extension

#### Community

- **Telegram**: [t.me/faresidehq](https://t.me/faresidehq)
- **X**: [x.com/faresidehq](https://x.com/faresidehq)
- **x402-rs GitHub**: [github.com/x402-rs/x402-rs](https://github.com/x402-rs/x402-rs)
- **Email**: [info@fareside.com](mailto:info@fareside.com)

## Quickstart [Getting Started]: Accept your first x402 payment in 5 minutes
Source: /docs/quickstart.md

import { DocsNotice } from "@/components/docs/DocsNotice";
import { DocsCard } from "@/components/docs/DocsCard";

### Prerequisites

- Node.js 20+ or Rust 1.88+
- A wallet address to receive payments
- Your FareSide API key from [dashboard](https://app.fareside.com) ([learn more](/docs/dashboard))

<DocsNotice variant='info' title='Note'>
  The versions of x402-* crates shown in this guide are indicative. Please check [crates.io](https://crates.io) for the latest available versions.
</DocsNotice>

### Choose Your Stack

The examples below use Hono (TypeScript) and Axum (Rust). x402 is a _protocol_. While Express, Hono, Next.js, Axum are supported out of the box, adding x402-payment-gating to any other framework or language is relatively straightforward.

<div className='grid gap-4 md:grid-cols-2'>
  <DocsCard title="TypeScript (Hono)" description="Recommended for most projects" href="#typescript-hono" />
  <DocsCard title="Rust (Axum)" description="High-performance services" href="#rust-axum" />
</div>

### TypeScript (Hono)

#### Step 1: Install Dependencies

We use the scoped `@x402` packages for v2 support from x402 reference SDK.

```bash
npm install hono @hono/node-server @x402/hono @x402/core @x402/evm
```

#### Step 2: Create Your Server

```typescript
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { paymentMiddleware } from "@x402/hono";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";

const app = new Hono();

// 1. Configure the Facilitator Client with your FareSide API key
const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://facilitator.fareside.com/YOUR_API_KEY"
});

// 2. Create the Resource Server and register payment schemes
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server); // Enable EVM payments

// 3. Configure payment middleware with FareSide facilitator
app.use(
  paymentMiddleware(
    {
      "GET /weather": {
        accepts: [
          {
            scheme: "exact",
            price: "$0.01",           // Price in USD (converted to USDC)
            network: "eip155:84532",  // Base Sepolia (CAIP-2 format)
            payTo: "0xYourWalletAddress",
          },
        ],
        description: "Weather API access",
        mimeType: "application/json",
      },
    },
    server,
  ),
);

// Your paid endpoint
app.get("/weather", (c) => {
  return c.json({
    location: "San Francisco",
    temperature: 68,
    conditions: "Sunny",
  });
});

serve({ fetch: app.fetch, port: 3000 });
console.log("Server running on http://localhost:3000");
```

#### Step 3: Test It

Start your server:

```bash
npx tsx server.ts
```

Make a request without payment:

```bash
curl -i http://localhost:3000/weather
```

You'll get a `402 Payment Required` response. The requirements are returned in the `Payment-Required` header (base64 encoded), which you can decode with:

```bash
echo "<Payment-Required header>" | base64 -d | jq
```

Decoded header payload example:

```json
{
  "x402Version": 2,
  "error": "Payment required",
  "resource": {
    "url": "http://localhost:3000/weather",
    "description": "Weather API access",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "amount": "10000", // 0.01 USDC (6 decimals)
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract on Base Sepolia
      "payTo": "0xYourWalletAddress",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USDC",
        "version": "2"
      }
    }
  ]
}
```

**Key Fields:**

- **`x402Version`**: Protocol version
- **`resource`**: Metadata about the protected resource
  - `url`: The resource identifier
  - `description`: Human-readable description of what is being purchased
  - `mimeType`: Content type of the resource
- **`accepts`**: Array of acceptable payment options. The client must choose one to proceed
  - **`scheme`**: The payment logic to use (e.g., `"exact"` for a fixed amount transfer)
  - **`network`**: The blockchain network identifier in CAIP-2 format
  - **`amount`**: The cost in atomic units (e.g., `10000` = 0.01 USDC)
  - **`asset`**: The token contract address (e.g., USDC address)
  - **`payTo`**: The recipient's wallet address
  - **`extra`**: Scheme-specific parameters (e.g., EIP-712 domain info for EVM tokens)

### Rust (Axum)

#### Step 1: Add Dependencies

```toml
[dependencies]
alloy-primitives = "1.4"
axum = "0.8"
serde_json = "1"
tokio = "1"
x402-axum = "1.1"
x402-chain-eip155 = { version = "1.1", features = ["server"] }
x402-types = "1.1"
```

#### Step 2: Create Your Server

```rust
use axum::{routing::get, Router};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use serde_json::json;
use alloy_primitives::address;
use x402_axum::X402Middleware;
use x402_chain_eip155::{V2Eip155Exact, KnownNetworkEip155};
use x402_types::networks::USDC;

###[tokio::main]
async fn main() {
    // Configure FareSide facilitator
    let x402 = X402Middleware::try_from(
        "https://facilitator.fareside.com/YOUR_API_KEY"
    ).unwrap();

    // Configure USDC on Base Sepolia and your receiving address
    let usdc = USDC::base_sepolia();
    let wallet = address!("0xYourWalletAddress");

    let app = Router::new()
        .route("/weather", get(weather_handler).layer(
            x402.with_price_tag(
                // Create a V2 price tag for 0.01 USDC
                V2Eip155Exact::price_tag(
                    wallet,
                    usdc.amount(10_000u64) // 0.01 USDC (6 decimals)
                )
            )
            // Description is required for v2 resource metadata
            .with_description("Weather API access".to_string())
        ));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    println!("Server running on http://localhost:3000");
    axum::serve(listener, app).await.unwrap();
}

async fn weather_handler() -> impl IntoResponse {
    (StatusCode::OK, axum::Json(json!({
        "location": "San Francisco",
        "temperature": 68,
        "conditions": "Sunny"
    })))
}
```

#### Step 3: Test It

```bash
cargo run
```

Make a request without payment:

```bash
curl -i http://localhost:3000/weather
```

You'll get a `402 Payment Required` response. The requirements are returned in the `Payment-Required` header (base64 encoded), which you can decode with:

```bash
echo "<Payment-Required header>" | base64 -d | jq
```

Decoded header payload example:

```json
{
  "x402Version": 2,
  "error": "Payment-Signature header is required",
  "resource": {
    "url": "http://localhost:3000/weather",
    "description": "Weather API access",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:84532",
      "amount": "10000", // 0.01 USDC (6 decimals)
      "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract on Base Sepolia
      "payTo": "0xYourWalletAddress",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USDC",
        "version": "2"
      }
    }
  ]
}
```

**Key Fields:**

- **`x402Version`**: Protocol version
- **`resource`**: Metadata about the protected resource
  - `url`: The resource identifier
  - `description`: Human-readable description of what is being purchased
  - `mimeType`: Content type of the resource
- **`accepts`**: Array of acceptable payment options. The client must choose one to proceed
  - **`scheme`**: The payment logic to use (e.g., `"exact"` for a fixed amount transfer)
  - **`network`**: The blockchain network identifier in CAIP-2 format
  - **`amount`**: The cost in atomic units (e.g., `10000` = 0.01 USDC)
  - **`asset`**: The token contract address (e.g., USDC address)
  - **`payTo`**: The recipient's wallet address
  - **`extra`**: Scheme-specific parameters (e.g., EIP-712 domain info for EVM tokens)

### Making a Payment (Client Side)

To actually pay for the endpoint, you need an x402-compatible client. Here's how to test with `@x402/fetch`:

```bash
npm install @x402/fetch @x402/core @x402/evm viem
```

```typescript
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client, x402HTTPClient } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

// 1. Setup your wallet
const signer = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

// 2. Create x402 client and register EVM scheme
const client = new x402Client();
registerExactEvmScheme(client, { signer });

// 3. Wrap fetch
const fetchWithPayment = wrapFetchWithPayment(fetch, client);

// 4. Make request - payment is handled automatically
const response = await fetchWithPayment("http://localhost:3000/weather", {
  method: "GET",
});

const data = await response.json();
console.log("Weather:", data);

// Check payment receipt
if (response.ok) {
  const httpClient = new x402HTTPClient(client);
  const receipt = httpClient.getPaymentSettleResponse(
    (name) => response.headers.get(name)
  );
  console.log("Payment settled:", receipt);
}
```

For more details, see the [Making Payments](/docs/making-payments) guide.

### What Just Happened?

1. **Client requested** `/weather` without payment
2. **Server returned** `402 Payment Required` with payment details
3. **Client created** a signed payment payload using the registered EVM scheme
4. **Client retried** the request with `Payment-Signature` header
5. **FareSide facilitator verified** the payment on-chain
6. **Server delivered** the weather data

The payment goes directly to your wallet.

### Next Steps

- **[Setup Guide](/docs/setup-guide)** - Get your FareSide API key
- **[Hono/Express Integration](/docs/express-hono-nextjs)** - Detailed TypeScript examples
- **[Rust (Axum) Integration](/docs/rust-axum)** - Detailed Rust examples
- **[Making Payments](/docs/making-payments)** - Build x402 clients
- **[Supported Networks](/docs/supported-networks)** - Available chains and tokens

## Supported Networks [Getting Started]: Chains and tokens supported by FareSide
Source: /docs/supported-networks.md

import { DocsNotice } from "@/components/docs/DocsNotice";

FareSide supports multiple blockchain networks through the [x402-rs](https://github.com/x402-rs/x402-rs) facilitator. We use **CAIP-2** identifiers to unambiguously identify chains.

#### EVM Networks (`eip155`)

| Network           | CAIP-2 ID        | Chain ID | Status      | Testnet (CAIP-2)         |
|-------------------|------------------|----------|-------------|--------------------------|
| Base              | `eip155:8453`    | 8453     | ✅ Available | `eip155:84532` (Sepolia) |
| Polygon           | `eip155:137`     | 137      | ✅ Available | `eip155:80002` (Amoy)    |
| Avalanche C-Chain | `eip155:43114`   | 43114    | ✅ Available | `eip155:43113` (Fuji)    |
| Celo              | `eip155:42220`   | 42220    | ✅ Available | `eip155:11142220`        |
| Sei               | `eip155:1329`    | 1329     | ✅ Available | `eip155:1328`            |
| XDC               | `eip155:50`      | 50       | ✅ Available | -                        |
| XRPL EVM          | `eip155:1440000` | 1440000  | ✅ Available | -                        |
| Peaq              | `eip155:3338`    | 3338     | ✅ Available | -                        |
| IoTeX             | `eip155:4689`    | 4689     | ✅ Available | -                        |

<DocsNotice variant='info' title='Note'>
  FareSide supports any EVM-compatible network. If you need a network that isn't listed above, please [contact us](/docs/contact) to enable support for your preferred chain.
</DocsNotice>

#### Solana (`solana`)

| Network | CAIP-2 ID                                 | Status      | Testnet (CAIP-2)                                   |
|---------|-------------------------------------------|-------------|----------------------------------------------------|
| Solana  | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | ✅ Available | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` (Devnet) |

#### Aptos (`aptos`)

| Network | CAIP-2 ID | Status       | Testnet (CAIP-2) |
|---------|-----------|--------------|------------------|
| Aptos   | `aptos:1` | ✅ Available* |  `aptos:2`       |

#### Supported Tokens

##### EVM: EIP-3009 Compatible Tokens

Tokens implementing [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) (`transferWithAuthorization`) are natively supported. The client signs an [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data message, and the facilitator submits the signed authorization on-chain — the client pays no gas.

The `extra` field carries the EIP-712 domain parameters required for signature construction:

```json
{
  "scheme": "exact",
  "network": "eip155:84532",
  "amount": "10000",
  "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
  "payTo": "0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07",
  "maxTimeoutSeconds": 300,
  "extra": {
    "name": "USDC",
    "version": "2"
  }
}
```

**`extra` fields:**

- **`name`** and **`version`** — EIP-712 domain values. Must *exactly* match the values hardcoded in the token contract. Required for correct signature construction.
- **`assetTransferMethod`** — optional. Defaults to `eip3009` when omitted.

<DocsNotice variant='info' title='Note'>
  If `name` and `version` are omitted, the facilitator will attempt to fetch them from the token contract on-chain. Providing them explicitly avoids extra RPC calls and ensures signature consistency.
</DocsNotice>

Well-known EIP-3009 tokens: **USDC**, **EURC**.

For a detailed explanation of how EIP-3009 transfers work, see the [EVM Asset Transfer Methods](/docs/evm-asset-transfer-methods) guide.

##### EVM: Permit2 Compatible Tokens

For ERC-20 tokens **without** built-in EIP-3009 support, x402 uses [Uniswap Permit2](https://docs.uniswap.org/contracts/permit2/overview) combined with the `x402Permit2Proxy` contract to enable gasless settlement.

To use this path, set `assetTransferMethod` to `permit2` in `extra`:

```json
{
  "scheme": "exact",
  "network": "eip155:84532",
  "amount": "10000",
  "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
  "payTo": "0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07",
  "maxTimeoutSeconds": 300,
  "extra": {
    "assetTransferMethod": "permit2",
    "name": "USDC",
    "version": "2"
  }
}
```

**Key difference:** Permit2 requires a **one-time** on-chain `approve()` transaction from the client to the Permit2 contract before gasless signatures can be used. After this setup, all subsequent payments are signature-based.

The `x402Permit2Proxy` ensures the facilitator **cannot** alter the payment amount or destination — see [EVM Asset Transfer Methods](/docs/evm-asset-transfer-methods) for the full security model and payment flow.

<DocsNotice variant='info' title='Upto Scheme'>
  EVM chains also support the [`upto` scheme](/docs/upto-scheme) — usage-based payments where the client authorizes a maximum amount and the server charges based on actual consumption. The `upto` scheme uses Permit2 exclusively.
</DocsNotice>

##### Solana: Any SPL Token

All SPL tokens on Solana are supported for x402 payments. This includes:

- **USDC** - [Circle's](https://www.circle.com) USD stablecoin
- **EURC** - [Circle's](https://www.circle.com) EUR stablecoin
- **MXNe** - [Brale's](https://brale.xyz) Mexican Peso stablecoin
- Any other SPL token

##### Aptos: Fungible Assets

On Aptos, we support payments using the standard Fungible Asset standard (`0x1::primary_fungible_store::transfer`).

The `exact` scheme on Aptos supports **sponsored transactions**. When available, the `extra` field will contain `sponsored: true`, indicating the facilitator will pay the gas fees.

```json
{
  "scheme": "exact",
  "network": "aptos:1",
  "amount": "1000000",
  "asset": "0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b",
  "payTo": "0x1234...",
  "maxTimeoutSeconds": 60,
  "extra": {
    "sponsored": true
  }
}
```

#### Choosing a Network

##### For Development

Use **testnets** to avoid spending real funds:

- **Base Sepolia** (`eip155:84532`) - Recommended for EVM development
- **Solana Devnet** (`solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`) - Recommended for Solana development
- **Aptos Testnet** (`aptos:2`) - Recommended for Aptos development

Get testnet tokens from faucets:
- [Base Sepolia Faucet](https://docs.base.org/base-chain/tools/network-faucets) to get native Base Sepolia ETH
- [Solana Devnet Faucet](https://faucet.solana.com/) to get Solana Devnet SOL
- [Circle Faucet](https://faucet.circle.com) to get Circle's test tokens (USDC, EURC,...)

##### For Production

Choose based on your users and use case. All supported networks offer low fees suitable for micropayments.

For maximum user reach, accept on multiple chains and let users pay where convenient. You could later bridge/convert tokens, if enough value accumulated on a chain.

#### Configuration

##### FareSide Hosted Facilitator

The FareSide hosted facilitator supports all networks listed above. Simply specify the CAIP-2 network identifier in your price tag:

```typescript
// Hono example (TypeScript)
app.use(paymentMiddleware(
  {
    "/api/data": {
      accepts: [{
        scheme: "exact",
        price: "$0.01",
        network: "eip155:8453", // Base Mainnet
        payTo: "0xYourWallet",
      }],
      description: "Data access"
    }
  },
  server
));
```

```rust
// Axum example (Rust)
// Using helper for Base Mainnet
let usdc = USDC::base();
// Or manual CAIP-2
let chain_id: ChainId = "eip155:8453".parse().unwrap();
```

#### Adding New Networks

FareSide's network support is determined by [x402-rs](https://github.com/x402-rs/x402-rs). To request support for additional networks:

1. Open an issue on [x402-rs GitHub](https://github.com/x402-rs/x402-rs/issues)
2. Or contact us on [Telegram](https://t.me/faresidehq)

#### Next Steps

- **[Quickstart](/docs/quickstart)** - Start accepting payments
- **[Self-Hosting](/docs/self-hosting)** - Run your own facilitator

## Dashboard [Merchants]: Monitor payments, manage organizations, and get your API key
Source: /docs/dashboard.md

import { DocsNotice } from "@/components/docs/DocsNotice";
import { ImageZoom } from "@/components/docs/ImageZoom";

### Overview

FareSide Dashboard is the unified control plane for your x402 payment infrastructure. Everything FareSide handles on your behalf&nbsp;&mdash; verification, settlement, multi-chain routing&nbsp;&mdash; is transparent and manageable from one place.

**What the dashboard provides:**

- _Real-time monitoring_&nbsp;&mdash; Track every facilitation through its full lifecycle: verification → settlement → on-chain confirmation
- _Unified multi-chain balance_&nbsp;&mdash; Accept payments across dozens of networks, view them in a single aggregated view
- _Balance management_&nbsp;&mdash; Top up your organization balance and track all charges directly from the dashboard
- _Agent-native access_&nbsp;&mdash; The same data and controls available to humans are also accessible to AI agents via API (e.g., [OpenClaw](https://openclaw.ai))

<DocsNotice variant="info">
  Organization management, balance aggregation and facilitation monitoring features described below
  are already live. Agent API access is coming soon.
</DocsNotice>

---

### Guide

#### Account

Sign up at [app.fareside.com](https://app.fareside.com). Email + password, with email verification via OTP. Password reset is available at any time from the login screen or via the account settings.

#### Getting Your API Key

##### 1. Create an Organization

At the **Organizations** page and click **Create Organization**. Give it a name&nbsp;&mdash; that's all you need.

<ImageZoom
  client:only
  src="/images/dashboard/orgs.jpg"
  alt="Organizations page — create and manage your orgs"
/>

##### 2. Copy Your Access Token

Open your organization. Your **Access Token** (API key) is displayed on the organization card. Click the copy button or reveal the full token with the eye icon.

<ImageZoom
  client:only
  src="/images/dashboard/org-detail.jpg"
  alt="Organization detail — Access Token and facilitations list"
/>

##### 3. Use It

Your facilitator URL is:

```
https://facilitator.fareside.com/YOUR_API_KEY
```

**TypeScript (Hono):**

```typescript
import { HTTPFacilitatorClient } from "@x402/core/server";

const facilitatorClient = new HTTPFacilitatorClient({
  url: `https://facilitator.fareside.com/${process.env.FARESIDE_API_KEY}`,
});
```

**Rust (Axum):**

```rust
let facilitator_url = format!("https://facilitator.fareside.com/{}", api_key);
```

For full integration guides, see [Setup Guide](/docs/setup-guide), [Hono / Express / Next.js](/docs/express-hono-nextjs), or [Rust (Axum)](/docs/rust-axum).

- Each organization has its own Access Token. Create separate organizations to isolate payment streams (e.g., staging vs. production)
- Once payments start flowing through your API key, you can view [charts](#viewing-charts) with facilitation statistics and [details](#facilitations-list) of each individual facilitation in the dashboard
- A small balance is available right after you create an organization to test your integration

#### Organization Balance

Your organization's balance is displayed on it's page. To see the full pricing breakdown, open the balance details page.

##### Tariff and History

The balance details page shows all top-up and expense operations. For each debit for facilitation, you can navigate to the [facilitation details](#facilitation-details) to see the full breakdown or view transaction in a blockchain explorer.

<ImageZoom
  client:only
  src="/images/dashboard/org-balance-details.jpg"
  alt="Tariff details and balance history"
/>

<DocsNotice variant="info">
  Facilitations on testnet chains do not consume balance and are not shown in charts, but they
  appear in the full [facilitations list](#facilitations-list).
</DocsNotice>

##### Top Up

To add funds, click the **Top Up** button on the organization page or on the balance details page. Payment is made via a crypto wallet with no fees, using x402.

<ImageZoom
  client:only
  src="/images/dashboard/org-balance-top-up.jpg"
  alt="Organization balance top up"
/>

#### Monitoring Facilitations

##### Viewing Charts

The organization page shows charts by time period:

- **Volume** — Total amount paid by buyers, displayed at the current USD exchange rate for each asset. Switch to stacked column view by clicking on **Split Tokens** to see individual asset amounts without USD conversion
- **Transactions** — Number of transactions
- **New Users** — Number of users making their first transaction

Charts can be shifted by date, zoomed in or out, and displayed by range in week, day, or hour.

<ImageZoom
  client:only
  src="/images/dashboard/org-charts.jpg"
  alt="Volume chart by individual asset"
/>

##### Facilitations List

On the organization page click **Facilitations** and it shows a **list of all facilitations** with:

- `Description` and `Network` (chain name)
- `Amount/Asset` (e.g., `0.01 USDC`)
- `From` / `To` addresses
- `Status` badge
- `Date`

Use the **filters** button to narrow by **date range** and/or **status**.

<ImageZoom
  client:only
  src="/images/dashboard/facilitations-table.jpg"
  alt="Facilitations table with status and date filters"
/>

Every facilitation moves through the following states:

| Status    | What's happening                                                            |
| --------- | --------------------------------------------------------------------------- |
| Verifying | Payment payload received; signature and payer balance are being checked     |
| Verified  | Signature is valid; transaction is queued for settlement                    |
| Invalid   | ❌ Verification failed (insufficient funds, bad signature, expired payload) |
| Settling  | Transaction is being prepared and submitted on-chain                        |
| Pending   | Transaction sent; waiting for network confirmation                          |
| Settled   | ✅ Funds have been transferred to your address                              |
| Failed    | ❌ Settlement failed (chain revert, RPC error)                              |

##### Facilitation Details

Click any facilitation to see its full details:

- `ID`, `Status`, `Sender`, `Receiver`, `Network`, `Amount/Asset`
- `Payment Requirements` — schema, resource URL, description, max amount, pay-to address, timeout, extra parameters
- `Payment Payload` — from/to addresses, value, signature data (for permit-style), or raw transaction

For on-chain events, a link to the blockchain explorer is available directly from this page.

<ImageZoom
  client:only
  src="/images/dashboard/facilitation-details.jpg"
  alt="Facilitation details — full breakdown of a single payment"
/>

##### Facilitation Events

Switch to the **Events** tab to see the lifecycle timeline:

- `Type`: Event status (Verifying → Verified → Settling → Pending → Settled)
- `At`: When the event actually occurred
- `Details`: JSON payload with chain-specific data (tx hash, payer, etc.)

<ImageZoom
  client:only
  src="/images/dashboard/facilitation-events.jpg"
  alt="Facilitation events — lifecycle timeline with details"
/>

## Setup Guide [Merchants]: Get your FareSide API key and configure your integration
Source: /docs/setup-guide.md

import { DocsNotice } from "@/components/docs/DocsNotice";

This guide walks you through setting up FareSide to accept x402 payments in your application.

#### Getting Your API Key

Your FareSide API key (Access Token) is available directly in the [FareSide Dashboard](https://app.fareside.com).

1. **Sign up or log in** at [app.fareside.com](https://app.fareside.com)
2. **Create an Organization** — click **Create Organization** and give it a name
3. **Copy your Access Token** — it appears on the organization card, next to the key icon

Your facilitator URL will be:

```
https://facilitator.fareside.com/YOUR_API_KEY
```

<DocsNotice variant="info" title="FareSide Dashboard">
  Unified control plane for your x402 payment infrastructure. [Learn more](/docs/dashboard).
</DocsNotice>

#### Integration Overview

FareSide works with standard x402 middleware libraries. You don't need a FareSide-specific SDK &nbsp;&mdash; just point any x402-compatible middleware to the FareSide facilitator.

##### Supported Middleware

| Framework | Library         | Package                                            |
|-----------|-----------------|----------------------------------------------------|
| Hono      | `@x402/hono`    | [npm](https://www.npmjs.com/package/@x402/hono)    |
| Express   | `@x402/express` | [npm](https://www.npmjs.com/package/@x402/express) |
| Next.js   | `@x402/next`    | [npm](https://www.npmjs.com/package/@x402/next)    |
| Axum      | `x402-axum`     | [crates.io](https://crates.io/crates/x402-axum)    |

You can also see examples for Go, FastAPI and Flask [here](https://docs.x402.org/getting-started/quickstart-for-sellers).

#### TypeScript Setup

##### 1. Install Dependencies

```bash
npm install hono @hono/node-server @x402/hono @x402/core @x402/evm
```

##### 2. Configure the Middleware

```typescript
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { paymentMiddleware } from "@x402/hono";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";

const app = new Hono();

// Setup Facilitator Client
const facilitatorClient = new HTTPFacilitatorClient({
  url: `https://facilitator.fareside.com/${process.env.FARESIDE_API_KEY}`
});

// Your wallet address for receiving payments
const receiverAddress = process.env.RECEIVER_ADDRESS!;

// Setup Resource Server
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);

// Configure Middleware
app.use(paymentMiddleware(
  {
    "GET /api/premium": {
      accepts: [{
        scheme: "exact",
        price: "$0.10",
        network: "eip155:8453", // Base Mainnet
        payTo: receiverAddress,
      }],
      description: "Premium API access",
    },
    "GET /api/data": {
      accepts: [{
        scheme: "exact",
        price: "$0.01",
        network: "eip155:8453",
        payTo: receiverAddress,
      }],
      description: "Regular data",
    }
  },
  server
));

// Your protected routes
app.get("/api/premium", (c) => c.json({ data: "Premium content" }));
app.get("/api/data", (c) => c.json({ data: "Regular data" }));

serve({ fetch: app.fetch, port: 3000 });
```

##### 3. Environment Variables

Create a `.env` file:

```bash
### Your wallet address for receiving payments
RECEIVER_ADDRESS=0xYourWalletAddress

### Your FareSide API key
FARESIDE_API_KEY=YourApiKeyHere
```

#### Rust Setup

##### 1. Add Dependencies

```toml
[dependencies]
alloy-primitives = "1.4"
axum = "0.8"
serde_json = "1"
tokio = "1"
x402-axum = "1.1"
x402-chain-eip155 = { version = "1.1", features = ["server"] }
x402-types = "1.1"
```

##### 2. Configure the Middleware

```rust
use alloy_primitives::Address;
use axum::{routing::get, Json, Router};
use std::env;
use std::str::FromStr;
use x402_axum::X402Middleware;
use x402_chain_eip155::{KnownNetworkEip155, V2Eip155Exact};
use x402_types::networks::USDC;

###[tokio::main]
async fn main() {
    let api_key = env::var("FARESIDE_API_KEY").expect("FARESIDE_API_KEY required");
    let receiver_str = env::var("RECEIVER_ADDRESS").expect("RECEIVER_ADDRESS required");
    let receiver = Address::from_str(&receiver_str).expect("Invalid address");

    // Configure FareSide facilitator
    let facilitator_url = format!("https://facilitator.fareside.com/{}", api_key);
    let x402 = X402Middleware::try_from(facilitator_url.as_str()).unwrap();

    let usdc = USDC::base();

    let app = Router::new()
        // Protected route with $0.10 price
        .route(
            "/api/premium",
            get(premium_handler).layer(
                x402.with_price_tag(
                    V2Eip155Exact::price_tag(
                        receiver,
                        usdc.amount(100_000u64), // 0.10 USDC (6 decimals)
                    )
                )
                .with_description("Premium API access".to_string()),
            ),
        )
        // Protected route with $0.01 price
        .route(
            "/api/data",
            get(data_handler).layer(
                x402.with_price_tag(
                    V2Eip155Exact::price_tag(
                        receiver,
                        usdc.amount(10_000u64), // 0.01 USDC
                    )
                )
                .with_description("Regular data".to_string()),
            ),
        );

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn premium_handler() -> Json<serde_json::Value> {
    Json(serde_json::json!({ "data": "Premium content" }))
}

async fn data_handler() -> Json<serde_json::Value> {
    Json(serde_json::json!({ "data": "Regular data" }))
}
```

##### 3. Environment Variables

```bash
export RECEIVER_ADDRESS=0xYourWalletAddress
export FARESIDE_API_KEY=YourApiKeyHere
```

#### Wallet Address

To receive x402 payments, you just need a wallet address. No special wallet type or configuration is required.

You can get an address however you prefer:
- **Browser wallets** - MetaMask, Zerion, Rainbow, Coinbase Wallet
- **Hardware wallets** - Ledger, Trezor
- **CLI-generated** - Using `cast wallet new` or similar tools
- **Any other method** - The address just needs to be valid for the target network

For EVM networks, use an Ethereum-compatible address (0x...). For Solana, use a Solana public key.
No private keys are needed on your server, only the public address where you want to receive funds.

#### Testing Your Integration

##### 1. Start Your Server

```bash
### TypeScript
npx tsx server.ts

### Rust
cargo run
```

##### 2. Test Without Payment

```bash
curl -i http://localhost:3000/api/data
```

Expected response (`402 Payment Required`):
```http
HTTP/1.1 402 Payment Required
Payment-Required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQYXltZW50LVNpZ25hdHVyZSBoZWF...
Content-Type: application/json
...
```

The `Payment-Required` header contains the base64-encoded JSON object. Decoded, it looks like this:

```json
{
  "x402Version": 2,
  "error": "Payment-Signature header is required",
  "resource": {
    "url": "http://localhost:3000/api/data",
    "description": "Regular data",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "10000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0xYourWalletAddress",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USD Coin",
        "version": "2"
      }
    }
  ]
}
```

##### 3. Test With Payment

Use an x402 client to make actual payments. See [Making Payments](/docs/making-payments) for client setup.

#### Troubleshooting

- Check your API key is correct
- Verify network connectivity to `facilitator.fareside.com`
- Ensure you're using a [supported network](/docs/supported-networks)
- Check CAIP-2 network ID spelling (e.g., `eip155:8453`)
- Verify the payment amount matches the price
- Ensure the transaction is confirmed on-chain
- Contact [support](https://t.me/faresidehq) if issues persist

#### Next Steps

- **[Express/Hono/Next.js](/docs/express-hono-nextjs)** - Detailed TypeScript examples
- **[Rust (Axum)](/docs/rust-axum)** - Detailed Rust examples
- **[Self-Hosting](/docs/self-hosting)** - Run your own facilitator

## Hono / Express / Next.js [Merchants]: TypeScript integration examples for popular frameworks
Source: /docs/express-hono-nextjs.md

### Hono

[Hono](https://hono.dev) is a lightweight, fast web framework that works great with x402.

#### Installation

```bash
npm install hono @hono/node-server @x402/hono @x402/core @x402/evm
```

#### Basic Setup

```typescript
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { paymentMiddleware } from "@x402/hono";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";

const app = new Hono();

// Your receiving wallet address
const payTo = "0xYourAddress";

// Configure Facilitator Client
const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://facilitator.fareside.com/YOUR_API_KEY",
});

// Create Resource Server & Register Schemes
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);

// Configure Middleware
app.use(paymentMiddleware(
  {
    "GET /weather": {
      accepts: [{
        scheme: "exact",
        price: "$0.01",
        network: "eip155:84532", // Base Sepolia
        payTo,
      }],
      description: "Access to weather data",
      mimeType: "application/json",
    },
  },
  server,
));

// Implement your route
app.get("/weather", (c) => c.json({
  report: {
    weather: "sunny",
    temperature: 70,
  },
}));

serve({ fetch: app.fetch, port: 3000 });
```

### Express

For existing [Express](https://expressjs.com/) applications, use the `@x402/express` middleware.

#### Installation

```bash
npm install express @x402/express @x402/core @x402/evm
```

#### Basic Setup

```typescript
import express from "express";
import { paymentMiddleware } from "@x402/express";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";

const app = express();

// Your receiving wallet address
const payTo = "0xYourAddress";

// Configure Facilitator Client
const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://facilitator.fareside.com/YOUR_API_KEY",
});

// Create Resource Server & Register Schemes
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);

// Configure Middleware
app.use(paymentMiddleware(
  {
    "GET /weather": {
      accepts: [{
        scheme: "exact",
        price: "$0.01",
        network: "eip155:84532", // Base Sepolia
        payTo,
      }],
      description: "Access to weather data",
      mimeType: "application/json",
    },
  },
  server,
));

// Implement your route
app.get("/weather", (req, res) => {
  res.send({
    report: {
      weather: "sunny",
      temperature: 70,
    },
  });
});

app.listen(3000, () => {
  console.log(`Server listening at http://localhost:3000`);
});
```

### Next.js

For [Next.js](https://nextjs.org) applications, use the `@x402/next` middleware package.

#### Installation

```bash
npm install @x402/next @x402/core @x402/evm
```

#### Middleware Setup

Create a `middleware.ts` file in your project root:

```typescript
// middleware.ts
import { paymentProxy } from "@x402/next";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { registerExactEvmScheme } from "@x402/evm/exact/server";

// Your receiving wallet address
const payTo = "0xYourAddress";

// Configure Facilitator Client
const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://facilitator.fareside.com/YOUR_API_KEY",
});

// Create Resource Server & Register Schemes
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);

export const middleware = paymentProxy(
  {
    "/api/protected": {
      accepts: [{
        scheme: "exact",
        price: "$0.01",
        network: "eip155:84532", // Base Sepolia
        payTo,
      }],
      description: "Access to protected content",
      mimeType: "application/json",
    },
  },
  server,
);

export const config = {
  matcher: ["/api/protected/:path*"],
};
```

#### Route Handler

```typescript
// app/api/protected/route.ts
import { NextResponse } from "next/server";

export async function GET() {
  // This route is protected by the middleware
  // Payment verification happens automatically
  return NextResponse.json({
    data: "Protected content",
    timestamp: new Date().toISOString(),
  });
}
```

### Configuration Options

The route configuration object accepts the following options:

```typescript
interface RouteConfig {
  accepts: Array<{
    scheme: string;           // Payment scheme (e.g., "exact")
    price: string;            // Price in dollars (e.g., "$0.01")
    network: string;          // Network in CAIP-2 format (e.g., "eip155:84532")
    payTo: string;            // Your wallet address
  }>;
  description?: string;       // Description of the resource
  mimeType?: string;          // MIME type of the response
  extensions?: object;        // Optional extensions (e.g., Bazaar)
}
```

### Dynamic Pricing

Instead of a constant value for `price`, you can provide a function that calculates it based on request parameters:

```typescript
app.use(paymentMiddleware(
  {
    "GET /api/data": {
      accepts: [{
        price: ctx => {
          const isDiscount = !!ctx.adapter.getQueryParam?.("discount");
          return isDiscount ? "$0.05" : "$0.10";
        },
        /* ... */
      }],
    },
  },
  /* ... */
));
```

### Testing Your Integration

1. Make a request to your endpoint (e.g., `curl -i http://localhost:3000/weather`)
2. The server responds with a **402 Payment Required**, including payment instructions in the base64 encoded `Payment-Required` header, which you can decode and inspect with:
    ```bash
    echo "<Payment-Required header>" | base64 -d | jq
    ```
3. Complete the payment using a compatible client, wallet, or automated agent
    - See [Making Payments](/docs/making-payments) for client setup
4. Retry the request with the `Payment-Signature` header containing the payment payload
5. The server verifies the payment via the facilitator and returns your API response

### Next Steps

- **[Rust (Axum)](/docs/rust-axum)** - Rust integration examples
- **[Making Payments](/docs/making-payments)** - Build x402 clients

## Rust (Axum) [Merchants]: Rust integration with Axum framework using x402-axum
Source: /docs/rust-axum.md

Complete examples for integrating x402 payments with Rust using the [x402-axum](https://crates.io/crates/x402-axum) middleware and FareSide facilitator.

#### Dependencies

Add these to your `Cargo.toml`:

```toml
[dependencies]
alloy-primitives = "1.4"
axum = "0.8"
serde_json = "1"
solana-pubkey = "4.0"
tokio = { version = "1", features = ["full"] }
x402-axum = "1.0"
x402-chain-eip155 = { version = "1.0", features = ["server"] }
x402-chain-solana = { version = "1.0", features = ["server"] }
x402-types = "1.0"
```

#### Basic Setup

```rust
use axum::{routing::get, Router};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use serde_json::json;
use alloy_primitives::address;
use x402_axum::X402Middleware;
use x402_chain_eip155::{V2Eip155Exact, KnownNetworkEip155};
use x402_types::networks::USDC;

###[tokio::main]
async fn main() {
    // Configure FareSide facilitator
    let x402 = X402Middleware::try_from(
        "https://facilitator.fareside.com/YOUR_API_KEY"
    ).unwrap();

    // Configure USDC on Base Sepolia with your receiving address
    let usdc = USDC::base_sepolia();
    let wallet = address!("0xYourWalletAddress");

    let app = Router::new()
        .route("/weather", get(weather_handler).layer(
            x402.with_price_tag(
                V2Eip155Exact::price_tag(
                    wallet,
                    usdc.amount(10_000u64) // 0.01 USDC
                )
            )
            .with_description("Weather API access".to_string())
            .with_mime_type("application/json".to_string())
        ));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    println!("Server running on http://localhost:3000");
    axum::serve(listener, app).await.unwrap();
}

async fn weather_handler() -> impl IntoResponse {
    (StatusCode::OK, axum::Json(json!({
        "location": "San Francisco",
        "temperature": 68,
        "conditions": "Sunny"
    })))
}
```

#### Multiple Networks

Accept payments on multiple networks for the same endpoint using `.with_price_tag()` multiple times:

```rust
use axum::{routing::get, Router};
use alloy_primitives::address;
use solana_pubkey::pubkey;
use x402_axum::X402Middleware;
use x402_chain_eip155::{V2Eip155Exact, KnownNetworkEip155};
use x402_chain_solana::{V2SolanaExact, KnownNetworkSolana};
use x402_types::networks::USDC;

###[tokio::main]
async fn main() {
    let x402 = X402Middleware::try_from(
        "https://facilitator.fareside.com/YOUR_API_KEY"
    ).unwrap();

    let evm_wallet = address!("0xYourEVMWallet");
    let sol_wallet = pubkey!("YourSolanaWalletAddress");

    let app = Router::new().route(
        "/api/data",
        get(data_handler).layer(
            // Accept payment on Base ...
            x402.with_price_tag(V2Eip155Exact::price_tag(
                evm_wallet,
                USDC::base().amount(10_000u64),
            ))
            // ... OR Solana
            .with_price_tag(V2SolanaExact::price_tag(
                sol_wallet,
                USDC::solana().amount(10_000u64),
            ))
            .with_description("Multi-chain API".to_string()),
        ),
    );

    /* ... serve app ... */
}
```

#### Dynamic Pricing

Use `with_dynamic_price` to calculate price based on request parameters:

```rust
    let app = Router::new().route(
        "/api/data",
        get(handler).layer(x402.with_dynamic_price(|_headers, uri, _base_url| {
            // Check for discount query parameter
            let has_discount = uri.query().map(|q| q.contains("discount")).unwrap_or(false);
            let amount = if has_discount { 5_000u64 } else { 10_000u64 };

            async move {
                vec![V2Eip155Exact::price_tag(
                    address!("0xYourWallet"),
                    USDC::base().amount(amount),
                )]
            }
        })),
    );
```

#### Telemetry

`X402Middleware` supports telemetry via the [`tracing`](https://crates.io/crates/tracing) crate. When you have a tracing subscriber configured in your application, the middleware will automatically emit spans and events for payment verification, facilitator communication, and error handling.

Simply add `tracing` and your preferred subscriber to your dependencies:

```toml
[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"
```

The middleware integrates seamlessly with any tracing-compatible observability stack (OpenTelemetry, Jaeger, Datadog, etc.).

#### Full Example

See the complete working example at [x402-axum-example](https://github.com/x402-rs/x402-rs/tree/main/examples/x402-axum-example).

#### Next Steps

- **[Making Payments](/docs/making-payments)** - Build x402 clients with x402-reqwest
- **[Self-Hosting](/docs/self-hosting)** - Run your own facilitator

## Making Payments [Clients]: Learn how to build x402 clients that can pay for services
Source: /docs/making-payments.md

x402 clients automatically handle the payment flow:

1. Make a request to a protected endpoint
2. Receive `402 Payment Required` with payment details in `Payment-Required` header
3. Create and sign a payment transaction
4. Retry the request with payment proof in `Payment-Signature` header
5. Receive the requested resource

#### Rust (x402-reqwest)

The [x402-reqwest](https://crates.io/crates/x402-reqwest) crate provides middleware for reqwest that handles x402 payments automatically.

##### Installation

```toml
[dependencies]
alloy-signer-local = "1.4"
reqwest = "0.13"
solana-client = "3.1.4"
solana-keypair = "3.1.0"
tokio = "1"
x402-chain-eip155 = { version = "1.1", features = ["client"] }
x402-chain-solana = { version = "1.1", features = ["client"] }
x402-reqwest = "1.1"
```

##### EVM Payments (Base, Ethereum, etc.)

```rust
use alloy_signer_local::PrivateKeySigner;
use reqwest::Client;
use std::sync::Arc;
use x402_reqwest::{ReqwestWithPayments, ReqwestWithPaymentsBuild, X402Client};
use x402_chain_eip155::{V1Eip155ExactClient, V2Eip155ExactClient};

###[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load your wallet (never hardcode real keys!)
    let signer: PrivateKeySigner = "0x...".parse()?;
    let signer = Arc::new(signer);

    // Create x402 client and register EVM schemes
    let x402_client = X402Client::new()
        .register(V1Eip155ExactClient::new(signer.clone()))
        .register(V2Eip155ExactClient::new(signer));

    // Build reqwest client with middleware
    let http_client = Client::new()
        .with_payments(x402_client)
        .build();

    // Make request - payment handled automatically
    let response = http_client
        .get("http://localhost:3000/protected-route")
        .send()
        .await?;

    println!("Response: {:?}", response.text().await?);

    Ok(())
}
```

##### Solana Payments

```rust
use reqwest::Client;
use solana_keypair::Keypair;
use solana_client::nonblocking::rpc_client::RpcClient;
use std::sync::Arc;
use x402_reqwest::{ReqwestWithPayments, ReqwestWithPaymentsBuild, X402Client};
use x402_chain_solana::{V1SolanaExactClient, V2SolanaExactClient};

###[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load Solana wallet
    let keypair = Keypair::from_base58_string("...");
    let keypair = Arc::new(keypair);

    let rpc_client = Arc::new(RpcClient::new("https://api.mainnet-beta.solana.com".to_string()));

    // Create x402 client and register Solana schemes
    let x402_client = X402Client::new()
        .register(V1SolanaExactClient::new(keypair.clone(), rpc_client.clone()))
        .register(V2SolanaExactClient::new(keypair, rpc_client));

    // Build reqwest client
    let http_client = Client::new()
        .with_payments(x402_client)
        .build();

    // Make request
    let response = http_client
        .get("http://localhost:3000/protected-route")
        .send()
        .await?;

    println!("Response: {:?}", response.text().await?);

    Ok(())
}
```

##### Configuration

Payment Selection allows for more flexible payment strategies.

**Preferring specific networks:**

Use `PreferChain` to prioritize certain networks (e.g., Base) over others.

```rust
use x402_types::scheme::client::PreferChain;

// Prefer Base Mainnet, then any EVM chain, then anything else
let selector = PreferChain::new(vec![
    "eip155:8453".parse().unwrap(), // Base Mainnet
    "eip155:*".parse().unwrap(),    // Any EVM chain
]);

let client = X402Client::new()
    .with_selector(selector)
    .register(...);
```

**Limiting Payment Amount:**

Use `MaxAmount` to reject payments exceeding a certain value.

```rust
use x402_types::scheme::client::MaxAmount;
use alloy_primitives::U256;

// Limit payments to 1 USDC (1_000_000 atomic units)
let client = X402Client::new()
    .with_selector(MaxAmount(U256::from(1_000_000)))
    .register(...);
```

**Custom Logic:**

You can implement the `PaymentSelector` trait to define custom logic (e.g., "Prefer Solana if amount < $1, otherwise Base").

#### TypeScript / JavaScript

For TypeScript clients, use `@x402/fetch` or `@x402/axios`.

##### Installation

```bash
### Using fetch
npm install @x402/fetch @x402/core @x402/evm viem

### Or using Axios
npm install @x402/axios @x402/core @x402/evm axios viem
```

##### Using @x402/fetch

`@x402/fetch` extends the native `fetch` API to handle 402 responses automatically.

```typescript
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

// Create signer
const signer = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

// Create x402 client and register EVM scheme
const client = new x402Client();
registerExactEvmScheme(client, { signer });

// Wrap fetch with payment handling
const fetchWithPayment = wrapFetchWithPayment(fetch, client);

// Make request - payment is handled automatically
const response = await fetchWithPayment("https://api.example.com/paid-endpoint", {
  method: "GET",
});

const data = await response.json();
console.log("Response:", data);
```

##### Using @x402/axios

`@x402/axios` adds a payment interceptor to Axios.

```typescript
import { x402Client, wrapAxiosWithPayment } from "@x402/axios";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import axios from "axios";

// Create signer
const signer = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

// Create x402 client and register EVM scheme
const client = new x402Client();
registerExactEvmScheme(client, { signer });

// Create an Axios instance with payment handling
const api = wrapAxiosWithPayment(
  axios.create({ baseURL: "https://api.example.com" }),
  client,
);

// Make request - payment is handled automatically
const response = await api.get("/paid-endpoint");
console.log("Response:", response.data);
```

##### Using with MCP (AI Agents)

For AI agents using Model Context Protocol, you can wrap the transport or use the client directly in your tool implementation.

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import axios from "axios";
import { x402Client, wrapAxiosWithPayment } from "@x402/axios";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { base58 } from "@scure/base";

const evmPrivateKey = process.env.EVM_PRIVATE_KEY as `0x${string}`;
const svmPrivateKey = process.env.SVM_PRIVATE_KEY as string;
const baseURL = process.env.RESOURCE_SERVER_URL || "http://localhost:3000";
const endpointPath = process.env.ENDPOINT_PATH || "/weather";

if (!evmPrivateKey && !svmPrivateKey) {
  throw new Error("At least one of EVM_PRIVATE_KEY or SVM_PRIVATE_KEY must be provided");
}

/**
 * Creates an axios client configured with x402 payment support for EVM and/or SVM.
 */
async function createClient() {
  const client = new x402Client();

  // Register EVM scheme if private key is provided
  if (evmPrivateKey) {
    const evmSigner = privateKeyToAccount(evmPrivateKey);
    registerExactEvmScheme(client, { signer: evmSigner });
  }

  // Register SVM scheme if private key is provided
  if (svmPrivateKey) {
    const svmSigner = await createKeyPairSignerFromBytes(base58.decode(svmPrivateKey));
    registerExactSvmScheme(client, { signer: svmSigner });
  }

  return wrapAxiosWithPayment(axios.create({ baseURL }), client);
}

async function main() {
  const api = await createClient();

  // Create an MCP server
  const server = new McpServer({
    name: "x402 MCP Client Demo",
    version: "2.0.0",
  });

  // Add a tool that calls the paid API
  server.registerTool(
    "get-data-from-resource-server",
    {},
    async () => {
      const res = await api.get(endpointPath);
      return {
        content: [{ type: "text", text: JSON.stringify(res.data) }],
      };
    },
  );

  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(error => {
  console.error(error);
  process.exit(1);
});
```

##### Get Testnet Tokens

For dev purposes, you could get testnet tokens from faucets:
- [Base Sepolia Faucet](https://docs.base.org/base-chain/tools/network-faucets) to get native Base Sepolia ETH
- [Solana Devnet Faucet](https://faucet.solana.com/) to get Solana Devnet SOL
- [Circle Faucet](https://faucet.circle.com) to get Circle's test tokens (USDC, EURC,...)

## EVM Asset Transfer Methods [Core Concepts]: How EIP-3009 and Permit2 transfers work in x402 on EVM chains
Source: /docs/evm-asset-transfer-methods.md

import { DocsNotice } from "@/components/docs/DocsNotice";

On EVM networks, x402 supports two ways to move ERC-20 tokens gaslessly: **EIP-3009** and **Permit2**. Both let the client authorize a transfer by signing a message off-chain, while the facilitator submits the resulting transaction and pays the gas. The choice depends on whether the token natively supports `transferWithAuthorization`.

#### Background: Vanilla ERC-20

A standard ERC-20 token only has `transfer()` and `transferFrom()`. To allow a third party (like a facilitator) to move your tokens, you must first submit an on-chain `approve()` transaction — paying gas. After that, the approved address can call `transferFrom()` on your behalf.

This two-step process (`approve` + `transferFrom`) is not ideal for x402 because:

- The client must hold native gas tokens to submit the approval.
- Each approval is a separate on-chain transaction.
- The approved spender can move up to the approved amount at any time — with no per-transfer authorization.

Both EIP-3009 and Permit2 solve this by moving the authorization step off-chain.

#### EIP-3009: `transferWithAuthorization`

[EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) extends ERC-20 with a function that accepts a signed message authorizing a single transfer. The function is built into the token contract itself.

##### How It Works

```mermaid
sequenceDiagram
    participant Client
    participant Seller as Resource Server (Seller)
    participant Facilitator
    participant Token as ERC-20 Contract (EIP-3009)

    Client->>Seller: 1. Request resource
    Seller-->>Client: 2. 402 Payment Required (with PaymentRequirements)
    Note over Client: 3. Sign EIP-712 TransferWithAuthorization
    Client->>Seller: 4. Retry with signed PaymentPayload
    Seller->>Facilitator: 5. Verify signature, balance, simulate
    Facilitator-->>Seller: 6. Valid
    Note over Seller: 7. Fulfill request
    Seller->>Facilitator: 8. Settle
    Facilitator->>Token: 9. transferWithAuthorization(...)
    Token-->>Facilitator: 10. Transfer executed
    Facilitator-->>Seller: 11. Settlement confirmed (tx hash)
    Seller-->>Client: 12. Response + PAYMENT-RESPONSE header
```

##### Signature Structure

The client signs [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data with these fields:

```javascript
TransferWithAuthorization: [
  { name: "from",        type: "address" },
  { name: "to",          type: "address" },
  { name: "value",       type: "uint256" },
  { name: "validAfter",  type: "uint256" },
  { name: "validBefore", type: "uint256" },
  { name: "nonce",       type: "bytes32" },
]
```

The **EIP-712 domain** requires `name` and `version` to match the values hardcoded in the token contract (e.g., `name: "USDC"`, `version: "2"` for USDC). These are passed in the `extra` field of `PaymentRequirements`.

##### Security Properties

- **Per-transfer authorization**: Each signature authorizes exactly one transfer of a specific amount to a specific recipient. It cannot be reused after execution.
- **Time-bounded**: `validAfter` and `validBefore` define the window during which the authorization can be executed.
- **Unique nonce**: A random 32-byte nonce prevents replay attacks, enforced at the smart contract level.
- **Facilitator cannot alter anything**: Amount, recipient, and sender are all part of the signed message. Any change invalidates the signature.

##### `extra` Fields

| Field                 | Required | Default     | Description                                            |
|-----------------------|----------|-------------|--------------------------------------------------------|
| `name`                | Recommended | — | EIP-712 domain `name` from the token contract          |
| `version`             | Recommended | — | EIP-712 domain `version` from the token contract       |
| `assetTransferMethod` | No       | `eip3009`   | Can be omitted — `eip3009` is the default value        |

<DocsNotice variant='info' title='Note'>
  If `name` and `version` are omitted, the facilitator will query the token contract to determine them. This adds an extra RPC call and is less efficient, but functionally equivalent.
</DocsNotice>

##### Supported Tokens

EIP-3009 is implemented by a limited set of tokens. Known examples:

| Token | Networks                          |
|-------|-----------------------------------|
| USDC  | Base, Polygon, Avalanche, and other supported EVM chains |
| EURC  | Base, Avalanche                   |

---

#### Permit2: Universal ERC-20 Support

For tokens that do **not** implement EIP-3009, x402 uses [Uniswap's Permit2](https://docs.uniswap.org/contracts/permit2/overview) — a canonical contract deployed at the same address on all major EVM chains.

Permit2 introduces a signature-based transfer mechanism for **any** ERC-20 token. However, it requires a one-time on-chain approval to the Permit2 contract.

##### One-Time Setup

Before a client can use Permit2-based payments, they must approve the Permit2 contract to spend their tokens:

```
token.approve(PERMIT2_ADDRESS, type(uint256).max)
```

This is a single on-chain transaction that pays gas. After this, all subsequent x402 payments with that token are gasless.

<DocsNotice variant='success' title='Gasless Approval'>
  If the facilitator supports it, even this one-time approval can be gasless — see [EIP-2612 Gas Sponsoring](/docs/eip2612-gas-sponsoring) for tokens with EIP-2612 support, or the [`erc20ApprovalGasSponsoring` extension](https://github.com/coinbase/x402/blob/main/specs/extensions/erc20_gas_sponsoring.md) for any ERC-20 token.
</DocsNotice>

##### Why `x402Permit2Proxy`?

If the facilitator called Permit2 directly, the `spender` in the Permit2 signature would be the facilitator's address. Permit2's `permitTransferFrom` lets the `spender` choose where to send the tokens and how much. This means a malicious or compromised facilitator could redirect funds or change the amount.

The **`x402Permit2Proxy`** contract solves this by acting as the `spender` instead of the facilitator. The proxy contract uses Permit2's [**Witness pattern**](https://docs.uniswap.org/contracts/permit2/reference/signature-transfer#single-permitwitnesstransferfrom) — extra data included in the signed message that the proxy enforces on-chain:

```solidity
struct Witness {
    address to;         // Destination address — immutable once signed
    uint256 validAfter; // Earliest time the payment can be settled
    bytes extra;        // Reserved for extensions
}
```

The proxy contract **reads `to` from the witness** and uses it as the transfer destination. The facilitator has no ability to override this because:

1. The `to` address is part of the signed message. Changing it invalidates the signature.
2. The proxy contract is the only authorized `spender` in the Permit2 signature. The facilitator calls the proxy, not Permit2 directly.

##### How It Works (Exact Scheme)

```mermaid
sequenceDiagram
    participant Client
    participant Seller as Resource Server (Seller)
    participant Facilitator
    participant Proxy as x402ExactPermit2Proxy
    participant P2 as Permit2 Contract
    participant Token as ERC-20 Contract

    Note over Client: Prerequisite: token.approve(Permit2, MAX) — one time
    Client->>Seller: 1. Request resource
    Seller-->>Client: 2. 402 Payment Required
    Note over Client: 3. Sign permitWitnessTransferFrom<br/>(spender = Proxy, witness.to = payTo)
    Client->>Seller: 4. Retry with signed PaymentPayload
    Seller->>Facilitator: 5. Verify
    Facilitator-->>Seller: 6. Valid
    Note over Seller: 7. Fulfill request
    Seller->>Facilitator: 8. Settle
    Facilitator->>Proxy: 9. settle(...)
    Proxy->>P2: 10. permitWitnessTransferFrom(...)
    P2->>Token: 11. transferFrom(...)
    Token-->>P2: 12. Transfer executed
    P2-->>Proxy: 13. Success
    Proxy-->>Facilitator: 14. Settled
    Facilitator-->>Seller: 15. Settlement confirmed (tx hash)
    Seller-->>Client: 16. Response
```

##### Why the Facilitator Cannot Cheat

The trust model is designed so the facilitator is a **transaction broadcaster**, not a custodian:

| Attack Vector                 | Protection                                                                                  |
|-------------------------------|---------------------------------------------------------------------------------------------|
| Change destination address    | `witness.to` is signed by the client and enforced by the proxy contract                     |
| Call Permit2 directly         | The client's signature specifies the proxy as the `spender`, not the facilitator             |
| Replay the signature          | Permit2 nonces are single-use; the contract rejects already-used nonces                     |
| Delay settlement indefinitely | `deadline` in the Permit2 signature enforces an upper time bound; `witness.validAfter` enforces a lower bound |

##### `extra` Fields

| Field                 | Required | Description                                            |
|-----------------------|----------|--------------------------------------------------------|
| `assetTransferMethod` | **Yes**  | Must be `permit2`                                      |

##### PaymentPayload Structure

When `assetTransferMethod=permit2`, the `payload` contains `permit2Authorization` instead of `authorization`:

```json
{
  "payload": {
    "signature": "0x...",
    "permit2Authorization": {
      "permitted": {
        "token": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
        "amount": "10000"
      },
      "from": "0x857b06519E91e3A54538791bDbb0E22373e36b66",
      "spender": "0x<x402Permit2Proxy address>",
      "nonce": "0xf374...3480",
      "deadline": "1740672154",
      "witness": {
        "to": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
        "validAfter": "1740672089",
        "extra": {}
      }
    }
  }
}
```

Key differences from EIP-3009 payload:
- `spender` is the `x402Permit2Proxy` contract, not the facilitator.
- `witness.to` determines where funds go — enforced by the proxy, not by the facilitator.
- `deadline` replaces `validBefore` as the upper time bound.

#### Exact vs Upto (Permit2 only)

The `exact` scheme always transfers the full authorized amount. The `upto` scheme allows settling for **less or equal** than the authorized maximum — useful for usage-based pricing (LLM token charges, bandwidth metering, etc.). For a comprehensive guide to the `upto` scheme, see the [Upto Scheme](/docs/upto-scheme) documentation.

| Scheme | `assetTransferMethod` | Amount settled | Proxy contract |
|--------|-----------------------|----------------|----------------|
| `exact` | `eip3009` | Exactly `authorization.value` | None (direct token call) |
| `exact` | `permit2` | Exactly `permit.permitted.amount` | `x402ExactPermit2Proxy` |
| `upto` | `permit2` only | Any amount ≤ `permit.permitted.amount` | `x402UptoPermit2Proxy` |

<DocsNotice variant='warning' title='Important'>
  EIP-3009 does **not** support the `upto` scheme. `transferWithAuthorization` requires the exact amount at signature time — there is no way to settle for less.
</DocsNotice>

#### Choosing the Right Method

| Criteria              | EIP-3009                          | Permit2                                 |
|-----------------------|-----------------------------------|-----------------------------------------|
| Token support         | Only tokens with EIP-3009 (USDC, EURC, etc.) | Any ERC-20 token                 |
| Client setup          | None                              | One-time `approve()` to Permit2         |
| Gas for client        | Zero                              | One-time gas for approval               |
| Payload complexity    | Lower                             | Higher (Permit2 + Witness)              |
| Smart contract trust  | Token contract only               | Permit2 + x402Permit2Proxy              |
| Scheme support        | `exact` only                      | `exact` and `upto`                      |
| Default in x402       | Yes (`assetTransferMethod` default) | Must be explicitly set               |

For most integrations using USDC or EURC, EIP-3009 is the simplest path. For other ERC-20 tokens, or when you need the `upto` scheme, use Permit2.

#### Further Reading

- [EIP-3009 specification](https://eips.ethereum.org/EIPS/eip-3009)
- [EIP-712 specification](https://eips.ethereum.org/EIPS/eip-712)
- [Uniswap Permit2 documentation](https://docs.uniswap.org/contracts/permit2/overview)
- [x402 scheme: exact on EVM](https://github.com/coinbase/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md)
- [x402 scheme: upto on EVM](https://github.com/coinbase/x402/blob/main/specs/schemes/upto/scheme_upto_evm.md)
- [x402Permit2Proxy source code](https://github.com/coinbase/x402/tree/main/contracts/evm/src) — abstract and scheme specific realizations

## Upto Scheme [Core Concepts]: Usage-based payments with maximum amount authorization
Source: /docs/upto-scheme.md

import { DocsNotice } from "@/components/docs/DocsNotice";

The `upto` scheme lets a client authorize a **maximum amount**, while the actual charge is determined at settlement time based on resource consumption. This is designed for scenarios where the final cost isn't known until after the request is fulfilled.

#### Use Cases

- **LLM token generation** — client authorizes up to $5, actual charge based on tokens produced
- **Bandwidth metering** — pay per byte transferred, up to a cap
- **Dynamic compute** — authorize max compute cost, pay for actual resources consumed

#### How It Differs from `exact`

| Aspect | `exact` | `upto` |
|--------|---------|--------|
| Settlement amount | Always the full authorized amount | Any amount ≤ authorized maximum |
| Who determines amount | Fixed at authorization time | Server determines at settlement time |
| Zero settlement | Not applicable | Allowed — no on-chain transaction |
| Asset transfer methods | EIP-3009 or Permit2 | **Permit2 only** |
| Chain support | EVM, Solana, Aptos, ... | **EVM only** |

<DocsNotice variant='info' title='Why Permit2 only?'>
  EIP-3009's `transferWithAuthorization` requires the **exact** amount at signature time — the signed value cannot differ from the settled value. Permit2's `permitWitnessTransferFrom` allows the actual transfer amount to be ≤ the signed maximum, which is what makes the `upto` scheme possible.
</DocsNotice>

#### Payment Flow

```mermaid
sequenceDiagram
    participant Client
    participant Seller as Resource Server (Seller)
    participant Facilitator
    participant Proxy as x402UptoPermit2Proxy
    participant P2 as Permit2 Contract
    participant Token as ERC-20 Contract

    Note over Client: Prerequisite:<br/>token.approve(Permit2, MAX) — one time
    Client->>Seller: 1. Request resource
    Seller-->>Client: 2. 402 Payment Required<br/>(scheme: "upto", amount: max)
    Note over Client: 3. Sign permitWitnessTransferFrom<br/>(amount = max authorized)
    Client->>Seller: 4. Retry with<br/>signed PaymentPayload
    Seller->>Facilitator: 5. Verify payment
    Facilitator-->>Seller: 6. Valid
    Note over Seller: 7. Fulfill request,<br/>measure actual consumption
    Seller->>Facilitator: 8. Settle<br/>(actualAmount ≤ max)
    alt actualAmount > 0
        Facilitator->>Proxy: 9. settle(...)
        Proxy->>P2: 10. permitWitnessTransferFrom(...)
        P2->>Token: 11. transferFrom(...)
        Token-->>P2: 12. Transfer executed
        P2-->>Proxy: 13. Success
        Proxy-->>Facilitator: 14. Settled
    else actualAmount = 0
        Note over Facilitator: No on-chain transaction
    end
    Facilitator-->>Seller: 15. SettlementResponse<br/>(with actual amount)
    Seller-->>Client: 16. Response
```

##### Zero Settlement

When the actual consumption is zero (e.g., the request failed or produced no output), the facilitator returns success with `amount: "0"` and an empty transaction hash. No on-chain transaction is submitted — the Permit2 nonce is **not** consumed, but the authorization expires naturally via its `deadline`.

#### PaymentRequirements

The server advertises the `upto` scheme with the maximum amount the client should authorize:

```json
{
  "scheme": "upto",
  "network": "eip155:84532",
  "amount": "5000000",
  "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
  "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
  "maxTimeoutSeconds": 300
}
```

The `amount` field represents the **maximum** the client may be charged. The `extra` fields can be empty or omitted entirely.

#### PaymentPayload

The client signs a Permit2 `permitWitnessTransferFrom` authorization with `permitted.amount` set to the maximum:

```json
{
  "x402Version": 2,
  "accepted": {
    "scheme": "upto",
    "network": "eip155:84532",
    "amount": "5000000",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
    "maxTimeoutSeconds": 300
  },
  "payload": {
    "signature": "0x...",
    "permit2Authorization": {
      "permitted": {
        "token": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
        "amount": "5000000"
      },
      "from": "0x857b06519E91e3A54538791bDbb0E22373e36b66",
      "spender": "0x4020633461b2895a48930ff97ee8fcde8e520002",
      "nonce": "0xf374...3480",
      "deadline": "1740672154",
      "witness": {
        "to": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
        "validAfter": "1740672089",
        "extra": {}
      }
    }
  }
}
```

Key observations:
- The `spender` is the `x402UptoPermit2Proxy` contract (`0x402...0002`), not the facilitator.
- `witness.to` cryptographically binds the recipient — the facilitator cannot redirect funds.
- `permitted.amount` is the maximum — the facilitator can settle for any amount up to this value at the server's discretion.

#### SettlementResponse

The `upto` settlement response includes the actual amount charged:

```json
{
  "success": true,
  "transaction": "0x1234...cdef",
  "network": "eip155:84532",
  "payer": "0x857b06519E91e3A54538791bDbb0E22373e36b66",
  "amount": "2350000"
}
```

The `amount` field is specific to the `upto` scheme — it tells the server (and the client) exactly how much was charged.

#### Proxy Contract: `x402UptoPermit2Proxy`

The `upto` scheme uses a dedicated proxy contract (`x402UptoPermit2Proxy`) that differs from the `exact` variant in one critical way: the `settle()` function accepts an `amount` parameter.

```solidity
function settle(
    PermitTransferFrom calldata permit,
    uint256 amount,           // ← actual amount to transfer
    address owner,
    Witness calldata witness,
    bytes calldata signature
) external {
    if (amount > permit.permitted.amount) revert AmountExceedsPermitted();
    _settle(permit, amount, owner, witness, signature);
}
```

The contract enforces that `amount ≤ permit.permitted.amount`. If the facilitator tries to settle for more than the client authorized, the transaction reverts with `AmountExceedsPermitted()`.

#### Security Model

##### Core Guarantees

1. **Single-use authorization** — each Permit2 nonce can be consumed exactly once. After settlement, the authorization cannot be reused.
2. **Time-bounded** — `validAfter` (lower bound) and `deadline` (upper bound) restrict when the authorization is valid.
3. **Recipient binding** — `witness.to` is part of the signed message; the proxy enforces it as the transfer destination.
4. **Maximum amount enforcement** — the proxy contract guarantees the transferred amount is never greater than permitted amount, `amount ≤ permit.permitted.amount`.

##### Trust Considerations

With `upto`, the client trusts the server to charge a fair amount based on actual usage. The protocol guarantees:
- The charge will **never exceed** the authorized maximum.
- Funds will **only** go to the signed `witness.to` address.
- The authorization will be used **at most once**.

However, within these bounds, the server determines the final amount. A misbehaving server could charge up to `amount` regardless of actual consumption. Clients should authorize only what they're willing to spend per request.

#### Current Support

| Component | Status |
|-----------|--------|
| x402 Specification | ⏳ Mostly complete, but in [draft stage](https://github.com/coinbase/x402/pull/1074) still |
| x402UptoPermit2Proxy (on-chain) | ⏳ Deployed on Base Sepolia, waiting for audit and ops finalization |
| FareSide Facilitator (verify + settle) | ✅ Available |
| x402-rs Rust client | ✅ Available |
| x402-rs Axum middleware (server) | 🚧 In progress |
| TypeScript SDK (@x402) | 🚧 Not yet available |

<DocsNotice variant='info' title='Seller integration'>
  The `upto` scheme requires the resource server to report the actual consumed amount at settlement time. Server-side middleware support for this is being actively developed. Check the [x402-rs repository](https://github.com/x402-rs/x402-rs) and [reference SDK repository](https://github.com/coinbase/x402) for the latest status.
</DocsNotice>

#### Example Implementation

For developers eager to explore the code and experiment with the `upto` scheme, reference TypeScript implementations are available in the x402-rs repository:

- **[upto-evm-scheme.ts](https://github.com/x402-rs/x402-rs/blob/main/protocol-compliance/src/utils/upto-evm-scheme.ts)** — client and server scheme implementation for `upto` with Permit2 authorization signing
- **[payment-required.ts](https://github.com/x402-rs/x402-rs/blob/main/protocol-compliance/src/utils/payment-required.ts)** — HTTP middleware for handling payment verification and settlement with dynamic amount support

These implementations demonstrate the full payment flow including signature generation, verification, and settlement with actual usage-based amounts.

#### Further Reading

- [EVM Asset Transfer Methods](/docs/evm-asset-transfer-methods) — how EIP-3009 and Permit2 work in x402
- [x402 upto scheme specification](https://github.com/fabrice-cheng/x402/blob/2e11d699a86453d814939da54aea49a6612447e2/specs/schemes/upto/scheme_upto.md) (current draft)
- [x402 upto EVM specification](https://github.com/fabrice-cheng/x402/blob/2e11d699a86453d814939da54aea49a6612447e2/specs/schemes/upto/scheme_upto_evm.md) (current draft)

## EIP-2612 Gas Sponsoring [Core Concepts]: Gasless Permit2 approval for ERC-20 tokens with EIP-2612 support
Source: /docs/eip2612-gas-sponsoring.md

import { DocsNotice } from "@/components/docs/DocsNotice";

When using [Permit2-based payments](/docs/evm-asset-transfer-methods#permit2-universal-erc-20-support), the client needs a one-time on-chain `approve()` to the Permit2 contract. This requires the client to hold native gas tokens (ETH, MATIC, AVAX, etc.) and submit a separate transaction before making any x402 payment.

The [`eip2612GasSponsoring` extension](https://github.com/coinbase/x402/blob/main/specs/extensions/eip2612_gas_sponsoring.md) removes this step entirely if the token supports [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612). The client signs an off-chain permit message, and the facilitator submits it alongside the payment — no gas, no extra transaction, no native token balance needed.

#### Why This Matters

The core value is **removing friction from the first payment**. Without this extension, a new user paying with a Permit2-based token must:

1. _Acquire native gas tokens for the target network_
2. Submit an on-chain `approve(Permit2, amount)` transaction
3. Wait for confirmation
4. Only then make the actual x402 payment

For stablecoins-only users (AI agents, automated systems, new wallets funded with just USDC), step 1 is a dead end. The user has the money to pay for the service but can't get past the approval gate.

With `eip2612GasSponsoring`, all of this collapses into a single flow: the client signs two messages (the Permit2 payment + the EIP-2612 permit), and the facilitator handles the rest.

#### The Classic Approve Flow

Standard ERC-20 tokens only have `transfer()` and `transferFrom()`. To let a third party move your tokens, you submit an on-chain `approve()` — paying gas in the network's native currency:

```
1. Client → token.approve(Permit2, MAX)     ← on-chain, client pays gas
2. Client signs Permit2 payment             ← off-chain, gasless
3. Facilitator settles via x402Permit2Proxy ← on-chain, facilitator pays gas
```

Step 1 is the problem. It's a separate transaction that requires native tokens, adds latency, and breaks the seamless experience that x402 is designed to provide.

#### What Changed Before the Standard

The ERC-20 approval friction isn't new, and various projects tried to solve it:

- _Meta-transaction relayers_ — third-party services that wrap and broadcast user transactions, paying gas on their behalf. Each relayer used a custom protocol, and clients had to integrate with specific relay infrastructure.
- _Vendor-specific gasless approval APIs_ — wallet providers and DeFi protocols built proprietary gasless approval endpoints, each with its own signature scheme, API surface, and trust assumptions.
- _Custom forwarder contracts_ — smart contracts that accepted signed messages and forwarded calls, effectively reimplementing parts of what EIP-2612 later standardized.

These solutions worked in isolation but were **not interoperable**. A gasless approval flow built for one relayer didn't work with another. A wallet's proprietary meta-transaction format wasn't compatible with a different facilitator's expectations. Every integration was a custom build.

#### How EIP-2612 Changes This

[EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) adds a `permit()` function directly to the token contract. Instead of submitting an on-chain `approve()`, the token holder signs an [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed message specifying:

- _owner_ — the token holder
- _spender_ — the address being approved (in our case, the canonical Permit2 contract)
- _value_ — the amount to approve
- _deadline_ — when the permit expires
- _nonce_ — replay protection, managed by the token contract

Anyone can submit this signed permit to the token contract. The contract verifies the signature, checks the nonce, and sets the allowance — all in one call, with the submitter (the facilitator) paying the gas.

<DocsNotice variant='info' title='Standard, Not a Hack'>
  EIP-2612 is an [Ethereum standard](https://eips.ethereum.org/EIPS/eip-2612) — not a proprietary API or a custom relayer protocol. Any wallet, facilitator, or application that implements the standard can produce and consume these signatures without special coordination.
</DocsNotice>

#### Flow Inside x402

When the facilitator advertises `eip2612GasSponsoring` support, the Permit2 one-time approval becomes invisible to the user:

```mermaid
sequenceDiagram
    participant Client
    participant Seller as Resource Server (Seller)
    participant Facilitator
    participant Proxy as x402Permit2Proxy
    participant Token as ERC-20 Contract (EIP-2612)
    participant P2 as Permit2 Contract

    Client->>Seller: 1. Request resource
    Seller-->>Client: 2. 402 Payment Required<br/>(extensions: eip2612GasSponsoring)
    Note over Client: 3. Sign EIP-2612 permit<br/>(approve Permit2 for token)
    Note over Client: 4. Sign Permit2 payment<br/>(permitWitnessTransferFrom)
    Client->>Seller: 5. Retry with PaymentPayload<br/>+ eip2612GasSponsoring extension
    Seller->>Facilitator: 6. Verify
    Note over Facilitator: 7. Validate EIP-2612 signature<br/>+ simulate settleWithPermit
    Facilitator-->>Seller: 8. Valid
    Note over Seller: 9. Fulfill request
    Seller->>Facilitator: 10. Settle
    Facilitator->>Proxy: 11. settleWithPermit(...)
    Proxy->>Token: 12. permit(owner, Permit2, ...)
    Token-->>Proxy: 13. Allowance set
    Proxy->>P2: 14. permitWitnessTransferFrom(...)
    P2->>Token: 15. transferFrom(...)
    Token-->>P2: 16. Transfer executed
    Facilitator-->>Seller: 17. Settlement confirmed
    Seller-->>Client: 18. Response
```

From the client's perspective, the experience is identical to EIP-3009 — two off-chain signatures, zero gas. The facilitator calls `x402Permit2Proxy.settleWithPermit()`, which atomically executes both the EIP-2612 permit and the Permit2 payment settlement in a single transaction.

##### What This Gives the Product

- _Zero-gas onboarding_ — first-time users with a stablecoin balance can pay immediately, no native token needed.
- _Facilitator-sponsored approval_ — the facilitator pays for the permit transaction as part of settlement, absorbing the cost.
- _Single atomic transaction_ — the permit and the payment execute together. If either fails, neither takes effect.
- _No client-side infrastructure_ — the client doesn't need a relayer, a meta-transaction service, or any vendor-specific integration.

#### Interoperability

The critical difference between `eip2612GasSponsoring` and the pre-standard solutions is interoperability.

EIP-2612 defines a **common interface** for signed approvals. This means:

- _Wallets_ produce permits using the same EIP-712 typed data structure, regardless of which facilitator will process them.
- _Tokens_ expose the same `permit()` function with the same parameter types and nonce management.
- _Facilitators_ can verify and submit permits without knowing which wallet signed them or which client SDK produced the payload.
- _Applications_ declare support via a standard extension key (`eip2612GasSponsoring`) instead of per-vendor negotiation.

In the context of the x402 ecosystem, this means a client built for one x402 facilitator's `eip2612GasSponsoring` works with any other facilitator that advertises the same extension. There's no vendor lock-in in the approval layer.

#### Which Tokens Benefit

The `eip2612GasSponsoring` extension applies to ERC-20 tokens that implement the EIP-2612 `permit()` function. Not all ERC-20 tokens do — this is an opt-in standard at the token contract level.

Tokens that benefit the most are those designed for gasless, automated payment scenarios:

| Token  | Why It Matters |
|--------|----------------|
| USDT0  | Tether's omnichain USDT, built with EIP-2612 support. As Tether migrates liquidity to USDT0, gasless approval becomes available for the most widely used stablecoin in new deployments. |
| USDC   | On many chains, USDC implements both EIP-3009 and EIP-2612. Where EIP-3009 is available, it's preferred (simpler flow). Where only EIP-2612 is present, this extension provides the fallback for gasless Permit2 approval. |

<DocsNotice variant='info' title='Growing Adoption'>
  EIP-2612 adoption is growing. New token deployments increasingly include `permit()` support, and existing tokens sometimes add it via upgrades. The set of compatible tokens expands over time without requiring protocol changes.
</DocsNotice>

##### USDT0 and the Tether Migration

USDT0 deserves special attention. Tether is actively migrating to its omnichain token standard, and USDT0 contracts include EIP-2612 support. This makes USDT0 a natural fit for `eip2612GasSponsoring` — the most widely used stablecoin issuer is building native compatibility with gasless approval flows.

For x402, this means payment scenarios involving USDT0 can be fully gasless from day one, without the client ever needing to hold native gas tokens. Combined with the standard x402 Permit2 settlement flow, USDT0 payments become as seamless as USDC payments with EIP-3009.

#### Limitations

##### Only EIP-2612 Tokens

This extension only works for tokens that implement the `permit()` function. For tokens without EIP-2612 support, the alternatives are:

- _Direct approval_ — the client submits an on-chain `approve()` and pays gas.
- _[`erc20ApprovalGasSponsoring` extension](https://github.com/coinbase/x402/blob/main/specs/extensions/erc20_gas_sponsoring.md)_ — the facilitator sponsors the gas for the client's signed approval transaction. This works for any ERC-20 token but requires a more complex flow (the facilitator funds the client's wallet, relays the approval, and settles — all in an atomic batch).

##### One-Time Per Token Per Spender

The EIP-2612 permit sets an allowance from the token owner to the Permit2 contract. Once approved, all subsequent Permit2-based x402 payments with that token are gasless without the extension. The extension is needed only for the **first** payment with a given token when no prior approval exists.

##### Security Remains the Integrator's Responsibility

EIP-2612 improves UX and interoperability, but it doesn't change the security model. Integrators must still:

- Verify that the `spender` in the EIP-2612 permit matches the canonical Permit2 contract address.
- Validate that the permit signature recovers to the expected `from` address.
- Ensure the facilitator simulates the full `settleWithPermit` call before accepting the payment.

The [FareSide facilitator](/docs/setup-guide) handles these checks automatically, but custom implementations must implement them correctly.

#### The Takeaway

`eip2612GasSponsoring` turns gasless approval from a collection of vendor-specific workarounds into an interoperable part of the standard x402 payment flow. For tokens that support EIP-2612 — a set that includes USDT0 and continues to grow — the entire payment experience becomes gasless, from the very first transaction.

#### Further Reading

- [EVM Asset Transfer Methods](/docs/evm-asset-transfer-methods) — how EIP-3009 and Permit2 transfers work in x402
- [EIP-2612 specification](https://eips.ethereum.org/EIPS/eip-2612)
- [x402 extension spec: eip2612GasSponsoring](https://github.com/coinbase/x402/blob/main/specs/extensions/eip2612_gas_sponsoring.md)
- [x402 extension spec: erc20ApprovalGasSponsoring](https://github.com/coinbase/x402/blob/main/specs/extensions/erc20_gas_sponsoring.md)
- [Why x402Permit2Proxy?](/docs/evm-asset-transfer-methods#why-x402permit2proxy)

## Self-Hosting [Advanced]: Run your own x402 facilitator with x402-rs
Source: /docs/self-hosting.md

import { DocsNotice } from "@/components/docs/DocsNotice";

If you prefer full control over your infrastructure, you can run your own x402 facilitator using [x402-rs](https://github.com/x402-rs/x402-rs).

##### Consider Self-Hosting If:

- **Custom logic** - You need specialized behavior during x402 workflows
- **Regulatory compliance** - Data residency requirements or audit trails that must stay on your infrastructure
- **Absolute control** - You want complete ownership of the payment verification and settlement process
- **Blockchain proximity** - You're already running nodes or have deep integration with the target chain
- **Ops-ready** - You're comfortable managing gas bumps, stuck transactions, nonce coordination, and private key management yourself

##### Use FareSide Hosted If:

- **Avoid settlement complexity** - We handle gas bumps, stuck transactions, nonce management, private key management and other operational headaches for you
- **Getting started** - Focus on building your product, not payment infrastructure
- **Managed reliability** - Want uptime guarantees and automatic failover
- **Observability** - Payment monitoring and a multi-chain balance dashboard out of the box
- **Advanced features** - Need payment splits, webhooks, or cross-chain support (coming soon)

#### Prerequisites

- Docker (recommended) or Rust toolchain (1.80+)
- Access to RPC endpoints for your target networks
- Private key for the facilitator wallet (for gas)
- Server with reliable uptime

#### Quick Start with Docker

Prebuilt Docker images are available from:
- **GitHub Container Registry**: `ghcr.io/x402-rs/x402-facilitator`

##### 1. Create Configuration File

Create a `config.json` file. Note the use of CAIP-2 identifiers for chain keys.

```json
{
  "port": 8080,
  "host": "0.0.0.0",
  "chains": {
    "eip155:84532": {
      "eip1559": true,
      "signers": ["$EVM_PRIVATE_KEY"],
      "rpc": [{
        "http": "https://sepolia.base.org",
        "rate_limit": 100
      }]
    },
    "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": {
      "signers": ["$SOLANA_PRIVATE_KEY"],
      "rpc": "https://api.devnet.solana.com"
    },
    "aptos:2": {
      "rpc": "https://fullnode.testnet.aptoslabs.com/v1",
      "sponsor_gas": true,
      "signer": "$APTOS_PRIVATE_KEY"
    }
  },
  "schemes": [{
    "id": "v1-eip155-exact",
    "chains": "eip155:*"
  }, {
    "id": "v2-eip155-exact",
    "chains": "eip155:*"
  }, {
    "id": "v2-eip155-upto",
    "chains": "eip155:*"
  }, {
    "id": "v2-solana-exact",
    "chains": "solana:*"
  }, {
    "id": "v2-aptos-exact",
    "chains": "aptos:*"
  }]
}
```

##### 2. Create Environment File

Create a `.env` file with your secrets:

```bash
CONFIG=config.json
EVM_PRIVATE_KEY=0x...
SOLANA_PRIVATE_KEY=base58key...
APTOS_PRIVATE_KEY=0x...
RUST_LOG=info
```

##### 3. Run the Container

```bash
docker run --env-file .env -v $(pwd)/config.json:/app/config.json -p 8080:8080 ghcr.io/x402-rs/x402-facilitator
```

##### 4. Verify It's Running

```bash
curl http://localhost:8080/health
```

#### Configuration Reference

The facilitator is configured via `config.json`.

##### Chains Configuration

Keys are CAIP-2 chain identifiers.

**EVM (`eip155`):**
```json
"eip155:8453": {
  "eip1559": true,
  "flashblocks": false,
  "signers": ["$PRIVATE_KEY"],
  "rpc": [{ "http": "https://mainnet.base.org" }]
}
```

- `eip1559` — use EIP-1559 transaction format (defaults to `true`; set it to `false` if a chain does not support it)
- `flashblocks` — when `true`, use `latest` block for gas estimation (check if your chain is flashblocks-enabled)
- `signers` — array of private keys (hex with `0x` prefix), supports `$ENV_VAR` syntax
- `rpc` — array of RPC endpoint objects with `http` or `https` URL and optional `rate_limit`

**Solana (`solana`):**
```json
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": {
  "signer": "$PRIVATE_KEY",
  "rpc": "https://api.mainnet-beta.solana.com",
  "pubsub": "wss://api.mainnet-beta.solana.com"
}
```

- `signer` — Ed25519 private key (base58-encoded), supports `$ENV_VAR` syntax
- `rpc` — HTTP RPC endpoint URL
- `pubsub` — WebSocket endpoint for transaction confirmations (optional but recommended)

**Aptos (`aptos`):**
```json
"aptos:1": {
  "rpc": "https://fullnode.mainnet.aptoslabs.com/v1",
  "sponsor_gas": true,
  "signer": "$PRIVATE_KEY"
}
```

- `rpc` — Aptos fullnode REST API URL
- `sponsor_gas` — when `true`, the facilitator pays gas fees for client transactions (recommended)
- `signer` — Ed25519 private key (hex with `0x` prefix), supports `$ENV_VAR` syntax

##### Schemes Configuration

Defines which payment schemes are enabled for which chains.

```json
"schemes": [
  {
    "id": "v2-eip155-exact",
    "chains": "eip155:*"
  },
  {
    "id": "v2-eip155-upto",
    "chains": "eip155:*"
  },
  {
    "id": "v2-solana-exact",
    "chains": "solana:*"
  },
  {
    "id": "v2-aptos-exact",
    "chains": "aptos:*"
  }
]
```

Supported `chains` field formats:
- Specific: `eip155:84532` matches only Base Sepolia
- Pattern: `eip155:{1,5,84532}` matches specific chains
- Wildcard: `eip155:*` matches all EVM chains, `solana:*` matches all Solana chains

#### Building from Source

If you prefer to build locally:

```bash
### Clone the repository
git clone https://github.com/x402-rs/x402-rs.git
cd x402-rs

### Build and run the facilitator package
cargo run --release --package x402-facilitator --features full
```

Or build your own Docker image:

```bash
docker build -t x402-facilitator .
docker run --env-file .env -v $(pwd)/config.json:/app/config.json -p 8080:8080 x402-facilitator
```

#### Docker Compose

For production deployments:

```yaml
version: '3.8'
services:
  facilitator:
    image: ghcr.io/x402-rs/x402-facilitator
    ports:
      - "8080:8080"
    volumes:
      - ./config.json:/app/config.json
    env_file:
      - .env
    restart: unless-stopped
```

#### Connecting Your Services

Point your middleware to your self-hosted facilitator:

##### TypeScript

```typescript
import { HTTPFacilitatorClient } from "@x402/core/server";

const facilitatorClient = new HTTPFacilitatorClient({
  url: "http://your-facilitator:8080"
});
```

##### Rust (x402-axum)

```rust
use x402_axum::X402Middleware;

// Point to your self-hosted facilitator
let x402 = X402Middleware::try_from("http://your-facilitator:8080/").unwrap();
```

#### Production Considerations

##### High Availability

For production, run multiple facilitator instances behind a load balancer:

**Example `docker-compose.prod.yml`:**

```yaml
version: '3.8'
services:
  facilitator-1:
    image: ghcr.io/x402-rs/x402-facilitator
    volumes:
      - ./config.json:/app/config.json
    env_file: .env.instance1  # Unique private key

  facilitator-2:
    image: ghcr.io/x402-rs/x402-facilitator
    volumes:
      - ./config.json:/app/config.json
    env_file: .env.instance2  # Different private key

  nginx:
    image: nginx
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
```

<DocsNotice variant="warning" title="Each instance must have its own private key">
  Sharing a private key across multiple facilitator instances will cause **nonce collisions** and transaction failures. Each instance needs a unique `EVM_PRIVATE_KEY`, `SOLANA_PRIVATE_KEY`, and `APTOS_PRIVATE_KEY`, and each wallet must be funded with gas tokens.
</DocsNotice>

Each facilitator instance needs its own funded wallet:

- Generate separate keys for each instance
- Fund each wallet with gas tokens on all supported networks
- Monitor gas balances across all wallets
- Consider using a key management service for rotation

RPC providers often have rate limits. For high-availability deployments:

- Carefully distribute RPC endpoints across instances to spread the load
- Use different RPC providers for different instances

```bash
### .env.instance1
EVM_PRIVATE_KEY=0xKey1...
RPC_URL_BASE=https://base-mainnet.g.alchemy.com/v2/KEY_1

### .env.instance2
EVM_PRIVATE_KEY=0xKey2...
RPC_URL_BASE=https://base.llamarpc.com
```

##### Security

- **Minimize wallet balances** - Don't keep large amounts of gas funds in facilitator wallets. Top up regularly as they deplete rather than pre-funding with large amounts
- **Never expose private keys** in logs or error messages
- **Use HTTPS** in production (terminate at load balancer)
- **Firewall** the facilitator to only accept traffic from your services
- **Rotate keys** periodically

##### Logs

Enable detailed logging via environment variable:

```bash
RUST_LOG=debug
```

##### Observability

The facilitator emits OpenTelemetry-compatible traces and metrics. To enable:

```bash
### For Honeycomb / Jaeger / etc
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_SERVICE_NAME=x402-facilitator
```

#### Comparison: Self-Hosted vs FareSide

| Aspect                 | Self-Hosted                                      | FareSide         |
|------------------------|--------------------------------------------------|------------------|
| Setup time             | Hours                                            | Minutes          |
| Settlement ops         | Gas bumps, nonces, stuck txs - all yours         | Handled for you  |
| Private key management | Generate, secure, rotate, fund multiple wallets  | Not your problem |
| RPC management         | Rate limits, failover, multiple providers        | Managed          |
| High availability      | Complex (unique keys per instance, RPC planning) | Built-in         |
| Cost model             | Fixed (servers + gas + ops time)                 | Per transaction  |
| Uptime                 | Your responsibility                              | 99.9% SLA        |
| Payment routing        | DIY                                              | Included         |
| Support                | Community                                        | Direct           |

#### Migration

##### From FareSide to Self-Hosted

1. Set up your facilitator
2. Test with a staging environment
3. Update middleware configuration
4. Monitor for issues

##### From Self-Hosted to FareSide

1. [Sign up at app.fareside.com](https://app.fareside.com/register)
2. [Get your API key](/docs/setup-guide)
3. Update middleware to use FareSide URL
4. Decommission your facilitator

#### Resources

- **x402-rs Repository**: [github.com/x402-rs/x402-rs](https://github.com/x402-rs/x402-rs)
- **Issues & Support**: [GitHub Issues](https://github.com/x402-rs/x402-rs/issues)
- **FareSide Support**: [t.me/faresidehq](https://t.me/faresidehq)

## Build Your Own Facilitator [Advanced]: Create a custom x402 facilitator using Rust
Source: /docs/build-your-own-facilitator.md

import { DocsNotice } from "@/components/docs/DocsNotice";

This guide explains how to build a custom x402 facilitator implementation using the x402-rs ecosystem.

#### Overview

**A facilitator helps the seller to avoid bothering with on-chain intricacies**:
- _Verifies_ payment payloads signed by clients
- _Settles_ payments on-chain
- _Manages_ blockchain connections and signers

The x402-rs ecosystem provides building blocks to create custom facilitators tailored to your needs.

#### Why Build a Custom Facilitator?

You might want to build a custom facilitator for several reasons:

1. **Support for custom blockchains** — You need to support a blockchain that is not yet supported by the official x402-rs crates. This involves implementing a custom chain provider and adapting the payment schemes to work with it.

2. **Custom chain provider behavior** — You want to customize how the facilitator interacts with a supported chain. For example, you might want to implement a custom transaction settlement logic for your chain: to add custom transaction signing logic, gas pricing strategies, or nonce management.

3. **Chain-specific deployment** — You want to run a facilitator that only supports specific chains or schemes, reducing the binary size and attack surface. This can be achieved through feature flags in the `facilitator` crate or by creating a minimal custom facilitator.

4. **Custom middleware or authentication** — You need to add custom HTTP middleware, authentication, or logging that is specific to your infrastructure.

5. **Integration with existing infrastructure** — You want to integrate the x402 facilitator into an existing application or service, rather than running it as a standalone binary.

#### Architecture

If you are curious, and expect to dig deeper, here is a high-level architecture:

```mermaid
flowchart TB
    Server["Your HTTP Server<br/>(Axum, Actix, Rocket, etc.)"]

    Server --> Facilitator["x402-facilitator-local<br/>(Verification & Settlement Logic)"]

    Facilitator --> ChainReg[Chain Registry]
    Facilitator --> SchemeReg[Scheme Registry]

    ChainReg -.-> EIP155["EIP-155 Provider"]
    ChainReg -.-> Solana["Solana Provider"]
    ChainReg -.-> Aptos["Aptos Provider"]

    EIP155 ~~~ Solana ~~~ Aptos

    SchemeReg -.-> V1E155["V1Eip155Exact"]
    SchemeReg -.-> V2E155["V2Eip155Exact"]
    SchemeReg -.-> V1Sol["V1SolanaExact"]
    SchemeReg -.-> V2Sol["V2SolanaExact"]
    SchemeReg -.-> V2Apt["V2AptosExact"]

    V1E155 ~~~ V2E155 ~~~ V1Sol ~~~ V2Sol ~~~ V2Apt
```

#### Getting Started

##### 1. Add Dependencies

> **Note:** The versions shown below are indicative. Please check the latest versions on [crates.io](https://crates.io) or the source repository if the packages are not published on crates.io.

```toml
[dependencies]
x402-types = { version = "1.0", features = ["cli"] }
x402-facilitator-local = { version = "1.0" }
x402-chain-eip155 = { version = "1.0", features = ["facilitator"] }
x402-chain-solana = { version = "1.0", features = ["facilitator"] }

dotenvy = "0.15"
serde_json = "1.0"
tokio = { version = "1.35", features = ["full"] }
async-trait = "0.1"
axum = "0.8"
tower-http = "0.9"
rustls = { version = "0.23", features = ["ring"] }
```

##### 2. Initialize the Facilitator

```rust
use x402_facilitator_local::{FacilitatorLocal, handlers};
use x402_types::chain::{ChainRegistry, FromConfig};
use x402_types::scheme::{SchemeBlueprints, SchemeRegistry};
use x402_chain_eip155::{V1Eip155Exact, V2Eip155Exact};
use x402_chain_solana::{V1SolanaExact, V2SolanaExact};
use std::sync::Arc;
use axum::Router;
use tower_http::cors;
use axum::http::Method;

###[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize rustls crypto provider
    rustls::crypto::CryptoProvider::install_default(
        rustls::crypto::ring::default_provider()
    ).expect("Failed to initialize rustls crypto provider");

    // Load .env variables
    dotenvy::dotenv().ok();

    // Load configuration
    let config = Config::load()?;

    // Initialize chain registry from config
    let chain_registry = ChainRegistry::from_config(config.chains()).await?;

    // Register supported schemes
    let scheme_blueprints = {
        let mut blueprints = SchemeBlueprints::new();
        blueprints.register(V1Eip155Exact);
        blueprints.register(V2Eip155Exact);
        blueprints.register(V1SolanaExact);
        blueprints.register(V2SolanaExact);
        blueprints
    };

    // Build scheme registry
    let scheme_registry =
        SchemeRegistry::build(chain_registry, scheme_blueprints, config.schemes());

    // Create facilitator
    let facilitator = FacilitatorLocal::new(scheme_registry);
    let state = Arc::new(facilitator);

    // Create HTTP routes with CORS
    let app = Router::new()
        .merge(handlers::routes().with_state(state))
        .layer(
            cors::CorsLayer::new()
                .allow_origin(cors::Any)
                .allow_methods([Method::GET, Method::POST])
                .allow_headers(cors::Any),
        );

    // Run server
    let addr = SocketAddr::new(config.host(), config.port());
    let listener = tokio::net::TcpListener::bind(addr).await?;
    axum::serve(listener, app).await?;

    Ok(())
}
```

##### 3. Configuration

Create a `config.json` file:

```json
{
  "port": 8080,
  "host": "0.0.0.0",
  "chains": {
    "eip155:8453": {
      "eip1559": true,
      "flashblocks": true,
      "signers": ["$BASE_PRIVATE_KEY"],
      "rpc": [
        {
          "http": "https://mainnet.base.org",
          "rate_limit": 100
        }
      ]
    },
    "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": {
      "signer": "$SOLANA_PRIVATE_KEY",
      "rpc": "https://api.mainnet-beta.solana.com",
      "pubsub": "wss://api.mainnet-beta.solana.com"
    }
  },
  "schemes": [
    {
      "id": "v1-eip155-exact",
      "chains": "eip155:*"
    },
    {
      "id": "v2-eip155-exact",
      "chains": "eip155:*"
    },
    {
      "id": "v1-solana-exact",
      "chains": "solana:*"
    },
    {
      "id": "v2-solana-exact",
      "chains": "solana:*"
    }
  ]
}
```

Notice how `config.json` references secrets via environment variable syntax (e.g., `$BASE_PRIVATE_KEY`). These are resolved at runtime — values prefixed with `$` or wrapped in `${...}` are read from the corresponding environment variable.

Create a `.env` file alongside your `config.json` to store these secrets:

```bash
BASE_PRIVATE_KEY=0x...
SOLANA_PRIVATE_KEY=base58key...
```

The facilitator uses [dotenvy](https://crates.io/crates/dotenvy) to automatically load `.env` from the **current working directory** at startup (as shown in [Step 2](#2-initialize-the-facilitator) with `dotenvy::dotenv().ok()`). Make sure you run the binary from the directory containing your `.env` file, or export the variables manually before launching.

<DocsNotice variant="warning" title="Security">
  Never commit your `.env` file to version control. Add `.env` to your `.gitignore`.
</DocsNotice>

##### 4. Build and Run

Once you have your `config.json`, `.env`, and your code from the previous steps, you can build and run the facilitator.

**Run locally via `cargo`:**

```bash
### Run with the default config path (config.json in the current directory)
cargo run

### Run with a custom config path using the --config (-c) CLI flag
cargo run -- --config /path/to/config.json

### Alternatively, specify the config path via the CONFIG env variable
CONFIG=/path/to/config.json cargo run
```

**Build and run a release binary:**

```bash
### Build a release binary
cargo build --release

### Run with the --config flag
./target/release/your-facilitator --config /path/to/config.json

### Or via the CONFIG env variable
CONFIG=/path/to/config.json ./target/release/your-facilitator
```

The config file path is resolved in the following order of priority:
1. `--config` (`-c`) CLI flag
2. `CONFIG` environment variable
3. `config.json` in the current working directory (default)

If the facilitator starts successfully, you will see the server listening on the configured `host:port` (default `0.0.0.0:8080`).

##### 5. Verify It Works

With the facilitator running, you can test the full payment flow by running a seller (resource server) pointed at your facilitator, and a buyer (client) that pays for a protected resource.

See these guides to set up both sides:

- [Quickstart](/docs/quickstart) — Accept your first x402 payment in 5 minutes (TypeScript or Rust)
- [Making Payments](/docs/making-payments) — Build a client that pays for x402-protected resources

Point the seller's `facilitatorUrl` to your running facilitator (e.g., `http://localhost:8080`) instead of the hosted FareSide endpoint. When the buyer makes a payment, you should see your facilitator verify and settle the transaction on-chain.

#### Advanced Customization

##### Custom Scheme Implementation

To implement a custom payment scheme:

1. Implement the `X402SchemeFacilitator` trait from `x402-types`
2. Implement the `X402SchemeFacilitatorBuilder` trait
3. Implement the `X402SchemeId` trait
4. Register it with the `SchemeBlueprints`

See the [How to Write a Scheme](/docs/how-to-write-a-scheme) guide for detailed instructions.

##### Custom Chain Support

To add support for a new blockchain that is not yet supported by x402-rs:

1. **Implement the `ChainProviderOps` trait** for your provider type. This trait provides basic operations like getting signer addresses and chain ID.

2. **Implement the `FromConfig` trait** to construct your provider from configuration. This allows your provider to be initialized from the JSON configuration file.

3. **Create scheme implementations for your chain**. For each scheme you want to support (e.g., `exact`), implement the `X402SchemeFacilitator` trait. Your scheme will use your custom chain provider to interact with the blockchain.

4. **Register with the `ChainRegistry`**. Add your chain provider to the registry so it can be discovered by the scheme registry.

5. **Add the scheme to your facilitator's `schemes.rs`**. Similar to how the `facilitator` crate has a `schemes.rs` file that implements `X402SchemeFacilitatorBuilder` for each scheme, you'll need to add your scheme there to bridge the generic `ChainProvider` enum to your chain-specific provider type.

##### Custom Chain Provider for Supported Chains

Even for supported chains like EIP-155 (EVM), you might want to customize the chain provider behavior. The EIP-155 schemes use the `Eip155MetaTransactionProvider` trait (see [x402-types](https://docs.rs/crate/x402-types)) to send transactions. You can implement this trait to customize:

- _Transaction signing logic_ — Add custom signature validation or multi-sig support
- _Gas pricing strategies_ — Implement dynamic gas pricing based on network conditions
- _Nonce management_ — Customize how nonces are tracked and reset
- _Transaction submission_ — Add retry logic, batching, or fallback to different RPC endpoints

To do this:

1. Create a new type that wraps or replaces `Eip155ChainProvider`
2. Implement `Eip155MetaTransactionProvider` for your type
3. Implement `ChainProviderOps` and `FromConfig` for your type
4. Use your custom provider when building the `ChainRegistry`

##### Chain-Specific Facilitator Deployment

If you want to run a facilitator that only supports specific chains (e.g., only Solana, only EVM chains), you have two options:

**Option 1: Use the `facilitator` crate with feature flags**

The `facilitator` crate supports feature flags to enable only specific chains. Since this crate is not published on crates.io, use a git dependency:

```toml
[dependencies]
x402-facilitator = { git = "https://github.com/x402-rs/x402-rs", default-features = false, features = ["chain-solana"] }
```

Available features:
- `chain-eip155` — Enable EIP-155 (EVM) chain support
- `chain-solana` — Enable Solana chain support
- `chain-aptos` — Enable Aptos chain support
- `telemetry` — Enable OpenTelemetry tracing

Then in your `main.rs`, simply call the `run` function:

```rust
###[tokio::main]
async fn main() {
    let result = x402_facilitator::run().await;
    if let Err(e) = result {
        eprintln!("{e}");
        std::process::exit(1)
    }
}
```

**Option 2: Create a minimal custom facilitator**

Follow the "[Getting Started](#getting-started)" section above, but only register the schemes you need:

```rust
// Only register Solana schemes
let scheme_blueprints = {
    let mut blueprints = SchemeBlueprints::new();
    blueprints.register(V1SolanaExact);
    blueprints.register(V2SolanaExact);
    blueprints
};
```

This approach gives you full control over the binary size and dependencies.

##### Middleware Integration

Integrate with your existing HTTP framework:

```rust
use axum::{middleware, Router};

let app = Router::new()
    .route("/verify", post(verify_handler))
    .route("/settle", post(settle_handler))
    .layer(middleware::from_fn(your_auth_middleware));
```

##### Adding Pre/Post Processing Logic

You can wrap `FacilitatorLocal` to add custom logic before or after payment verification and settlement. This is useful for:

- **Logging and auditing** — Log all payment attempts for compliance
- **Rate limiting** — Enforce limits on verification/settlement calls
- **Custom validation** — Add business-specific validation rules
- **Metrics collection** — Track payment success rates, latency, etc.

To do this, create a wrapper struct and implement the `Facilitator` trait:

```rust
use x402_facilitator_local::FacilitatorLocal;
use x402_types::facilitator::Facilitator;
use x402_types::proto;
use std::sync::Arc;

/// A wrapper around FacilitatorLocal that adds custom pre/post processing.
pub struct FancyFacilitator<A> {
    inner: FacilitatorLocal<A>,
}

impl<A> FancyFacilitator<A> {
    pub fn new(inner: FacilitatorLocal<A>) -> Self {
        Self { inner }
    }
}

impl<A: Clone + Send + Sync + 'static> Facilitator for FancyFacilitator<A>
where
    FacilitatorLocal<A>: Facilitator,
{
    type Error = <FacilitatorLocal<A> as Facilitator>::Error;

    async fn verify(
        &self,
        request: &proto::VerifyRequest,
    ) -> Result<proto::VerifyResponse, Self::Error> {
        // Pre-processing: custom validation, logging, rate limiting, etc.
        println!("Verifying payment for scheme: {:?}", request.scheme);

        // Delegate to inner facilitator
        let response = self.inner.verify(request).await?;

        // Post-processing: audit logging, metrics, etc.
        println!("Payment verified: payer={}", response.payer);

        Ok(response)
    }

    async fn settle(
        &self,
        request: &proto::SettleRequest,
    ) -> Result<proto::SettleResponse, Self::Error> {
        // Pre-processing
        println!("Settling payment...");

        // Delegate to inner facilitator
        let response = self.inner.settle(request).await?;

        // Post-processing
        println!("Payment settled: tx={}", response.transaction);

        Ok(response)
    }

    async fn supported(&self) -> Result<proto::SupportedResponse, Self::Error> {
        self.inner.supported().await
    }
}

// Usage:
let facilitator = FacilitatorLocal::new(scheme_registry);
let fancy_facilitator = FancyFacilitator::new(facilitator);
let state = Arc::new(fancy_facilitator);
```

#### Deployment

##### Docker

Create a `Dockerfile`:

```dockerfile
FROM rust:trixie as builder
WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:trixie-slim
COPY --from=builder /app/target/release/your-facilitator /usr/local/bin/
ENTRYPOINT ["your-facilitator"]
```

##### Environment Variables

- `HOST` - Server bind address (default: `0.0.0.0`)
- `PORT` - Server port (default: `8080`)
- `CONFIG` - Path to configuration file (default: `config.json`). You can also pass the config path via the `--config` (`-c`) CLI flag, which takes priority over this environment variable.
- `RUST_LOG` - Log level (default: `info`)
- `OTEL_*` - OpenTelemetry configuration (when `telemetry` feature enabled)

#### Observability

Enable OpenTelemetry tracing:

```rust
use x402_facilitator_local::util::Telemetry;

let telemetry = Telemetry::new()
    .with_name("my-facilitator")
    .with_version("1.0.0")
    .register();

let tracing_layer = telemetry.http_tracing();

let app = Router::new()
    .merge(handlers::routes().with_state(state))
    .layer(tracing_layer);
```

#### Example

- [x402-facilitator](https://github.com/x402-rs/x402-rs/tree/main/facilitator) - Useful for understanding the structure

#### Support

For questions or issues:
- Open an issue on [GitHub](https://github.com/x402-rs/x402-rs)
- Check the [x402 protocol documentation](https://x402.org)
- Review individual crate documentation on [docs.rs](https://docs.rs)

## How to Write a Scheme [Advanced]: Implement custom payment logic for x402-rs
Source: /docs/how-to-write-a-scheme.md

This guide explains how to create a custom payment scheme for the x402-rs facilitator.

#### What is a Scheme?

A **scheme** defines how a payment is verified and settled on a specific blockchain. It encapsulates:

- **Payload format** — The structure of payment data (signatures, transactions, authorizations)
- **Verification logic** — How to validate a payment is correct before execution
- **Settlement logic** — How to execute the payment on-chain
- **Supported chains** — Which blockchain networks the scheme works with

For example, the `exact` scheme implements ERC-3009 `transferWithAuthorization` for EVM chains and SPL token transfers for Solana. You might create a new scheme for subscription payments, escrow flows, or alternative token standards.

##### Schemes and Blueprints

The x402 protocol defines **schemes** at the specification level — conceptual descriptions of how a particular payment flow works (e.g., "exact" means the buyer authorizes a transfer of the exact amount to the seller).

A **blueprint** is the concrete Rust implementation of that scheme for the [x402-rs facilitator](https://github.com/x402-rs/x402-rs/tree/main/facilitator). It is a struct (e.g., `V2SolanaExact`) that carries the scheme's unique identifier and knows how to create a handler for a given chain provider. In code, a blueprint is any type that implements both `X402SchemeId` (providing the identifier like `v2-solana-exact`) and `X402SchemeFacilitatorBuilder` (providing the factory method to instantiate the handler). These two traits are combined into the `X402SchemeBlueprint` marker trait.

At startup, you register blueprints into a `SchemeBlueprints` registry. The system then uses your configuration to build concrete handlers (`X402SchemeFacilitator`) from these blueprints — one handler per blueprint per matching chain provider. The handlers are stored in the `SchemeRegistry` and process `verify`, `settle`, and `supported` requests at runtime.

#### Overview

Not every part of the scheme system is meant to be extended. The table below clarifies which concepts are open for customization and which are fixed by the protocol or implementation:

| Concept               | Open/Closed | Description                                                                                                           |
|-----------------------|-------------|-----------------------------------------------------------------------------------------------------------------------|
| **Schemes**           | **Open**    | Widely extensible. Anyone can create custom schemes for new payment flows.                                            |
| **Protocol Versions** | Closed      | Fixed set: v1 and v2. Defined by the x402 specification. (v1 is legacy, v2 will probably live for the next few years) |
| **Chain Providers**   | Closed      | Predefined set for the implementation due to chain-specific complexity.                                               |

#### Architecture

```mermaid
flowchart TB
    subgraph Registration
        SB[SchemeBlueprints] -->|by id| BP[X402SchemeBlueprint]
        BP -->|build with ChainProvider| H[Box dyn X402SchemeFacilitator]
    end

    subgraph Runtime
        SR[SchemeRegistry] -->|by_slug| H
        H -->|verify| VR[VerifyResponse]
        H -->|settle| SR2[SettleResponse]
        H -->|supported| SPR[SupportedResponse]
    end
```

#### Crate Structure

Schemes are organized in chain-specific crates under `crates/chains/`:

```
crates/
├── x402-types/           # Core types and traits
│   └── src/scheme/       # X402SchemeId, X402SchemeFacilitator, etc.
├── x402-chain-solana/    # Solana-specific implementations
│   └── src/
│       ├── v1_solana_exact/
│       └── v2_solana_exact/
├── x402-chain-eip155/    # EVM-specific implementations
│   └── src/
│       ├── v1_eip155_exact/
│       └── v2_eip155_exact/
└── x402-chain-aptos/     # Aptos-specific implementations
    └── src/
        └── v2_aptos_exact/
```

Each scheme directory contains:
- `mod.rs` - Module exports and scheme ID implementation
- `facilitator.rs` - Facilitator implementation (server-side)
- `client.rs` - Client implementation (optional)
- `server.rs` - Server types (optional)
- `types.rs` - Scheme-specific types

#### Naming Convention

Scheme IDs follow the pattern: `v{version}-{namespace}-{scheme}`

| ID | Struct Name | Directory |
|------|-------------|-----------|
| `v2-solana-exact` | `V2SolanaExact` | `v2_solana_exact/` |
| `v1-eip155-exact` | `V1Eip155Exact` | `v1_eip155_exact/` |
| `v2-solana-myscheme` | `V2SolanaMyscheme` | `v2_solana_myscheme/` |

This makes it easy to map between IDs, chain namespaces, scheme names, and code.

#### Core Traits and Structs

##### X402SchemeId

Provides identification for a scheme. This trait defines the scheme's version, namespace, and name:

```rust
pub trait X402SchemeId {
    /// The x402 protocol version (1 or 2). Defaults to 2.
    fn x402_version(&self) -> u8 {
        2
    }

    /// The chain namespace (e.g., "eip155", "solana")
    fn namespace(&self) -> &str;

    /// The scheme name (e.g., "exact", "myscheme")
    fn scheme(&self) -> &str;

    /// Computed ID: "v{version}-{namespace}-{scheme}"
    fn id(&self) -> String {
        format!(
            "v{}-{}-{}",
            self.x402_version(),
            self.namespace(),
            self.scheme()
        )
    }
}
```

##### X402SchemeFacilitatorBuilder

Factory for creating scheme facilitators:

```rust
pub trait X402SchemeFacilitatorBuilder<P> {
    /// Creates a new scheme handler for the given chain provider.
    ///
    /// # Arguments
    ///
    /// * `provider` - The chain provider to use for on-chain operations
    /// * `config` - Optional scheme-specific configuration
    fn build(
        &self,
        provider: P,
        config: Option<serde_json::Value>,
    ) -> Result<Box<dyn X402SchemeFacilitator>, Box<dyn std::error::Error>>;
}
```

- The type parameter `P` represents the chain provider type (e.g., `&ChainProvider`, `Arc<SolanaChainProvider>`)
- The `build` method receives a chain provider. Implementations typically use a chain-specific trait like `SolanaChainProviderLike` to access provider methods
- The optional `config` allows scheme-specific configuration (parse however you wish, see "Configure in JSON" section)

##### X402SchemeBlueprint

A combined trait that requires both `X402SchemeId` and `X402SchemeFacilitatorBuilder`. This is automatically implemented for any type that implements both traits:

```rust
pub trait X402SchemeBlueprint<P>:
    X402SchemeId + for<'a> X402SchemeFacilitatorBuilder<&'a P>
{
}
impl<T, P> X402SchemeBlueprint<P> for T where
    T: X402SchemeId + for<'a> X402SchemeFacilitatorBuilder<&'a P>
{
}
```

The type parameter `P` represents the chain provider type that the blueprint can work with.

##### X402SchemeFacilitator

Three core operations every scheme facilitator must implement:

```rust
###[async_trait::async_trait]
pub trait X402SchemeFacilitator: Send + Sync {
    async fn verify(&self, request: &proto::VerifyRequest)
        -> Result<proto::VerifyResponse, X402SchemeFacilitatorError>;
    async fn settle(&self, request: &proto::SettleRequest)
        -> Result<proto::SettleResponse, X402SchemeFacilitatorError>;
    async fn supported(&self)
        -> Result<proto::SupportedResponse, X402SchemeFacilitatorError>;
}
```

| Method      | Purpose                                            |
|-------------|----------------------------------------------------|
| `verify`    | Validate a payment without executing it.           |
| `settle`    | Execute the payment on-chain.                      |
| `supported` | Advertise what payment kinds this scheme supports. |

##### SchemeHandlerSlug

At runtime, handlers are identified by a slug combining chain ID, version, and scheme name:

```rust
pub struct SchemeHandlerSlug {
    pub chain_id: ChainId,
    pub x402_version: u8,
    pub name: String,
}
```

This allows the same scheme to be applied to different chains.

#### Step-by-Step Guide

##### Step 1: Define Types

Use proto generics. For v2 schemes:

```rust
// In crates/chains/x402-chain-solana/src/v2_solana_myscheme/types.rs
use x402_types::proto::v2;

pub type PaymentRequirements = v2::PaymentRequirements<MyScheme, MyAmountType, MyAddressType, MyExtra>;
pub type PaymentPayload = v2::PaymentPayload<PaymentRequirements, MyPayload>;
pub type VerifyRequest = v2::VerifyRequest<PaymentPayload, PaymentRequirements>;
pub type SettleRequest = VerifyRequest;
```

##### Step 2: Implement X402SchemeId

```rust
// In crates/chains/x402-chain-solana/src/v2_solana_myscheme/mod.rs
use x402_types::scheme::X402SchemeId;

pub struct V2SolanaMyscheme;

impl X402SchemeId for V2SolanaMyscheme {
    // x402_version() defaults to 2, no need to override

    fn namespace(&self) -> &str {
        "solana"
    }

    fn scheme(&self) -> &str {
        "myscheme"
    }
}
```

##### Step 3: Implement X402SchemeFacilitatorBuilder

In the chain-specific crate, implement the builder for the chain-specific provider type:

```rust
// In crates/chains/x402-chain-solana/src/v2_solana_myscheme/facilitator.rs
use crate::chain::provider::SolanaChainProviderLike;
use x402_types::chain::ChainProviderOps;
use x402_types::scheme::X402SchemeFacilitator;

impl<P> X402SchemeFacilitatorBuilder<P> for V2SolanaMyscheme
where
    P: SolanaChainProviderLike + ChainProviderOps + Send + Sync + 'static,
{
    fn build(
        &self,
        provider: P,
        config: Option<serde_json::Value>,
    ) -> Result<Box<dyn X402SchemeFacilitator>, Box<dyn Error>>
    {
        // Optionally parse config here
        let config = config
            .map(serde_json::from_value::<V2SolanaMyschemeFacilitatorConfig>)
            .transpose()?
            .unwrap_or_default();

        Ok(Box::new(V2SolanaMyschemeFacilitator::new(provider, config)))
    }
}
```

Then, in the facilitator crate, implement the adapter for the generic `ChainProvider` enum:

```rust
// In facilitator/src/schemes.rs
###[cfg(feature = "chain-solana")]
use x402_chain_solana::V2SolanaMyscheme;

###[cfg(feature = "chain-solana")]
impl X402SchemeFacilitatorBuilder<&ChainProvider> for V2SolanaMyscheme {
    fn build(
        &self,
        provider: &ChainProvider,
        config: Option<serde_json::Value>,
    ) -> Result<Box<dyn X402SchemeFacilitator>, Box<dyn std::error::Error>> {
        let solana_provider = if let ChainProvider::Solana(provider) = provider {
            Arc::clone(provider)
        } else {
            return Err("V2SolanaMyscheme::build: provider must be a SolanaChainProvider".into());
        };
        self.build(solana_provider, config)
    }
}
```

##### Step 4: Implement Facilitator

```rust
// In crates/chains/x402-chain-solana/src/v2_solana_myscheme/facilitator.rs
use crate::chain::provider::SolanaChainProviderLike;
use x402_types::chain::ChainProviderOps;
use x402_types::proto;
use x402_types::proto::v2;
use x402_types::scheme::{
    X402SchemeFacilitator, X402SchemeFacilitatorError,
};

/// Configuration for V2 Solana Myscheme facilitator
###[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
###[serde(default)]
pub struct V2SolanaMyschemeFacilitatorConfig {
    // Add your scheme-specific configuration fields here
}

impl Default for V2SolanaMyschemeFacilitatorConfig {
    fn default() -> Self {
        Self {
            // Set default values for your configuration
        }
    }
}

pub struct V2SolanaMyschemeFacilitator<P> {
    provider: P,
    config: V2SolanaMyschemeFacilitatorConfig,
}

impl<P> V2SolanaMyschemeFacilitator<P> {
    pub fn new(provider: P, config: V2SolanaMyschemeFacilitatorConfig) -> Self {
        Self { provider, config }
    }
}

###[async_trait::async_trait]
impl<P> X402SchemeFacilitator for V2SolanaMyschemeFacilitator<P>
where
    P: SolanaChainProviderLike + ChainProviderOps + Send + Sync,
{
    async fn verify(&self, request: &proto::VerifyRequest)
        -> Result<proto::VerifyResponse, X402SchemeFacilitatorError>
    {
        let request = types::VerifyRequest::from_proto(request.clone())?;
        // Your verification logic...
        Ok(proto::v2::VerifyResponse::valid(payer.to_string()).into())
    }

    async fn settle(&self, request: &proto::SettleRequest)
        -> Result<proto::SettleResponse, X402SchemeFacilitatorError>
    {
        // Your settlement logic...
        Ok(proto::v2::SettleResponse::Success { payer, transaction, network }.into())
    }

    async fn supported(&self) -> Result<proto::SupportedResponse, X402SchemeFacilitatorError> {
        let chain_id = self.provider.chain_id();
        let kinds = vec![proto::SupportedPaymentKind {
            x402_version: proto::v2::X402Version2.into(),
            scheme: "myscheme".to_string(),
            network: chain_id.to_string(),
            extra: None,
        }];
        let signers = {
            let mut signers = HashMap::with_capacity(1);
            signers.insert(chain_id, self.provider.signer_addresses());
            signers
        };
        Ok(proto::SupportedResponse {
            kinds,
            extensions: Vec::new(),
            signers,
        })
    }
}
```

##### Step 5: Register the Scheme

For custom facilitators, register dynamically in the facilitator crate:

```rust
// In facilitator/src/schemes.rs
###[cfg(feature = "chain-solana")]
use x402_chain_solana::V2SolanaMyscheme;

// Then in your initialization code:
let blueprints = SchemeBlueprints::new().and_register(V2SolanaMyscheme);
```

##### Step 6: Configure in JSON

```json
{
  "schemes": [
    {
      "enabled": true,
      "id": "v2-solana-myscheme",
      "chains": "solana:*",
      "config": { "yourOption": "value" }
    }
  ]
}
```

- `id`: The scheme blueprint ID (matches `X402SchemeId::id()`)
- `chains`: Pattern matching (`*` for all, `{a,b}` for specific chain references)
- `config`: Passed to your `build()` method

#### Per-Chain Custom Handlers

A powerful feature of the scheme system is the ability to have **different handlers for the same scheme on different chains**. This is useful when:

- A specific chain requires custom logic (e.g., different gas handling, chain-specific optimizations)
- You want to override the default behavior for a particular chain
- You need chain-specific configuration

##### How It Works

1. **Create a custom scheme blueprint** that extends or modifies the base scheme behavior
2. **Register it with a unique ID** (e.g., `v1-eip155-exact-custom`)
3. **Enable it for specific chains** in your config

##### Example: Custom Handler for a Specific Chain

Suppose you want `eip155:3` to use custom logic while all other EVM chains use the standard `v1-eip155-exact`:

**Step 1: Create the custom scheme**

```rust
// In crates/chains/x402-chain-eip155/src/v1_eip155_exact_custom/mod.rs
use x402_types::scheme::X402SchemeId;

pub struct V1Eip155ExactCustom;

impl X402SchemeId for V1Eip155ExactCustom {
    fn x402_version(&self) -> u8 {
        1
    }

    fn namespace(&self) -> &str {
        "eip155"
    }

    fn scheme(&self) -> &str {
        "exact"  // Same scheme name - will handle "exact" payments
    }

    // Override the default ID to distinguish from the standard scheme
    fn id(&self) -> String {
        "v1-eip155-exact-custom".to_string()
    }
}

// In crates/chains/x402-chain-eip155/src/v1_eip155_exact_custom/facilitator.rs
use crate::chain::provider::Eip155ChainProviderLike;
use x402_types::chain::ChainProviderOps;
use x402_types::scheme::X402SchemeFacilitator;

impl<P> X402SchemeFacilitatorBuilder<P> for V1Eip155ExactCustom
where
    P: Eip155ChainProviderLike + ChainProviderOps + Send + Sync + 'static,
{
    fn build(&self, provider: P, config: Option<serde_json::Value>)
        -> Result<Box<dyn X402SchemeFacilitator>, Box<dyn Error>>
    {
        // Your custom facilitator with chain-specific logic
        Ok(Box::new(V1Eip155ExactCustomFacilitator::new(provider, config)))
    }
}

// In facilitator/src/schemes.rs
###[cfg(feature = "chain-eip155")]
use x402_chain_eip155::V1Eip155ExactCustom;

###[cfg(feature = "chain-eip155")]
impl X402SchemeFacilitatorBuilder<&ChainProvider> for V1Eip155ExactCustom {
    fn build(&self, provider: &ChainProvider, config: Option<serde_json::Value>)
        -> Result<Box<dyn X402SchemeFacilitator>, Box<dyn std::error::Error>>
    {
        let eip155_provider = if let ChainProvider::Eip155(provider) = provider {
            Arc::clone(provider)
        } else {
            return Err("V1Eip155ExactCustom::build: provider must be an Eip155ChainProvider".into());
        };
        self.build(eip155_provider, config)
    }
}
```

**Step 2: Register both schemes**

```rust
// In facilitator/src/schemes.rs
###[cfg(feature = "chain-eip155")]
use x402_chain_eip155::{V1Eip155Exact, V1Eip155ExactCustom};

// Then in your initialization code:
let blueprints = SchemeBlueprints::new()
    .and_register(V1Eip155Exact)        // Standard handler
    .and_register(V1Eip155ExactCustom); // Custom handler
```

**Step 3: Configure in JSON**

```json
{
  "chains": {
    "eip155:1": { ... },
    "eip155:3": { ... },
    "eip155:8453": { ... }
  },
  "schemes": [
    {
      "id": "v1-eip155-exact",
      "chains": "eip155:*"
    },
    {
      "id": "v1-eip155-exact-custom",
      "chains": "eip155:3",
      "config": { "customOption": "value" }
    }
  ]
}
```

##### Key Points

- The **scheme name** (returned by `scheme()`) determines which payment requests the handler processes
- The **ID** (returned by `id()`) is used to match config entries to blueprints
- Multiple blueprints can have the same `scheme()` but different `id()` values
- The `chains` pattern in config determines which chain(s) each blueprint instance handles
- Each config entry creates a separate handler instance for matching chains

##### Chain Pattern Matching

The `chains` field supports several patterns:

| Pattern | Matches |
|---------|---------|
| `eip155:84532` | Exact chain ID |
| `eip155:*` | All EVM chains |
| `solana:*` | All Solana chains |
| `eip155:{1,8453}` | Specific chain references |

## Contact [Resources]: Get in touch with the FareSide team
Source: /docs/contact.md

#### Primary Contact: Telegram

**[t.me/faresidehq](https://t.me/faresidehq)**

This is the best way to reach us for:

- Getting started
- Technical support
- Partnership inquiries
- Feature requests
- Bug reports

#### Open Source: x402-rs GitHub

**[github.com/x402-rs/x402-rs](https://github.com/x402-rs/x402-rs)**

For issues related to the open-source x402-rs implementation:

- Bug reports
- Feature requests
- Pull requests
- Documentation improvements

#### What to Include

When reaching out for support, please include:

##### For Technical Issues

- Network you're using (Base, Solana, Aptos, etc.)
- Error messages or codes
- Relevant code snippets
- Steps to reproduce

##### For Getting Started

- Brief description of your use case
- Expected transaction volume
- Preferred network(s)

#### Email

**[info@fareside.com](mailto:info@fareside.com)** — for partnerships, billing, and anything else.

#### Community

Join the conversation:

- **Telegram**: [t.me/faresidehq](https://t.me/faresidehq)
- **X**: [x.com/faresidehq](https://x.com/faresidehq)
- **x402-rs GitHub**: [github.com/x402-rs/x402-rs](https://github.com/x402-rs/x402-rs)

# Blog

## x402 Contracts Dashboard: A Missing Public Good: x402 payments don't work on a chain without a handful of shared contracts deployed there. We built a public dashboard to show which chains have them, and let anyone deploy the missing ones.
Source: /blog/2026-07-06-contracts-dashboard.md

x402 payments don't work on a chain if the required contracts aren't deployed there. Until now, there was no easy way to know if a given chain was actually ready: you'd probe each address yourself, or find out when a transaction failed.

We built the [Contracts Dashboard](https://contracts.fareside.com) to fix that.

### What These Contracts Are

A few contracts need to exist on any EVM chain before x402 payments can settle there:

- [_Create2 Factory_](https://github.com/Arachnid/deterministic-deployment-proxy) — the foundation that makes the rest of the addresses deterministic
- [_Uniswap V4 Permit2_](https://github.com/Uniswap/permit2) — the token approval layer used by the payment proxies
- [_x402ExactPermit2Proxy_](https://github.com/x402-foundation/x402/tree/main/contracts/evm#x402exactpermit2proxy) — payment settlement contracts for "exact" x402 scheme
- [_x402UptoPermit2Proxy_](https://github.com/x402-foundation/x402/tree/main/contracts/evm#x402uptopermit2proxy) — payment settlement contracts for "upto" x402 scheme
- _x402BatchSettlement_, _ERC3009DepositCollector_, _Permit2DepositCollector_ (see [x402 Foundation repository](https://github.com/x402-foundation/x402/tree/main/contracts/evm#canonical-addresses)) — settlement and collection variants for "batch-settlement" x402 scheme,
- [_UniversalSigValidator_](https://github.com/x402-rs/Validator6492) — signature validation for various signature types.

Every conformant x402 client and server depends on them. If your chain is missing them, nobody's x402 payments can settle there.

### The Dashboard

The dashboard shows every supported chain and whether each contract is deployed and has the expected bytecode. Green is deployed. Amber is missing.

![Contracts Dashboard — deployment status across supported chains](/images/dashboard/screenshot-of-the-full-dashboard.png)

That's it. One page. No noise.

You can see the status for all FareSide-supported chains without connecting anything. Connect a wallet, and you also get a live view of the chain's available contracts. Useful for checking a chain that's not yet on our list.

![Polygon Amoy connected — contracts missing](/images/dashboard/polygon-amoy-missing.png)

### Deploying Missing Contracts

If a chain you care about is missing contracts, you can deploy them yourself directly from the dashboard. No special tools required — just MetaMask with enough gas.

The flow:

1. Connect your wallet
2. Switch to the chain you want to deploy on
3. The top section shows the deployment status for that chain — missing contracts show a deploy button
4. Click deploy for any missing contract

![Deploying missing contracts on Polygon Amoy](/images/dashboard/amoy-deploying.png)

All these contracts are deployed via [Arachnid's CREATE2 deployer](https://github.com/Arachnid/deterministic-deployment-proxy). CREATE2 computes contract addresses from the deployer address, a salt, and the bytecode — not from the wallet doing the deploying. Whoever deploys it, whatever wallet they use, it lands at the same address on every chain.

You don't need to coordinate with anyone. Deploy it, and all x402 tooling finds it at its canonical address.

As x402 adds chains, the contracts need to follow. The gap between "this chain claims x402 support" and "x402 actually settles on this chain" has been invisible until now.
The dashboard makes it visible. Now anyone can close it.

The contracts dashboard is live at [contracts.fareside.com](https://contracts.fareside.com).

---

## Why x402 v2 Uses CAIP-2 Chain Identifiers: A deep dive into why x402 v2 adopted CAIP-2 chain identifiers instead of human-readable names, and why those weird-looking identifiers actually make sense.
Source: /blog/2026-01-22-x402-v2-caip2-identifiers.md

Early in x402's development, the human identifier approach showed its limits pretty quickly. CAIP-2 got proposed as an alternative in the GitHub discussions, and v2 adopted it. Here's why that was the right call.

### The v1 Problem That Needed Fixing

[x402 v1](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v1.md) started with good intentions. Coinbase wanted chain-agnostic payments, so they used friendly names like `base` and `base-sepolia`. Simple, readable, easy to understand. Naturally, they borrowed from their own [Developer Platform's network naming](https://docs.cdp.coinbase.com/api-reference/networks#network-identifiers) because why reinvent the wheel?

Then reality hit.

**The maintenance nightmare.** They hardcoded a closed list. Every network operator wanted their chain added. SDK maintainers became bottlenecks. The requests never stopped, and the list kept growing in ways that didn't scale.

**The inference problem.** Here's where it gets messy. EVM chains need one address format and transaction structure. Solana needs another. Hedera something else entirely. But looking at a name like "avalanche-fuji"? Good luck programmatically figuring out which family that belongs to without building a mountain of brittle if-statements.

**No collision prevention.** What happens when two chains want the same name? Who decides? There was no governance model for this.

### Enter CAIP-2

[x402 v2](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v2.md) adopts [CAIP-2](https://standards.chainagnostic.org/CAIPs/caip-2) from the [Chain Agnostic Standards Alliance (CASA)](https://www.chainagnostic.org). The format is `namespace:reference`. Namespace tells you the chain family. Reference uniquely identifies a specific chain within that family.

Here's what this looks like in practice:

- `eip155:8453` — Base (Ethereum family)
- `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` — Solana mainnet

First reaction? "What the hell is 5eykt4...?" Fair. Let me explain why it looks this way.

### The Logic Behind the Weirdness

CASA's philosophy is simple: don't fight existing standards, leverage them.

**EVM chains** already solved this with [EIP-155](https://eips.ethereum.org/EIPS/eip-155). The Ethereum community standardized on numeric chain IDs years ago. So CAIP-2 uses `eip155` as the namespace and the chain ID as the reference. `eip155:8453` is just saying "this is an EIP-155 chain with ID 8453." No new standard needed.

**Bitcoin-based chains** work the same way with `bip122`, pointing to Bitcoin's [BIP-122](https://bips.dev/122/) standard.

**Chains without formal standards** get creative. Filecoin uses their [internal conventions](https://github.com/ChainAgnostic/namespaces/blob/main/fil/caip2.md)&nbsp;— `f` for mainnet, `t` for testnet. Solana? They use a [hash of the genesis block](https://github.com/ChainAgnostic/namespaces/blob/main/solana/caip2.md). Not pretty to read, but cryptographically verifiable and guaranteed unique.

The key insight: anyone can propose a namespace for their chain family. The colon separator prevents naming conflicts. As long as your namespace is unique, your references can be whatever _your community_ already uses internally.

### Why This Actually Solves The Problem

Payment schemes in x402 are family-specific, not chain-specific. An EVM payment works the same way whether you're on Base, Optimism, or some new L2 launching next quarter. Solana payments work differently, but consistently across all Solana-family chains.

With CAIP-2, implementations can:

1. **Route correctly by namespace** — see `eip155`? Use the EVM payment handler. See `solana`? Use the Solana handler.

2. **Handle future chains automatically** — new EVM chain launching in 2026? Already works. No SDK update needed.

3. **Scale without manual maintenance** — nobody's a gatekeeper anymore. Chain families manage their own namespaces.

The identifiers look weird at first. But they trade a few minutes of unfamiliarity for years of maintainability and actual chain agnosticism. That's the bet v2 makes, and it's the right one.

---

**TL;DR**

- Those weird CAIP identifiers have real logic behind them
- They enable true chain agnosticism by encoding family information
- They make x402 mechanisms scalable without manual list maintenance

---

## Why FareSide Exists: Why we built FareSide: surviving the x402 memecoin surge at 77% market share, shutting down because free isn't sustainable, and bringing it back as production infrastructure.
Source: /blog/2025-12-05-hello-world.md

We didn't plan to start a company around x402 infrastructure. We built [x402-rs](https://github.com/x402-rs/x402-rs), our Rust implementation of x402 protocol, because the protocol itself was interesting, Rust was the right tool to handle volume of projected billions of AI agents, and the ecosystem needed an open-source implementation that worked.

It did work better than expected.

### The Surge

We started with a simple facilitator for developers to test against. Then Polygon reached out with a PR to support their network. That collaboration led us down the path of making it actually scalable: managing nonces across multiple private keys, handling concurrent transactions, building defensive architecture that wouldn't fall apart under real load.

Then the memecoin frenzy hit.

x402-rs became the second-largest facilitator by transaction volume, briefly hitting 77% of all x402 traffic. The exploration paid off: our infrastructure stayed online while others went down. But it also proved exactly how big the operational can of worms actually is at scale.

Gas spikes during high activity. Nonces collide across instances. RPC providers rate-limit you when you need them most. Transactions get stuck and need manual intervention. Every edge case you didn't plan for shows up in production, usually at 3am.

We handled it. Then we shut it down.

Not because it failed. Because subsidizing hundreds of thousands of daily transactions out of pocket isn't a business model. We weren't chasing clout or building for a leaderboard. We'd proven the infrastructure could handle real load. The economics couldn't.

### What We Learned

Running a facilitator at scale taught us things the spec doesn't cover:

**Settlement is probabilistic.** Gas spikes. Nonces collide. Transactions get stuck. RPC providers rate-limit you at the worst possible time. You need defensive architecture and redundancy at every place.

**Operations matter more than features.** The difference between a facilitator that works in demos and one that works in production is whether transactions actually land on-chain when the network is congested and your private key management doesn't cause nonce disasters across multiple instances.

**Sustainable infrastructure needs sustainable economics.** Free facilitators are marketing tools. They work until they don't, and their priorities shift with whoever's paying for them. Production workloads need infrastructure that's aligned with your operational requirements, not someone else's promo budget.

**Infrastructure failures break revenue.** Every failed settlement is a lost dollar and lost trust you never get to retry.

### The Fragmentation Problem

While we were rebuilding, we have been watching the ecosystem split.

One group strives to build own walled gardens with their own clients, their own APIs, incompatible with the broader protocol. Slightly better UX, marginally valuable features, but ultimately N-squared integration complexity.

Another group doubled down on the protocol: x402 as the standard, interoperability as the goal, Coinbase and Cloudflare backing it with real resources. Slower to mature, but if it works, any compliant client talks to any compliant API.

It all did play out before with TCP/IP vs IPX. The story is being unfolded now with stablecoins eating the lunch of banksters. Open protocols compound, proprietary platforms plateau.

### What We're Building

FareSide is us bringing x402-rs back as production infrastructure, operated sustainably.

We rebuilt the transaction relayer with hard-won lessons about failure isolation, observability, and operational discipline. The same Rust codebase now powers multiple production deployments, including Polygon's official facilitator, UltravioletaDAO, x402labs and countless other folks. We know it works because other people are running it at scale.

We're not trying to own the market. We're trying to provide one viable option for teams that need production x402 infrastructure but don't want to run it themselves. Protocol-first, multi-chain, interoperable by design.

### What's Next

FareSide is live. The same infrastructure that survived the surge now runs as a sustainable, production service.

If you're building something real on x402: an API that agents will pay for, a service that needs reliable settlement, and x402 is on your critical path to revenue, we'd like to work with you.
We are here to be the boring, reliable layer you never have to think about again.

[Get started at app.fareside.com](https://app.fareside.com) or [self-host with x402-rs](https://github.com/x402-rs/x402-rs) if you want to do it all on your own.