| Crates.io | x402-rs |
| lib.rs | x402-rs |
| version | 0.12.5 |
| created_at | 2025-05-08 14:36:53.520413+00 |
| updated_at | 2026-01-21 15:06:40.696062+00 |
| description | x402 payments in Rust: verify, settle, and monitor payments over HTTP 402 flows |
| homepage | https://x402.rs |
| repository | https://github.com/x402-rs/x402-rs |
| max_upload_size | |
| id | 1665414 |
| size | 686,757 |
A Rust-based implementation of the x402 protocol with support for protocol v1 and v2.
This repository provides:
x402-rs (current crate):
x402-axum - Axum middleware for accepting x402 payments,x402-reqwest - Wrapper for reqwest for transparent x402 payments,x402-axum-example - an example of x402-axum usage with multi-chain support.x402-reqwest-example - an example of x402-reqwest usage with multi-chain support.The x402 protocol is a proposed standard for making blockchain payments directly through HTTP using native 402 Payment Required status code.
Servers declare payment requirements for specific routes. Clients send cryptographically signed payment payloads. Facilitators verify and settle payments on-chain.
docker run -v $(pwd)/config.json:/app/config.json -p 8080:8080 ghcr.io/x402-rs/x402-facilitator
Or build locally:
docker build -t x402-rs .
docker run -v $(pwd)/config.json:/app/config.json -p 8080:8080 x402-rs
See the Facilitator section below for full usage details
Use x402-axum to gate your routes behind on-chain payments:
use alloy_primitives::address;
use axum::{Router, routing::get};
use x402_axum::X402Middleware;
use x402_rs::networks::USDC;
use x402_rs::scheme::v2_eip155_exact::V2Eip155Exact;
let x402 = X402Middleware::try_from("https://facilitator.x402.rs").unwrap();
let app = Router::new().route(
"/paid-content",
get(handler).layer(
x402.with_price_tag(V2Eip155Exact::price_tag(
address!("0xYourAddress"),
USDC::base_sepolia().amount(10u64),
))
),
);
See x402-axum crate docs.
Use x402-reqwest to send payments:
use x402_reqwest::{ReqwestWithPayments, ReqwestWithPaymentsBuild, X402Client};
use x402_rs::scheme::v1_eip155_exact::client::V1Eip155ExactClient;
use x402_rs::scheme::v2_eip155_exact::client::V2Eip155ExactClient;
use alloy_signer_local::PrivateKeySigner;
use std::sync::Arc;
use reqwest::Client;
let signer: Arc<PrivateKeySigner> = Arc::new("0x...".parse()?); // never hardcode real keys!
let x402_client = X402Client::new()
.register(V1Eip155ExactClient::new(signer.clone()))
.register(V2Eip155ExactClient::new(signer));
let client = Client::new()
.with_payments(x402_client)
.build();
let res = client
.get("https://example.com/protected")
.send()
.await?;
The middleware automatically:
402 Payment Required responsesFor multi-chain support (EVM and Solana), register additional scheme clients:
use x402_rs::scheme::v1_solana_exact::client::V1SolanaExactClient;
use x402_rs::scheme::v2_solana_exact::client::V2SolanaExactClient;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_keypair::Keypair;
let solana_keypair = Arc::new(Keypair::from_base58_string("..."));
let solana_rpc = Arc::new(RpcClient::new("https://api.devnet.solana.com"));
let x402_client = X402Client::new()
.register(V1Eip155ExactClient::new(evm_signer.clone()))
.register(V2Eip155ExactClient::new(evm_signer))
.register(V1SolanaExactClient::new(solana_keypair.clone(), solana_rpc.clone()))
.register(V2SolanaExactClient::new(solana_keypair, solana_rpc));
| Milestone | Description | Status |
|---|---|---|
| Facilitator for Base USDC | Payment verification and settlement service, enabling real-time pay-per-use transactions for Base chain. | ✅ Complete |
| Metrics and Tracing | Expose OpenTelemetry metrics and structured tracing for observability, monitoring, and debugging | ✅ Complete |
| Server Middleware | Provide ready-to-use integration for Rust web frameworks such as axum and tower. | ✅ Complete |
| Client Library | Provide a lightweight Rust library for initiating and managing x402 payment flows from Rust clients. | ✅ Complete |
| Solana Support | Support Solana chain. | ✅ Complete |
| Protocol v2 Support | Support x402 protocol version 2 with improved payload structure. | ✅ Complete |
| Multiple chains and multiple tokens | Support various tokens and EVM compatible chains. | ✅ Complete |
| Axum Middleware v2 Support | Full x402 protocol v2 support in x402-axum with multi-chain, multi-scheme architecture. | ✅ Complete |
| Reqwest Client v2 Support | Full x402 protocol v2 support in x402-reqwest with multi-chain, multi-scheme architecture. | ✅ Complete |
| Buiild your own facilitator hooks | Pre/post hooks for analytics, access control, and auditability. | 🔜 Planned |
| Bazaar Extension | Marketplace integration for discovering and purchasing x402-protected resources. | 🔜 Planned |
| Gasless Approval Flow | Support for Permit2 and ERC20 approvals to enable gasless payment authorization. | 🔜 Planned |
| Upto Scheme | Payment scheme supporting "up to" amount payments with flexible pricing. | 🔜 Planned |
| Deferred Scheme | Payment scheme supporting deferred settlement and payment scheduling. | 🔜 Planned |
The initial focus is on establishing a stable, production-quality Rust SDK and middleware ecosystem for x402 integration.
The x402-rs crate (this repo) provides a runnable x402 facilitator binary. The Facilitator role simplifies adoption of x402 by handling:
By using a Facilitator, servers (sellers) do not need to:
Instead, they can rely on the Facilitator to perform verification and settlement, reducing operational overhead and accelerating x402 adoption. The Facilitator never holds user funds. It acts solely as a stateless verification and execution layer for signed payment payloads.
For a detailed overview of the x402 payment flow and Facilitator role, see the x402 protocol documentation.
Create a config.json file with your chain and scheme configuration:
{
"port": 8080,
"host": "0.0.0.0",
"chains": {
"eip155:84532": {
"eip1559": true,
"flashblocks": true,
"signers": ["$EVM_PRIVATE_KEY"],
"rpc": [
{
"http": "https://sepolia.base.org",
"rate_limit": 50
}
]
},
"solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG": {
"signer": "$SOLANA_PRIVATE_KEY",
"rpc": "https://api.devnet.solana.com",
"pubsub": "wss://api.devnet.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:*"
}
]
}
Configuration structure:
chains: A map of CAIP-2 chain identifiers to chain-specific configuration
eip155:*): Configure signers (array of private keys), rpc endpoints, and optional eip1559/flashblocks flagssolana:*): Configure signer (single private key), rpc endpoint, and optional pubsub endpointschemes: List of payment schemes to enable
id: Scheme identifier in format v{version}-{namespace}-{name} (e.g., v2-eip155-exact)chains: Chain pattern to match (e.g., eip155:* for all EVM chains, eip155:84532 for specific chain)Environment variable references:
Private keys can reference environment variables using $VAR or ${VAR} syntax:
"signers": ["$EVM_PRIVATE_KEY"]
Then set the environment variable:
export EVM_PRIVATE_KEY=0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
Prebuilt Docker images are available at GitHub Container Registry: ghcr.io/x402-rs/x402-facilitator
Run the container:
docker run -v $(pwd)/config.json:/app/config.json -p 8080:8080 ghcr.io/x402-rs/x402-facilitator
Or build a Docker image locally:
docker build -t x402-rs .
docker run -v $(pwd)/config.json:/app/config.json -p 8080:8080 x402-rs
You can also pass environment variables for private keys:
docker run -v $(pwd)/config.json:/app/config.json \
-e EVM_PRIVATE_KEY=0x... \
-e SOLANA_PRIVATE_KEY=... \
-p 8080:8080 ghcr.io/x402-rs/x402-facilitator
The container:
8080 (or a port you configure in config.json).debian:bullseye-slim).If you are building an x402-powered application, update the Facilitator URL to point to your self-hosted instance.
ℹ️ Tip: For production deployments, ensure your Facilitator is reachable via HTTPS and protect it against public abuse.
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();
const payTo = "0xYourAddress";
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator"
});
const server = new x402ResourceServer(facilitatorClient);
registerExactEvmScheme(server);
app.use(
paymentMiddleware(
{
"/protected-route": {
accepts: [
{
scheme: "exact",
price: "$0.10",
network: "eip155:84532",
payTo,
},
],
description: "Access to premium content",
mimeType: "application/json",
},
},
server,
),
);
app.get("/protected-route", (c) => {
return c.json({ message: "This content is behind a paywall" });
});
serve({ fetch: app.fetch, port: 3000 });
use alloy_primitives::address;
use axum::{Router, routing::get};
use axum::response::IntoResponse;
use http::StatusCode;
use x402_axum::X402Middleware;
use x402_rs::networks::USDC;
use x402_rs::scheme::v2_eip155_exact::V2Eip155Exact;
let x402 = X402Middleware::new("https://facilitator.x402.rs"); // 👈 Your self-hosted Facilitator
let app = Router::new().route(
"/paid-content",
get(handler).layer(
x402.with_price_tag(V2Eip155Exact::price_tag(
address!("0xYourAddress"),
USDC::base_sepolia().amount(10u64),
))
),
);
async fn handler() -> impl IntoResponse {
(StatusCode::OK, "This is VIP content!")
}
The service reads configuration from a JSON file (config.json by default) or via CLI argument --config <path>.
{
"port": 8080,
"host": "0.0.0.0",
"chains": { ... },
"schemes": [ ... ]
}
| Option | Type | Default | Description |
|---|---|---|---|
port |
number | 8080 |
HTTP server port (can also be set via PORT env var) |
host |
string | "0.0.0.0" |
HTTP host to bind to (can also be set via HOST env var) |
chains |
object | {} |
Map of CAIP-2 chain IDs to chain configuration |
schemes |
array | [] |
List of payment schemes to enable |
eip155:*){
"eip155:84532": {
"eip1559": true,
"flashblocks": true,
"receipt_timeout_secs": 30,
"signers": ["$EVM_PRIVATE_KEY"],
"rpc": [
{
"http": "https://sepolia.base.org",
"rate_limit": 50
}
]
}
}
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
signers |
array | ✅ | - | Array of private keys (hex format, 0x-prefixed) or env var references |
rpc |
array | ✅ | - | Array of RPC endpoint configurations |
rpc[].http |
string | ✅ | - | HTTP URL for the RPC endpoint |
rpc[].rate_limit |
number | ❌ | - | Rate limit for requests per second |
eip1559 |
boolean | ❌ | true |
Use EIP-1559 transaction type (type 2) instead of legacy transactions |
flashblocks |
boolean | ❌ | false |
Estimate gas against "latest" block to accommodate flashblocks-enabled RPC semantics |
receipt_timeout_secs |
number | ❌ | 30 |
Timeout for waiting for transaction receipt |
solana:*){
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": {
"signer": "$SOLANA_PRIVATE_KEY",
"rpc": "https://api.mainnet-beta.solana.com",
"pubsub": "wss://api.mainnet-beta.solana.com",
"max_compute_unit_limit": 400000,
"max_compute_unit_price": 1000000
}
}
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
signer |
string | ✅ | - | Private key (base58 format, 64 bytes) or env var reference |
rpc |
string | ✅ | - | HTTP URL for the RPC endpoint |
pubsub |
string | ❌ | - | WebSocket URL for pubsub notifications |
max_compute_unit_limit |
number | ❌ | 400000 |
Maximum compute unit limit for transactions |
max_compute_unit_price |
number | ❌ | 1000000 |
Maximum compute unit price for transactions |
{
"schemes": [
{
"enabled": true,
"id": "v2-eip155-exact",
"chains": "eip155:*",
"config": {}
}
]
}
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
enabled |
boolean | ❌ | true |
Whether this scheme is enabled |
id |
string | ✅ | - | Scheme identifier: v{version}-{namespace}-{name} |
chains |
string | ✅ | - | Chain pattern: eip155:*, solana:*, or specific chain ID |
config |
object | ❌ | - | Scheme-specific configuration |
Important: Schemes must be explicitly listed in the schemes array to be enabled. If a scheme is not in the configuration, it will not be available for payment verification or settlement.
Available schemes:
v1-eip155-exact - ERC-3009 transferWithAuthorization for EVM chains (protocol v1)v2-eip155-exact - ERC-3009 transferWithAuthorization for EVM chains (protocol v2)v1-solana-exact - SPL token transfer for Solana (protocol v1)v2-solana-exact - SPL token transfer for Solana (protocol v2)Environment variables can be used for:
$VAR or ${VAR} syntaxPORT and HOST as fallbacks if not in config fileRUST_LOG for log level (e.g., info, debug, trace)The facilitator emits OpenTelemetry-compatible traces and metrics to standard endpoints, making it easy to integrate with tools like Honeycomb, Prometheus, Grafana, and others. Tracing spans are annotated with HTTP method, status code, URI, latency, other request and process metadata.
To enable tracing and metrics export, set the appropriate OTEL_ environment variables:
# For Honeycomb, for example:
# Endpoint URL for sending OpenTelemetry traces and metrics
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io:443
# Comma-separated list of key=value pairs to add as headers
OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your_api_key,x-honeycomb-dataset=x402-rs
# Export protocol to use for telemetry. Supported values: `http/protobuf` (default), `grpc`
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
The service automatically detects and initializes exporters if OTEL_EXPORTER_OTLP_* variables are provided.
The Facilitator supports any network you configure in config.json. Common chain identifiers:
| Network | CAIP-2 Chain ID | Notes |
|---|---|---|
| Base Sepolia Testnet | eip155:84532 |
Testnet, Recommended for testing |
| Base Mainnet | eip155:8453 |
Mainnet |
| Ethereum Mainnet | eip155:1 |
Mainnet |
| Avalanche Fuji Testnet | eip155:43113 |
Testnet |
| Avalanche C-Chain Mainnet | eip155:43114 |
Mainnet |
| Polygon Amoy Testnet | eip155:80002 |
Testnet |
| Polygon Mainnet | eip155:137 |
Mainnet |
| Sei Testnet | eip155:713715 |
Testnet |
| Sei Mainnet | eip155:1329 |
Mainnet |
| Solana Mainnet | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp |
Mainnet |
| Solana Devnet | solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG |
Testnet, Recommended for testing |
Networks are enabled by adding them to the chains section in your config.json.
ℹ️ Tip: For initial development and testing, you can start with Base Sepolia (
eip155:84532) or Solana Devnet only.
Prerequisites:
cargo and a working toolchainBuild locally:
cargo build
Run with a config file:
cargo run -- --config config.json
Or place config.json in the current directory (it will be auto-detected):
cargo run
Feel free to open issues or pull requests to improve x402 support in the Rust ecosystem.