From c501b9160cb8800957973d4a0da78145cb7e124d Mon Sep 17 00:00:00 2001 From: welttowelt Date: Sat, 21 Feb 2026 22:56:44 +0100 Subject: [PATCH 1/3] Add Starknet facilitator-server coverage and validation for issue #41 --- .../facilitator-server/docker-compose.yml | 6 ++ examples/facilitator-server/src/index.ts | 5 + examples/facilitator-server/src/setup.ts | 25 +---- .../facilitator-server/src/starknet-config.ts | 41 ++++++++ examples/facilitator-server/tests/e2e-ci.ts | 60 +++++++++++ .../tests/starknet-config-validation.test.ts | 96 ++++++++++++++++++ .../tests/starknet-supported.test.ts | 99 +++++++++++++++++++ readme.md | 24 +++++ 8 files changed, 333 insertions(+), 23 deletions(-) create mode 100644 examples/facilitator-server/src/starknet-config.ts create mode 100644 examples/facilitator-server/tests/starknet-config-validation.test.ts create mode 100644 examples/facilitator-server/tests/starknet-supported.test.ts diff --git a/examples/facilitator-server/docker-compose.yml b/examples/facilitator-server/docker-compose.yml index bb53a2d6..1f705cda 100644 --- a/examples/facilitator-server/docker-compose.yml +++ b/examples/facilitator-server/docker-compose.yml @@ -42,6 +42,12 @@ services: # Optional SVM support # - SVM_PRIVATE_KEY=${SVM_PRIVATE_KEY} # - SVM_NETWORKS=${SVM_NETWORKS:-solana-devnet} + # Optional Starknet support (opt-in, disabled by default) + # - STARKNET_NETWORKS=${STARKNET_NETWORKS:-starknet-mainnet} + # - STARKNET_RPC_URL_STARKNET_MAINNET=${STARKNET_RPC_URL_STARKNET_MAINNET} + # - STARKNET_PAYMASTER_ENDPOINT_STARKNET_MAINNET=${STARKNET_PAYMASTER_ENDPOINT_STARKNET_MAINNET} + # - STARKNET_PAYMASTER_API_KEY=${STARKNET_PAYMASTER_API_KEY} + # - STARKNET_SPONSOR_ADDRESS_STARKNET_MAINNET=${STARKNET_SPONSOR_ADDRESS_STARKNET_MAINNET} # Optional RPC API keys (for better rate limits) # - ALCHEMY_API_KEY=${ALCHEMY_API_KEY} # - INFURA_API_KEY=${INFURA_API_KEY} diff --git a/examples/facilitator-server/src/index.ts b/examples/facilitator-server/src/index.ts index 8525832a..ef84a8a6 100644 --- a/examples/facilitator-server/src/index.ts +++ b/examples/facilitator-server/src/index.ts @@ -12,6 +12,11 @@ * - CDP_API_KEY_ID, CDP_API_KEY_SECRET, CDP_WALLET_SECRET: For CDP signer * - EVM_PRIVATE_KEY, SVM_PRIVATE_KEY: For private key signer (fallback) * - EVM_RPC_URL_BASE, EVM_RPC_URL_BASE_SEPOLIA: RPC URLs + * - STARKNET_NETWORKS: Comma-separated Starknet networks to enable (opt-in, default: disabled) + * - STARKNET_RPC_URL_STARKNET_MAINNET, STARKNET_RPC_URL_STARKNET_SEPOLIA: Starknet RPC URLs + * - STARKNET_PAYMASTER_ENDPOINT_STARKNET_MAINNET, STARKNET_PAYMASTER_ENDPOINT_STARKNET_SEPOLIA: Starknet paymaster endpoints + * - STARKNET_PAYMASTER_API_KEY, STARKNET_PAYMASTER_API_KEY_STARKNET_MAINNET, STARKNET_PAYMASTER_API_KEY_STARKNET_SEPOLIA: Paymaster API key(s) + * - STARKNET_SPONSOR_ADDRESS, STARKNET_SPONSOR_ADDRESS_STARKNET_MAINNET, STARKNET_SPONSOR_ADDRESS_STARKNET_SEPOLIA: Sponsor address(es), required for enabled Starknet networks * - UPTO_VERIFY_BALANCE_CHECK: "true" to enforce on-chain balance preflight in /verify for upto * - BEARER_TOKEN: Required bearer token for /verify and /settle * - BEARER_TOKENS: Optional comma-separated bearer token list (overrides BEARER_TOKEN) diff --git a/examples/facilitator-server/src/setup.ts b/examples/facilitator-server/src/setup.ts index 5af09263..b487cf1c 100644 --- a/examples/facilitator-server/src/setup.ts +++ b/examples/facilitator-server/src/setup.ts @@ -30,6 +30,7 @@ import { SVM_PRIVATE_KEY, CDP_ACCOUNT_NAME, } from "@daydreamsai/facilitator/config"; +import { buildStarknetConfigs } from "./starknet-config.js"; type EvmSignerConfig = FacilitatorConfig["evmSigners"] extends | (infer T)[] @@ -60,29 +61,7 @@ async function createDefaultSigners(): Promise<{ starknetConfigs: StarknetConfig[]; }> { const networkSetups = getNetworkSetups(); - const starknetNetworkSetups = getStarknetNetworkSetups(); - - const starknetConfigs: StarknetConfig[] = []; - for (const network of starknetNetworkSetups) { - if (!network.rpcUrl) { - console.warn(`⚠️ No RPC URL for ${network.name} - skipping`); - continue; - } - if (!network.paymasterEndpoint) { - console.warn(`⚠️ No paymaster endpoint for ${network.name} - skipping`); - continue; - } - - starknetConfigs.push({ - network: network.caip as StarknetConfig["network"], - rpcUrl: network.rpcUrl, - paymasterEndpoint: network.paymasterEndpoint, - ...(network.paymasterApiKey - ? { paymasterApiKey: network.paymasterApiKey } - : {}), - sponsorAddress: network.sponsorAddress, - }); - } + const starknetConfigs = buildStarknetConfigs(getStarknetNetworkSetups()); if (USE_CDP) { // CDP Signer (preferred) diff --git a/examples/facilitator-server/src/starknet-config.ts b/examples/facilitator-server/src/starknet-config.ts new file mode 100644 index 00000000..4bf0ae8c --- /dev/null +++ b/examples/facilitator-server/src/starknet-config.ts @@ -0,0 +1,41 @@ +import type { FacilitatorConfig } from "@daydreamsai/facilitator"; +import type { StarknetNetworkSetup } from "@daydreamsai/facilitator/config"; + +type StarknetConfig = FacilitatorConfig["starknetConfigs"] extends + | (infer T)[] + | undefined + ? T + : never; + +/** + * Converts validated Starknet network setup entries into facilitator configs. + * Networks with unresolved runtime dependencies are skipped defensively. + */ +export function buildStarknetConfigs( + starknetNetworkSetups: StarknetNetworkSetup[] +): StarknetConfig[] { + const starknetConfigs: StarknetConfig[] = []; + + for (const network of starknetNetworkSetups) { + if (!network.rpcUrl) { + console.warn(`⚠️ No RPC URL for ${network.name} - skipping`); + continue; + } + if (!network.paymasterEndpoint) { + console.warn(`⚠️ No paymaster endpoint for ${network.name} - skipping`); + continue; + } + + starknetConfigs.push({ + network: network.caip as StarknetConfig["network"], + rpcUrl: network.rpcUrl, + paymasterEndpoint: network.paymasterEndpoint, + ...(network.paymasterApiKey + ? { paymasterApiKey: network.paymasterApiKey } + : {}), + sponsorAddress: network.sponsorAddress, + }); + } + + return starknetConfigs; +} diff --git a/examples/facilitator-server/tests/e2e-ci.ts b/examples/facilitator-server/tests/e2e-ci.ts index 75fe7cb9..10ea6091 100644 --- a/examples/facilitator-server/tests/e2e-ci.ts +++ b/examples/facilitator-server/tests/e2e-ci.ts @@ -11,6 +11,12 @@ const serverDir = resolve(testsDir, ".."); const DATABASE_URL = process.env.DATABASE_URL; const PORT = Number(process.env.PORT ?? "18090"); const BEARER_TOKEN = process.env.BEARER_TOKEN ?? "e2e-test-token"; +const STARKNET_NETWORKS = "starknet-mainnet"; +const STARKNET_MAINNET_CAIP = "starknet:SN_MAIN"; +const STARKNET_MAINNET_RPC_URL = "https://starknet-mainnet.example.com"; +const STARKNET_MAINNET_PAYMASTER_ENDPOINT = + "https://starknet.paymaster.avnu.fi"; +const STARKNET_MAINNET_SPONSOR = "0xstarknet-mainnet-sponsor"; if (!DATABASE_URL) { throw new Error("DATABASE_URL is required for e2e test"); @@ -74,6 +80,46 @@ async function waitForRecord(pool: Pool, timeoutMs = 15_000): Promise { throw new Error("No /verify tracking row found in Postgres"); } +function assertStarknetSupportedPayload(payload: unknown): void { + const supported = payload as { + kinds?: Array<{ + network?: string; + scheme?: string; + x402Version?: number; + extra?: { paymasterEndpoint?: string; sponsorAddress?: string }; + }>; + signers?: Record; + }; + + const starknetKind = supported.kinds?.find( + (kind) => + kind.network === STARKNET_MAINNET_CAIP && kind.scheme === "exact" + ); + + assert.ok(starknetKind, "Expected /supported to include Starknet exact kind"); + assert.equal( + starknetKind?.x402Version, + 2, + "Expected Starknet supported kind to be x402 v2" + ); + assert.equal( + starknetKind?.extra?.paymasterEndpoint, + STARKNET_MAINNET_PAYMASTER_ENDPOINT, + "Expected Starknet paymaster endpoint in /supported extra metadata" + ); + assert.equal( + starknetKind?.extra?.sponsorAddress, + STARKNET_MAINNET_SPONSOR, + "Expected Starknet sponsor address in /supported extra metadata" + ); + + const starknetSigners = supported.signers?.["starknet:*"] ?? []; + assert.ok( + starknetSigners.includes(STARKNET_MAINNET_SPONSOR), + "Expected Starknet sponsor address in /supported signers" + ); +} + async function run(): Promise { const baseUrl = `http://127.0.0.1:${PORT}`; const pool = new Pool({ connectionString: DATABASE_URL }); @@ -97,6 +143,11 @@ async function run(): Promise { BEARER_TOKEN, EVM_PRIVATE_KEY: privateKey, EVM_NETWORKS: process.env.EVM_NETWORKS ?? "base-sepolia", + STARKNET_NETWORKS, + STARKNET_RPC_URL_STARKNET_MAINNET: STARKNET_MAINNET_RPC_URL, + STARKNET_PAYMASTER_ENDPOINT_STARKNET_MAINNET: + STARKNET_MAINNET_PAYMASTER_ENDPOINT, + STARKNET_SPONSOR_ADDRESS_STARKNET_MAINNET: STARKNET_MAINNET_SPONSOR, }, stdout: "pipe", stderr: "pipe", @@ -109,6 +160,15 @@ async function run(): Promise { await waitForServer(baseUrl); await pool.query("TRUNCATE TABLE resource_call_records"); + const supportedResponse = await fetch(`${baseUrl}/supported`); + assert.equal( + supportedResponse.status, + 200, + "Expected /supported to return 200" + ); + const supportedPayload = await supportedResponse.json(); + assertStarknetSupportedPayload(supportedPayload); + const verifyResponse = await fetch(`${baseUrl}/verify`, { method: "POST", headers: { diff --git a/examples/facilitator-server/tests/starknet-config-validation.test.ts b/examples/facilitator-server/tests/starknet-config-validation.test.ts new file mode 100644 index 00000000..b198b75b --- /dev/null +++ b/examples/facilitator-server/tests/starknet-config-validation.test.ts @@ -0,0 +1,96 @@ +import { describe, expect, it } from "bun:test"; +import { resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { buildStarknetConfigs } from "../src/starknet-config.js"; + +const __filename = fileURLToPath(import.meta.url); +const testsDir = resolve(__filename, ".."); +const serverDir = resolve(testsDir, ".."); + +const E2E_PRIVATE_KEY = + "0x0000000000000000000000000000000000000000000000000000000000000001"; + +describe("Starknet setup validation", () => { + it("fails fast with an actionable error when sponsor is missing", () => { + const processResult = Bun.spawnSync({ + cmd: ["bun", "-e", 'import "./src/setup.ts";'], + cwd: serverDir, + env: { + PATH: process.env.PATH ?? "", + HOME: process.env.HOME ?? "", + EVM_PRIVATE_KEY: E2E_PRIVATE_KEY, + EVM_NETWORKS: "base-sepolia", + STARKNET_NETWORKS: "starknet-mainnet", + STARKNET_RPC_URL_STARKNET_MAINNET: "https://starknet-mainnet.example.com", + }, + stdout: "pipe", + stderr: "pipe", + }); + + const stdout = new TextDecoder().decode(processResult.stdout); + const stderr = new TextDecoder().decode(processResult.stderr); + const combinedOutput = `${stdout}\n${stderr}`; + + expect(processResult.exitCode).not.toBe(0); + expect(combinedOutput).toContain( + "Missing Starknet sponsor address for starknet-mainnet" + ); + expect(combinedOutput).toContain( + "STARKNET_SPONSOR_ADDRESS_STARKNET_MAINNET" + ); + }); + + it("skips Starknet networks with unresolved RPC URLs", () => { + const warnings: string[] = []; + const originalWarn = console.warn; + console.warn = (...args: unknown[]) => { + warnings.push(args.map((value) => String(value)).join(" ")); + }; + + try { + const configs = buildStarknetConfigs([ + { + name: "starknet-mainnet", + caip: "starknet:SN_MAIN", + rpcUrl: undefined, + paymasterEndpoint: "https://starknet.paymaster.avnu.fi", + paymasterApiKey: "api-key", + sponsorAddress: "0xmainnet-sponsor", + }, + ]); + + expect(configs).toEqual([]); + expect(warnings).toContain("⚠️ No RPC URL for starknet-mainnet - skipping"); + } finally { + console.warn = originalWarn; + } + }); + + it("skips Starknet networks with unresolved paymaster endpoints", () => { + const warnings: string[] = []; + const originalWarn = console.warn; + console.warn = (...args: unknown[]) => { + warnings.push(args.map((value) => String(value)).join(" ")); + }; + + try { + const configs = buildStarknetConfigs([ + { + name: "starknet-sepolia", + caip: "starknet:SN_SEPOLIA", + rpcUrl: "https://starknet-sepolia.example.com", + paymasterEndpoint: undefined, + paymasterApiKey: "api-key", + sponsorAddress: "0xsepolia-sponsor", + }, + ]); + + expect(configs).toEqual([]); + expect(warnings).toContain( + "⚠️ No paymaster endpoint for starknet-sepolia - skipping" + ); + } finally { + console.warn = originalWarn; + } + }); +}); diff --git a/examples/facilitator-server/tests/starknet-supported.test.ts b/examples/facilitator-server/tests/starknet-supported.test.ts new file mode 100644 index 00000000..3da48e9a --- /dev/null +++ b/examples/facilitator-server/tests/starknet-supported.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from "bun:test"; +import { createFacilitator } from "@daydreamsai/facilitator"; +import { createApp } from "../src/app.js"; + +interface SupportedKind { + x402Version: number; + network: string; + scheme: string; + extra?: Record; +} + +interface SupportedResponse { + kinds: SupportedKind[]; + signers: Record; +} + +const MAINNET_CONFIG = { + network: "starknet:SN_MAIN" as const, + rpcUrl: "https://starknet-mainnet.example.com", + paymasterEndpoint: "https://starknet.paymaster.avnu.fi", + sponsorAddress: "0xmainnet-sponsor", +}; + +const SEPOLIA_CONFIG = { + network: "starknet:SN_SEPOLIA" as const, + rpcUrl: "https://starknet-sepolia.example.com", + paymasterEndpoint: "https://paymaster-sepolia.example.com", + sponsorAddress: "0xsepolia-sponsor", +}; + +async function fetchSupported(): Promise { + const facilitator = createFacilitator({ + starknetConfigs: [MAINNET_CONFIG, SEPOLIA_CONFIG], + }); + + const app = createApp({ facilitator }); + const response = await app.handle(new Request("http://localhost/supported")); + + expect(response.status).toBe(200); + + return response.json() as Promise; +} + +describe("/supported Starknet integration", () => { + it("returns Starknet kinds when Starknet configs are registered", async () => { + const supported = await fetchSupported(); + + const starknetKinds = supported.kinds.filter((kind) => + kind.network.startsWith("starknet:") + ); + + expect(starknetKinds.length).toBe(2); + }); + + it("includes Starknet paymaster and sponsor metadata in kind.extra", async () => { + const supported = await fetchSupported(); + + const mainnetKind = supported.kinds.find( + (kind) => kind.network === MAINNET_CONFIG.network && kind.scheme === "exact" + ); + const sepoliaKind = supported.kinds.find( + (kind) => kind.network === SEPOLIA_CONFIG.network && kind.scheme === "exact" + ); + + expect(mainnetKind?.extra).toEqual({ + paymasterEndpoint: MAINNET_CONFIG.paymasterEndpoint, + sponsorAddress: MAINNET_CONFIG.sponsorAddress, + }); + + expect(sepoliaKind?.extra).toEqual({ + paymasterEndpoint: SEPOLIA_CONFIG.paymasterEndpoint, + sponsorAddress: SEPOLIA_CONFIG.sponsorAddress, + }); + }); + + it('includes Starknet sponsor addresses in signers["starknet:*"]', async () => { + const supported = await fetchSupported(); + + const starknetSigners = supported.signers["starknet:*"] ?? []; + + expect(starknetSigners).toContain(MAINNET_CONFIG.sponsorAddress); + expect(starknetSigners).toContain(SEPOLIA_CONFIG.sponsorAddress); + }); + + it("keeps Starknet networks canonical (starknet:SN_*) with x402 v2", async () => { + const supported = await fetchSupported(); + + const starknetKinds = supported.kinds.filter((kind) => + kind.network.startsWith("starknet:") + ); + + const networks = starknetKinds.map((kind) => kind.network).sort(); + expect(networks).toEqual(["starknet:SN_MAIN", "starknet:SN_SEPOLIA"]); + + for (const kind of starknetKinds) { + expect(kind.x402Version).toBe(2); + } + }); +}); diff --git a/readme.md b/readme.md index 4a2fc58b..85018fa8 100644 --- a/readme.md +++ b/readme.md @@ -276,6 +276,30 @@ bun dev curl http://localhost:8090/supported ``` +### Facilitator Server (Starknet Opt-in) + +Starknet is disabled by default. Enable it explicitly with `STARKNET_NETWORKS`. + +```bash +cd examples/facilitator-server + +PORT=8090 \ +BEARER_TOKEN=local-dev-token \ +EVM_PRIVATE_KEY=0x... \ +STARKNET_NETWORKS=starknet-mainnet,starknet-sepolia \ +STARKNET_RPC_URL_STARKNET_MAINNET=https://starknet-mainnet.example.com \ +STARKNET_RPC_URL_STARKNET_SEPOLIA=https://starknet-sepolia.example.com \ +STARKNET_PAYMASTER_ENDPOINT_STARKNET_MAINNET=https://starknet.paymaster.avnu.fi \ +STARKNET_PAYMASTER_ENDPOINT_STARKNET_SEPOLIA=https://starknet.paymaster.avnu.fi \ +STARKNET_PAYMASTER_API_KEY=your-avnu-api-key \ +STARKNET_SPONSOR_ADDRESS=0xyour-sponsor-address \ +bun run dev +``` + +`STARKNET_SPONSOR_ADDRESS` can be set globally as above or per-network with +`STARKNET_SPONSOR_ADDRESS_STARKNET_MAINNET` and +`STARKNET_SPONSOR_ADDRESS_STARKNET_SEPOLIA`. + ### One-Click Deploy (Railway) Deploy your own facilitator instance to Railway with one click: From c6788858a9261b087abd1079467bf4c39300cd8a Mon Sep 17 00:00:00 2001 From: welttowelt Date: Sat, 21 Feb 2026 23:18:31 +0100 Subject: [PATCH 2/3] Address review nitpicks on Starknet test setup and type alias --- examples/facilitator-server/src/setup.ts | 10 +++--- .../facilitator-server/src/starknet-config.ts | 2 +- .../tests/starknet-supported.test.ts | 36 +++++++++---------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/examples/facilitator-server/src/setup.ts b/examples/facilitator-server/src/setup.ts index b487cf1c..3aa1c519 100644 --- a/examples/facilitator-server/src/setup.ts +++ b/examples/facilitator-server/src/setup.ts @@ -30,7 +30,10 @@ import { SVM_PRIVATE_KEY, CDP_ACCOUNT_NAME, } from "@daydreamsai/facilitator/config"; -import { buildStarknetConfigs } from "./starknet-config.js"; +import { + buildStarknetConfigs, + type StarknetConfig, +} from "./starknet-config.js"; type EvmSignerConfig = FacilitatorConfig["evmSigners"] extends | (infer T)[] @@ -42,11 +45,6 @@ type SvmSignerConfig = FacilitatorConfig["svmSigners"] extends | undefined ? T : never; -type StarknetConfig = FacilitatorConfig["starknetConfigs"] extends - | (infer T)[] - | undefined - ? T - : never; type NetworkId = EvmSignerConfig["networks"]; const UPTO_VERIFY_BALANCE_CHECK = process.env.UPTO_VERIFY_BALANCE_CHECK === "true"; diff --git a/examples/facilitator-server/src/starknet-config.ts b/examples/facilitator-server/src/starknet-config.ts index 4bf0ae8c..63cad183 100644 --- a/examples/facilitator-server/src/starknet-config.ts +++ b/examples/facilitator-server/src/starknet-config.ts @@ -1,7 +1,7 @@ import type { FacilitatorConfig } from "@daydreamsai/facilitator"; import type { StarknetNetworkSetup } from "@daydreamsai/facilitator/config"; -type StarknetConfig = FacilitatorConfig["starknetConfigs"] extends +export type StarknetConfig = FacilitatorConfig["starknetConfigs"] extends | (infer T)[] | undefined ? T diff --git a/examples/facilitator-server/tests/starknet-supported.test.ts b/examples/facilitator-server/tests/starknet-supported.test.ts index 3da48e9a..f6588b8a 100644 --- a/examples/facilitator-server/tests/starknet-supported.test.ts +++ b/examples/facilitator-server/tests/starknet-supported.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "bun:test"; +import { beforeAll, describe, expect, it } from "bun:test"; import { createFacilitator } from "@daydreamsai/facilitator"; import { createApp } from "../src/app.js"; @@ -28,22 +28,21 @@ const SEPOLIA_CONFIG = { sponsorAddress: "0xsepolia-sponsor", }; -async function fetchSupported(): Promise { - const facilitator = createFacilitator({ - starknetConfigs: [MAINNET_CONFIG, SEPOLIA_CONFIG], - }); - - const app = createApp({ facilitator }); - const response = await app.handle(new Request("http://localhost/supported")); +describe("/supported Starknet integration", () => { + let supported: SupportedResponse; - expect(response.status).toBe(200); + beforeAll(async () => { + const facilitator = createFacilitator({ + starknetConfigs: [MAINNET_CONFIG, SEPOLIA_CONFIG], + }); + const app = createApp({ facilitator }); + const response = await app.handle(new Request("http://localhost/supported")); - return response.json() as Promise; -} + expect(response.status).toBe(200); + supported = await (response.json() as Promise); + }); -describe("/supported Starknet integration", () => { - it("returns Starknet kinds when Starknet configs are registered", async () => { - const supported = await fetchSupported(); + it("returns Starknet kinds when Starknet configs are registered", () => { const starknetKinds = supported.kinds.filter((kind) => kind.network.startsWith("starknet:") @@ -52,8 +51,7 @@ describe("/supported Starknet integration", () => { expect(starknetKinds.length).toBe(2); }); - it("includes Starknet paymaster and sponsor metadata in kind.extra", async () => { - const supported = await fetchSupported(); + it("includes Starknet paymaster and sponsor metadata in kind.extra", () => { const mainnetKind = supported.kinds.find( (kind) => kind.network === MAINNET_CONFIG.network && kind.scheme === "exact" @@ -73,8 +71,7 @@ describe("/supported Starknet integration", () => { }); }); - it('includes Starknet sponsor addresses in signers["starknet:*"]', async () => { - const supported = await fetchSupported(); + it('includes Starknet sponsor addresses in signers["starknet:*"]', () => { const starknetSigners = supported.signers["starknet:*"] ?? []; @@ -82,8 +79,7 @@ describe("/supported Starknet integration", () => { expect(starknetSigners).toContain(SEPOLIA_CONFIG.sponsorAddress); }); - it("keeps Starknet networks canonical (starknet:SN_*) with x402 v2", async () => { - const supported = await fetchSupported(); + it("keeps Starknet networks canonical (starknet:SN_*) with x402 v2", () => { const starknetKinds = supported.kinds.filter((kind) => kind.network.startsWith("starknet:") From c93b21ea636539a0658df99524cc1a065a1e18d9 Mon Sep 17 00:00:00 2001 From: welttowelt Date: Sat, 21 Feb 2026 23:33:18 +0100 Subject: [PATCH 3/3] Improve Starknet supported test diagnostics --- .../facilitator-server/tests/starknet-supported.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/facilitator-server/tests/starknet-supported.test.ts b/examples/facilitator-server/tests/starknet-supported.test.ts index f6588b8a..6b070570 100644 --- a/examples/facilitator-server/tests/starknet-supported.test.ts +++ b/examples/facilitator-server/tests/starknet-supported.test.ts @@ -60,12 +60,15 @@ describe("/supported Starknet integration", () => { (kind) => kind.network === SEPOLIA_CONFIG.network && kind.scheme === "exact" ); - expect(mainnetKind?.extra).toEqual({ + expect(mainnetKind).toBeDefined(); + expect(sepoliaKind).toBeDefined(); + + expect(mainnetKind!.extra).toEqual({ paymasterEndpoint: MAINNET_CONFIG.paymasterEndpoint, sponsorAddress: MAINNET_CONFIG.sponsorAddress, }); - expect(sepoliaKind?.extra).toEqual({ + expect(sepoliaKind!.extra).toEqual({ paymasterEndpoint: SEPOLIA_CONFIG.paymasterEndpoint, sponsorAddress: SEPOLIA_CONFIG.sponsorAddress, });