Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions examples/typescript/facilitator/basic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ and fill required environment variables:
- `SVM_PRIVATE_KEY` - Solana facilitator private key
- `PORT` - Server port (optional, defaults to 4022)

Optional environment variables:

- `EVM_NETWORK` _(recommended for testnet exploration)_ - CAIP-2 EVM network the
facilitator settles on. Default is `eip155:84532` (Base Sepolia). Chains shipped
in viem's chain database (and present in
[`DEFAULT_STABLECOINS`](../../../../typescript/packages/mechanisms/evm/src/shared/defaultAssets.ts))
just work — e.g. `eip155:8453` for Base Mainnet. For chains viem doesn't ship a
default RPC for (or to use a custom RPC), set `EVM_RPC_URL`. The Solana accept
stays on Solana Devnet by default.
- `EVM_RPC_URL` - Custom RPC URL override. Required for chains where viem ships no
default RPC (e.g. Mezo Testnet `eip155:31611`).

**⚠️ Security Note:** The facilitator key is the signer used to settle payments on-chain. Keep it separate from your seller `payTo` wallet and buyer test wallets, and make sure it is funded only for facilitator gas/fees.

2. Install and build all packages from the typescript examples root:
Expand Down
60 changes: 53 additions & 7 deletions examples/typescript/facilitator/basic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,60 @@ import { toFacilitatorSvmSigner } from "@x402/svm";
import { ExactSvmScheme } from "@x402/svm/exact/facilitator";
import dotenv from "dotenv";
import express from "express";
import { createWalletClient, http, publicActions } from "viem";
import {
type Chain,
createWalletClient,
defineChain,
http,
publicActions,
} from "viem";
import * as allChains from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";

dotenv.config();

// Configuration
const PORT = process.env.PORT || "4022";

// CAIP-2 EVM network selection. Default is Base Sepolia (eip155:84532); set
// EVM_NETWORK to point at any EVM chain. EVM_RPC_URL overrides viem's chain
// default RPC (required for chains viem doesn't ship with a public RPC).
const EVM_NETWORK = (process.env.EVM_NETWORK ??
"eip155:84532") as `${string}:${string}`;

/**
* Map a CAIP-2 EVM identifier to a viem `Chain`. Falls back to a minimal
* `defineChain` so chains viem hasn't packaged still work for callers
* supplying their own EVM_RPC_URL.
*
* @param caip2 - CAIP-2 EVM identifier (e.g. "eip155:84532")
* @returns viem Chain object suitable for createWalletClient/createPublicClient
*/
function resolveViemChain(caip2: string): Chain {
const [namespace, ref] = caip2.split(":");
if (namespace !== "eip155") {
throw new Error(`resolveViemChain: not an EVM network: ${caip2}`);
}
const chainId = Number(ref);
if (!Number.isInteger(chainId) || chainId <= 0) {
throw new Error(`resolveViemChain: invalid EVM chain id in ${caip2}`);
}
const known = (Object.values(allChains) as Chain[]).find(
(c) => c.id === chainId,
);
if (known) return known;
return defineChain({
id: chainId,
name: `EVM ${chainId}`,
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: [] } },
});
}

const evmChain = resolveViemChain(EVM_NETWORK);
const evmRpcUrl =
process.env.EVM_RPC_URL?.trim() || evmChain.rpcUrls.default?.http?.[0] || "";

// Validate required environment variables
if (!process.env.EVM_PRIVATE_KEY) {
console.error("❌ EVM_PRIVATE_KEY environment variable is required");
Expand All @@ -39,6 +84,7 @@ const evmAccount = privateKeyToAccount(
process.env.EVM_PRIVATE_KEY as `0x${string}`,
);
console.info(`EVM Facilitator account: ${evmAccount.address}`);
console.info(`EVM Network: ${EVM_NETWORK} (${evmChain.name})`);

// Initialize the SVM account from private key
const svmAccount = await createKeyPairSignerFromBytes(
Expand All @@ -49,8 +95,8 @@ console.info(`SVM Facilitator account: ${svmAccount.address}`);
// Create a Viem client with both wallet and public capabilities
const viemClient = createWalletClient({
account: evmAccount,
chain: baseSepolia,
transport: http(),
chain: evmChain,
transport: http(evmRpcUrl || undefined),
}).extend(publicActions);

// Initialize the x402 Facilitator with EVM and SVM support
Expand Down Expand Up @@ -118,10 +164,10 @@ const facilitator = new x402Facilitator()

// Register EVM and SVM schemes
facilitator.register(
"eip155:84532",
EVM_NETWORK,
new ExactEvmScheme(evmSigner, { deployERC4337WithEIP6492: true }),
); // Base Sepolia
facilitator.register("eip155:84532", new UptoEvmScheme(evmSigner));
);
facilitator.register(EVM_NETWORK, new UptoEvmScheme(evmSigner));
facilitator.register(
"solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
new ExactSvmScheme(svmSigner),
Expand Down
4 changes: 4 additions & 0 deletions examples/typescript/servers/advanced/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ and fill required environment variables:

> **Hedera Testnet:** Get testnet HBAR from the [Hedera Faucet](https://portal.hedera.com/faucet).

Optional environment variables (apply to the `dynamic-price`, `dynamic-pay-to`, `hooks`, and `custom-money-definition` demos):

- `EVM_NETWORK` _(recommended for testnet exploration)_ - CAIP-2 EVM network the route accepts. Default is `eip155:84532` (Base Sepolia). Set to any chain in [`DEFAULT_STABLECOINS`](../../../../typescript/packages/mechanisms/evm/src/shared/defaultAssets.ts) to retarget the demo. The `custom-money-definition` demo also keeps a separate `eip155:100` (Gnosis Chain) literal as part of the registerMoneyParser teaching surface — that literal is intentional and not driven by `EVM_NETWORK`.

2. Install and build all packages from the typescript examples root:

```bash
Expand Down
11 changes: 9 additions & 2 deletions examples/typescript/servers/advanced/custom-money-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ if (!facilitatorUrl) {
}
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });

// CAIP-2 EVM network selection for the route. Default is Base Sepolia (eip155:84532);
// set EVM_NETWORK to point at any EVM chain in @x402/evm's DEFAULT_STABLECOINS.
// NOTE: the eip155:100 (Gnosis Chain) literal below is intentional — it teaches
// the registerMoneyParser pattern for a custom-chain asset, independent of the
// route's chain selection.
const EVM_NETWORK = (process.env.EVM_NETWORK ?? "eip155:84532") as `${string}:${string}`;

const app = express();

app.use(
Expand All @@ -28,15 +35,15 @@ app.use(
accepts: {
scheme: "exact",
price: "$0.001",
network: "eip155:84532",
network: EVM_NETWORK,
payTo: evmAddress,
},
description: "Weather data",
mimeType: "application/json",
},
},
new x402ResourceServer(facilitatorClient).register(
"eip155:84532",
EVM_NETWORK,
new ExactEvmScheme().registerMoneyParser(async (amount, network) => {
// Custom money parser such that on the Gnosis Chain (xDai) network, we use Wrapped XDAI (WXDAI) when describing money
// NOTE: Wrapped XDAI is not an EIP-3009 complaint token, and would fail the current ExactEvm implementation. This example is for demonstration purposes
Expand Down
8 changes: 6 additions & 2 deletions examples/typescript/servers/advanced/dynamic-pay-to.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ if (!facilitatorUrl) {
}
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });

// CAIP-2 EVM network selection. Default is Base Sepolia (eip155:84532); set
// EVM_NETWORK to point at any EVM chain in @x402/evm's DEFAULT_STABLECOINS.
const EVM_NETWORK = (process.env.EVM_NETWORK ?? "eip155:84532") as `${string}:${string}`;

const app = express();

app.use(
Expand All @@ -38,7 +42,7 @@ app.use(
accepts: {
scheme: "exact",
price: "$0.001",
network: "eip155:84532",
network: EVM_NETWORK,
payTo: context => {
// Dynamic payTo based on HTTP request context
const country = context.adapter.getQueryParam?.("country") ?? "US";
Expand All @@ -49,7 +53,7 @@ app.use(
mimeType: "application/json",
},
},
new x402ResourceServer(facilitatorClient).register("eip155:84532", new ExactEvmScheme()),
new x402ResourceServer(facilitatorClient).register(EVM_NETWORK, new ExactEvmScheme()),
),
);

Expand Down
8 changes: 6 additions & 2 deletions examples/typescript/servers/advanced/dynamic-price.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ if (!facilitatorUrl) {
}
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });

// CAIP-2 EVM network selection. Default is Base Sepolia (eip155:84532); set
// EVM_NETWORK to point at any EVM chain in @x402/evm's DEFAULT_STABLECOINS.
const EVM_NETWORK = (process.env.EVM_NETWORK ?? "eip155:84532") as `${string}:${string}`;

const app = express();

app.use(
Expand All @@ -31,14 +35,14 @@ app.use(
const tier = context.adapter.getQueryParam?.("tier") ?? "standard";
return tier === "premium" ? "$0.005" : "$0.001";
},
network: "eip155:84532",
network: EVM_NETWORK,
payTo: evmAddress,
},
description: "Weather data",
mimeType: "application/json",
},
},
new x402ResourceServer(facilitatorClient).register("eip155:84532", new ExactEvmScheme()),
new x402ResourceServer(facilitatorClient).register(EVM_NETWORK, new ExactEvmScheme()),
),
);

Expand Down
8 changes: 6 additions & 2 deletions examples/typescript/servers/advanced/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ if (!facilitatorUrl) {
}
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });

// CAIP-2 EVM network selection. Default is Base Sepolia (eip155:84532); set
// EVM_NETWORK to point at any EVM chain in @x402/evm's DEFAULT_STABLECOINS.
const EVM_NETWORK = (process.env.EVM_NETWORK ?? "eip155:84532") as `${string}:${string}`;

const resourceServer = new x402ResourceServer(facilitatorClient)
.register("eip155:84532", new ExactEvmScheme())
.register(EVM_NETWORK, new ExactEvmScheme())
.onBeforeVerify(async context => {
console.log("Before verify hook", context);
// Abort verification by returning { abort: true, reason: string }
Expand Down Expand Up @@ -54,7 +58,7 @@ app.use(
accepts: {
scheme: "exact",
price: "$0.001",
network: "eip155:84532",
network: EVM_NETWORK,
payTo: evmAddress,
},
description: "Weather data",
Expand Down
4 changes: 4 additions & 0 deletions examples/typescript/servers/custom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ and fill required environment variables:
- `FACILITATOR_URL` - Facilitator endpoint URL
- `EVM_ADDRESS` - Ethereum address to receive payments

Optional environment variables:

- `EVM_NETWORK` _(recommended for testnet exploration)_ - CAIP-2 EVM network the route accepts. Default is `eip155:84532` (Base Sepolia). Set to any chain in [`DEFAULT_STABLECOINS`](../../../../typescript/packages/mechanisms/evm/src/shared/defaultAssets.ts) to retarget the demo.

2. Install and build all packages from the typescript examples root:

```bash
Expand Down
11 changes: 8 additions & 3 deletions examples/typescript/servers/custom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,20 @@ if (!facilitatorUrl) {
process.exit(1);
}

// CAIP-2 EVM network selection. Default is Base Sepolia (eip155:84532); set
// EVM_NETWORK to point at any EVM chain in @x402/evm's DEFAULT_STABLECOINS.
const EVM_NETWORK = (process.env.EVM_NETWORK ?? "eip155:84532") as `${string}:${string}`;

console.log("\n🔧 Custom x402 Server Implementation");
console.log("This example demonstrates manual payment handling without middleware.\n");
console.log(`✅ Payment address: ${evmAddress}`);
console.log(`✅ Facilitator: ${facilitatorUrl}\n`);
console.log(`✅ Facilitator: ${facilitatorUrl}`);
console.log(`✅ Network: ${EVM_NETWORK}\n`);

// Create facilitator client and resource server
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
const resourceServer = new x402ResourceServer(facilitatorClient).register(
"eip155:84532",
EVM_NETWORK,
new ExactEvmScheme(),
);

Expand All @@ -61,7 +66,7 @@ const routeConfigs: Record<string, RoutePaymentConfig> = {
"GET /weather": {
scheme: "exact",
price: "$0.001",
network: "eip155:84532",
network: EVM_NETWORK,
payTo: evmAddress,
description: "Weather data",
mimeType: "application/json",
Expand Down
8 changes: 8 additions & 0 deletions examples/typescript/servers/express/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ and fill required environment variables:
- `EVM_ADDRESS` - Ethereum address to receive payments
- `SVM_ADDRESS` - Solana address to receive payments

Optional environment variables:

- `EVM_NETWORK` *(recommended for testnet exploration)* - CAIP-2 EVM network the route accepts.
Default is `eip155:84532` (Base Sepolia). Set to any chain in
[`DEFAULT_STABLECOINS`](../../../../typescript/packages/mechanisms/evm/src/shared/defaultAssets.ts)
to retarget the demo — e.g. `eip155:8453` for Base Mainnet, `eip155:31611` for
Mezo Testnet. The SVM accept stays on Solana Devnet by default.

2. Install and build all packages from the typescript examples root:
```bash
cd ../../
Expand Down
8 changes: 6 additions & 2 deletions examples/typescript/servers/express/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ if (!evmAddress || !svmAddress) {
process.exit(1);
}

// CAIP-2 EVM network selection. Default is Base Sepolia (eip155:84532); set
// EVM_NETWORK to point at any EVM chain in @x402/evm's DEFAULT_STABLECOINS.
const EVM_NETWORK = (process.env.EVM_NETWORK ?? "eip155:84532") as `${string}:${string}`;

const facilitatorUrl = process.env.FACILITATOR_URL;
if (!facilitatorUrl) {
console.error("❌ FACILITATOR_URL environment variable is required");
Expand All @@ -30,7 +34,7 @@ app.use(
{
scheme: "exact",
price: "$0.001",
network: "eip155:84532",
network: EVM_NETWORK,
payTo: evmAddress,
},
{
Expand All @@ -45,7 +49,7 @@ app.use(
},
},
new x402ResourceServer(facilitatorClient)
.register("eip155:84532", new ExactEvmScheme())
.register(EVM_NETWORK, new ExactEvmScheme())
.register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()),
),
);
Expand Down
8 changes: 8 additions & 0 deletions examples/typescript/servers/fastify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ and fill required environment variables:
- `EVM_ADDRESS` - Ethereum address to receive payments
- `SVM_ADDRESS` - Solana address to receive payments

Optional environment variables:

- `EVM_NETWORK` *(recommended for testnet exploration)* - CAIP-2 EVM network the route accepts.
Default is `eip155:84532` (Base Sepolia). Set to any chain in
[`DEFAULT_STABLECOINS`](../../../../typescript/packages/mechanisms/evm/src/shared/defaultAssets.ts)
to retarget the demo — e.g. `eip155:8453` for Base Mainnet, `eip155:31611` for
Mezo Testnet. The SVM accept stays on Solana Devnet by default.

2. Install and build all packages from the typescript examples root:

```bash
Expand Down
8 changes: 6 additions & 2 deletions examples/typescript/servers/fastify/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ if (!evmAddress || !svmAddress) {
process.exit(1);
}

// CAIP-2 EVM network selection. Default is Base Sepolia (eip155:84532); set
// EVM_NETWORK to point at any EVM chain in @x402/evm's DEFAULT_STABLECOINS.
const EVM_NETWORK = (process.env.EVM_NETWORK ?? "eip155:84532") as `${string}:${string}`;

const facilitatorUrl = process.env.FACILITATOR_URL;
if (!facilitatorUrl) {
console.error("❌ FACILITATOR_URL environment variable is required");
Expand All @@ -30,7 +34,7 @@ paymentMiddleware(
{
scheme: "exact",
price: "$0.001",
network: "eip155:84532",
network: EVM_NETWORK,
payTo: evmAddress,
},
{
Expand All @@ -45,7 +49,7 @@ paymentMiddleware(
},
},
new x402ResourceServer(facilitatorClient)
.register("eip155:84532", new ExactEvmScheme())
.register(EVM_NETWORK, new ExactEvmScheme())
.register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()),
);

Expand Down
8 changes: 8 additions & 0 deletions examples/typescript/servers/hono/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ and fill required environment variables:
- `EVM_ADDRESS` - Ethereum address to receive payments
- `SVM_ADDRESS` - Solana address to receive payments

Optional environment variables:

- `EVM_NETWORK` *(recommended for testnet exploration)* - CAIP-2 EVM network the route accepts.
Default is `eip155:84532` (Base Sepolia). Set to any chain in
[`DEFAULT_STABLECOINS`](../../../../typescript/packages/mechanisms/evm/src/shared/defaultAssets.ts)
to retarget the demo — e.g. `eip155:8453` for Base Mainnet, `eip155:31611` for
Mezo Testnet. The SVM accept stays on Solana Devnet by default.

2. Install and build all packages from the typescript examples root:

```bash
Expand Down
Loading
Loading