diff --git a/examples/typescript/facilitator/basic/README.md b/examples/typescript/facilitator/basic/README.md index 952ca4b2d7..298a989bc8 100644 --- a/examples/typescript/facilitator/basic/README.md +++ b/examples/typescript/facilitator/basic/README.md @@ -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: diff --git a/examples/typescript/facilitator/basic/index.ts b/examples/typescript/facilitator/basic/index.ts index 2a90c30b27..5a614005f9 100644 --- a/examples/typescript/facilitator/basic/index.ts +++ b/examples/typescript/facilitator/basic/index.ts @@ -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"); @@ -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( @@ -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 @@ -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), diff --git a/examples/typescript/servers/advanced/README.md b/examples/typescript/servers/advanced/README.md index f3c0286d6f..f87bb866e8 100644 --- a/examples/typescript/servers/advanced/README.md +++ b/examples/typescript/servers/advanced/README.md @@ -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 diff --git a/examples/typescript/servers/advanced/custom-money-definition.ts b/examples/typescript/servers/advanced/custom-money-definition.ts index 401bc36c7a..48883fe7ce 100644 --- a/examples/typescript/servers/advanced/custom-money-definition.ts +++ b/examples/typescript/servers/advanced/custom-money-definition.ts @@ -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( @@ -28,7 +35,7 @@ app.use( accepts: { scheme: "exact", price: "$0.001", - network: "eip155:84532", + network: EVM_NETWORK, payTo: evmAddress, }, description: "Weather data", @@ -36,7 +43,7 @@ app.use( }, }, 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 diff --git a/examples/typescript/servers/advanced/dynamic-pay-to.ts b/examples/typescript/servers/advanced/dynamic-pay-to.ts index c676d83fa4..c7d8b48e1f 100644 --- a/examples/typescript/servers/advanced/dynamic-pay-to.ts +++ b/examples/typescript/servers/advanced/dynamic-pay-to.ts @@ -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( @@ -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"; @@ -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()), ), ); diff --git a/examples/typescript/servers/advanced/dynamic-price.ts b/examples/typescript/servers/advanced/dynamic-price.ts index ab30e80343..3d3d8946bf 100644 --- a/examples/typescript/servers/advanced/dynamic-price.ts +++ b/examples/typescript/servers/advanced/dynamic-price.ts @@ -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( @@ -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()), ), ); diff --git a/examples/typescript/servers/advanced/hooks.ts b/examples/typescript/servers/advanced/hooks.ts index 016e434c07..ebf0245c1d 100644 --- a/examples/typescript/servers/advanced/hooks.ts +++ b/examples/typescript/servers/advanced/hooks.ts @@ -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 } @@ -54,7 +58,7 @@ app.use( accepts: { scheme: "exact", price: "$0.001", - network: "eip155:84532", + network: EVM_NETWORK, payTo: evmAddress, }, description: "Weather data", diff --git a/examples/typescript/servers/custom/README.md b/examples/typescript/servers/custom/README.md index d94311af78..20c0d81a84 100644 --- a/examples/typescript/servers/custom/README.md +++ b/examples/typescript/servers/custom/README.md @@ -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 diff --git a/examples/typescript/servers/custom/index.ts b/examples/typescript/servers/custom/index.ts index 897b54bdab..ca6f45f221 100644 --- a/examples/typescript/servers/custom/index.ts +++ b/examples/typescript/servers/custom/index.ts @@ -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(), ); @@ -61,7 +66,7 @@ const routeConfigs: Record = { "GET /weather": { scheme: "exact", price: "$0.001", - network: "eip155:84532", + network: EVM_NETWORK, payTo: evmAddress, description: "Weather data", mimeType: "application/json", diff --git a/examples/typescript/servers/express/README.md b/examples/typescript/servers/express/README.md index 2891fbcb1d..0542af3259 100644 --- a/examples/typescript/servers/express/README.md +++ b/examples/typescript/servers/express/README.md @@ -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 ../../ diff --git a/examples/typescript/servers/express/index.ts b/examples/typescript/servers/express/index.ts index aab5e3e720..104f7eef98 100644 --- a/examples/typescript/servers/express/index.ts +++ b/examples/typescript/servers/express/index.ts @@ -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"); @@ -30,7 +34,7 @@ app.use( { scheme: "exact", price: "$0.001", - network: "eip155:84532", + network: EVM_NETWORK, payTo: evmAddress, }, { @@ -45,7 +49,7 @@ app.use( }, }, new x402ResourceServer(facilitatorClient) - .register("eip155:84532", new ExactEvmScheme()) + .register(EVM_NETWORK, new ExactEvmScheme()) .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()), ), ); diff --git a/examples/typescript/servers/fastify/README.md b/examples/typescript/servers/fastify/README.md index dcb087d304..866048aecf 100644 --- a/examples/typescript/servers/fastify/README.md +++ b/examples/typescript/servers/fastify/README.md @@ -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 diff --git a/examples/typescript/servers/fastify/index.ts b/examples/typescript/servers/fastify/index.ts index 8f1bc079e4..82d114a58e 100644 --- a/examples/typescript/servers/fastify/index.ts +++ b/examples/typescript/servers/fastify/index.ts @@ -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"); @@ -30,7 +34,7 @@ paymentMiddleware( { scheme: "exact", price: "$0.001", - network: "eip155:84532", + network: EVM_NETWORK, payTo: evmAddress, }, { @@ -45,7 +49,7 @@ paymentMiddleware( }, }, new x402ResourceServer(facilitatorClient) - .register("eip155:84532", new ExactEvmScheme()) + .register(EVM_NETWORK, new ExactEvmScheme()) .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()), ); diff --git a/examples/typescript/servers/hono/README.md b/examples/typescript/servers/hono/README.md index 6809144390..e772df5daa 100644 --- a/examples/typescript/servers/hono/README.md +++ b/examples/typescript/servers/hono/README.md @@ -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 diff --git a/examples/typescript/servers/hono/index.ts b/examples/typescript/servers/hono/index.ts index 3dc85285c7..d671fd7525 100644 --- a/examples/typescript/servers/hono/index.ts +++ b/examples/typescript/servers/hono/index.ts @@ -14,6 +14,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"); @@ -31,7 +35,7 @@ app.use( { scheme: "exact", price: "$0.001", - network: "eip155:84532", + network: EVM_NETWORK, payTo: evmAddress, }, { @@ -46,7 +50,7 @@ app.use( }, }, new x402ResourceServer(facilitatorClient) - .register("eip155:84532", new ExactEvmScheme()) + .register(EVM_NETWORK, new ExactEvmScheme()) .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()), ), ); diff --git a/examples/typescript/servers/self-facilitation/README.md b/examples/typescript/servers/self-facilitation/README.md index 065a59520d..cdc7c11a64 100644 --- a/examples/typescript/servers/self-facilitation/README.md +++ b/examples/typescript/servers/self-facilitation/README.md @@ -26,6 +26,17 @@ Then fill required environment variables: - `EVM_PRIVATE_KEY` - Ethereum private key used by the embedded facilitator +Optional environment variables: + +- `EVM_NETWORK` *(recommended for testnet exploration)* - CAIP-2 EVM network the embedded + facilitator operates 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`. +- `EVM_RPC_URL` - Custom RPC URL override. Required for chains where viem ships no + default RPC (e.g. Mezo Testnet `eip155:31611`). + 2. Install and build all packages from the TypeScript examples root: ```bash diff --git a/examples/typescript/servers/self-facilitation/index.ts b/examples/typescript/servers/self-facilitation/index.ts index b2ac8a0b82..f704fd0b29 100644 --- a/examples/typescript/servers/self-facilitation/index.ts +++ b/examples/typescript/servers/self-facilitation/index.ts @@ -5,9 +5,9 @@ import { registerExactEvmScheme } from "@x402/evm/exact/facilitator"; import { ExactEvmScheme as ExactEvmServerScheme } from "@x402/evm/exact/server"; import { config } 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"; config(); @@ -16,13 +16,48 @@ if (!process.env.EVM_PRIVATE_KEY) { process.exit(1); } +// 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] || ""; + const evmAccount = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`); // 1) Build facilitator signer from an on-chain client. const viemClient = createWalletClient({ account: evmAccount, - chain: baseSepolia, - transport: http(), + chain: evmChain, + transport: http(evmRpcUrl || undefined), }).extend(publicActions); const evmSigner = toFacilitatorEvmSigner({ @@ -39,7 +74,7 @@ const evmSigner = toFacilitatorEvmSigner({ const facilitator = new x402Facilitator(); registerExactEvmScheme(facilitator, { signer: evmSigner, - networks: "eip155:84532", // Base Sepolia + networks: EVM_NETWORK, }); // 3) Use standard express middleware wired to the local facilitator. @@ -53,7 +88,7 @@ app.use( { scheme: "exact", price: "$0.001", - network: "eip155:84532", + network: EVM_NETWORK, payTo: evmAccount.address, }, ], @@ -65,7 +100,7 @@ app.use( verify: facilitator.verify.bind(facilitator), settle: facilitator.settle.bind(facilitator), getSupported: async () => facilitator.getSupported(), - }).register("eip155:84532", new ExactEvmServerScheme()), + }).register(EVM_NETWORK, new ExactEvmServerScheme()), ), ); diff --git a/examples/typescript/servers/upto/index.ts b/examples/typescript/servers/upto/index.ts index f75071bc9c..4baa511cec 100644 --- a/examples/typescript/servers/upto/index.ts +++ b/examples/typescript/servers/upto/index.ts @@ -19,6 +19,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(); // The "upto" scheme authorizes up to a maximum amount but settles only what you specify. @@ -32,7 +36,7 @@ app.use( accepts: { scheme: "upto", price: maxPrice, - network: "eip155:84532", + network: EVM_NETWORK, payTo: evmAddress, }, description: "AI text generation — billed by token usage", @@ -42,7 +46,7 @@ app.use( }, }, }, - new x402ResourceServer(facilitatorClient).register("eip155:84532", new UptoEvmScheme()), + new x402ResourceServer(facilitatorClient).register(EVM_NETWORK, new UptoEvmScheme()), ), );