diff --git a/e2e/coinbase/facilitators/typescript/README.md b/e2e/coinbase/facilitators/typescript/README.md new file mode 100644 index 00000000..a87bbcad --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/README.md @@ -0,0 +1,189 @@ +# E2E Test Facilitator: TypeScript + +This facilitator demonstrates and tests the TypeScript x402 facilitator implementation with EVM, SVM, and optional Stellar payment verification and settlement. + +## What It Tests + +### Core Functionality +- ✅ **V2 Protocol** - Modern x402 facilitator protocol +- ✅ **V1 Protocol** - Legacy x402 facilitator protocol +- ✅ **Payment Verification** - Validates payment payloads off-chain +- ✅ **Payment Settlement** - Executes transactions on-chain +- ✅ **Multi-chain Support** - EVM, SVM, and (optional) Stellar mechanisms +- ✅ **HTTP API** - Express.js server exposing facilitator endpoints + +### Facilitator Endpoints +- ✅ `POST /verify` - Verifies payment payload validity +- ✅ `POST /settle` - Settles payment on blockchain +- ✅ `GET /supported` - Returns supported payment kinds +- ✅ **Extension Support** - Bazaar discovery extension + +## What It Demonstrates + +### Lifecycle Hooks Usage + +This e2e facilitator showcases **production-ready lifecycle hook patterns**: + +```typescript +const facilitator = new x402Facilitator() + .register("eip155:*", new ExactEvmFacilitator(evmSigner)) + .register("stellar:*", new ExactStellarScheme([stellarSigner])) + .registerExtension(BAZAAR) + // Hook 1: Track verified payments + extract discovery info + .onAfterVerify(async (context) => { + if (context.result.isValid) { + const paymentHash = createPaymentHash(context.paymentPayload); + verifiedPayments.set(paymentHash, context.timestamp); + + // Catalog discovered resources + const discovered = extractDiscoveryInfo(context.paymentPayload, context.requirements); + if (discovered) { + bazaarCatalog.catalogResource(discovered); + } + } + }) + // Hook 2: Validate payment was verified before settlement + .onBeforeSettle(async (context) => { + const paymentHash = createPaymentHash(context.paymentPayload); + if (!verifiedPayments.has(paymentHash)) { + return { abort: true, reason: "Payment must be verified first" }; + } + + // Check timeout + const age = context.timestamp - verifiedPayments.get(paymentHash)!; + if (age > 5 * 60 * 1000) { + return { abort: true, reason: "Verification expired" }; + } + }) + // Hook 3: Clean up tracking after settlement + .onAfterSettle(async (context) => { + const paymentHash = createPaymentHash(context.paymentPayload); + verifiedPayments.delete(paymentHash); + }) + // Hook 4: Clean up on failure too + .onSettleFailure(async (context) => { + const paymentHash = createPaymentHash(context.paymentPayload); + verifiedPayments.delete(paymentHash); + }); +``` + + +### Facilitator Setup + +```typescript +import { x402Facilitator } from "@x402/core/facilitator"; +import { ExactEvmFacilitator } from "@x402/evm"; +import { ExactEvmFacilitatorV1, NETWORKS as EVM_NETWORKS } from "@x402/evm/v1"; +import { ExactSvmFacilitator } from "@x402/svm"; +import { ExactSvmFacilitatorV1, NETWORKS as SVM_NETWORKS } from "@x402/svm/v1"; +import { ExactStellarFacilitator } from "@x402/stellar"; + +// Create facilitator with bazaar extension +const facilitator = new x402Facilitator() + .registerExtension("bazaar"); + +// Register EVM V2 wildcard +facilitator.register( + "eip155:*", + new ExactEvmFacilitator(evmSigner) +); + +// Register all EVM V1 networks +EVM_NETWORKS.forEach(network => { + facilitator.registerSchemeV1( + network, + new ExactEvmFacilitatorV1(evmSigner) + ); +}); + +// Register SVM schemes similarly... + +// Register Stellar (v2) schemes similarly... +``` + +### HTTP Server + +```typescript +import express from "express"; +import { createFacilitatorRouter } from "@x402/server/facilitator"; + +const app = express(); +app.use(express.json()); + +// Mount facilitator routes at root +app.use("/", createFacilitatorRouter(facilitator)); + +app.listen(port, () => { + console.log(`Facilitator ready at http://localhost:${port}`); +}); +``` + +### Key Concepts Shown + +1. **Extension Registration** - Bazaar discovery +2. **Comprehensive Network Support** - All EVM V1 networks, all SVM V1 networks +3. **Wildcard Schemes** - Efficient V2 registration with `eip155:*`, `solana:*`, and `stellar:*` +4. **HTTP Router Integration** - `@x402/server/facilitator` for Express +5. **Real Signers** - Actual blockchain transaction submission +6. **Multi-Protocol** - V1 and V2 side-by-side + +## Test Scenarios + +This facilitator is tested with: +- **Clients:** TypeScript Fetch, Go HTTP +- **Servers:** Express (TypeScript), Gin (Go) +- **Networks:** Base Sepolia (EVM), Solana Devnet (SVM), Stellar Testnet (Stellar) +- **Test Cases:** + - V1 EVM payments + - V2 EVM payments + - V1 SVM payments + - V2 SVM payments + - V2 Stellar payments (optional) + +### Success Criteria +- ✅ Verification returns valid status +- ✅ Settlement returns transaction hash +- ✅ Supported endpoint lists all mechanisms +- ✅ Bazaar extension included + +## Running + +```bash +# Via e2e test suite +cd e2e +pnpm test --facilitator=typescript + +# Direct execution +cd e2e/facilitators/typescript +export EVM_PRIVATE_KEY="0x..." +export SVM_PRIVATE_KEY="..." +export STELLAR_PRIVATE_KEY="S..." # optional +export PORT=4025 +pnpm start +``` + +## Environment Variables + +### Required +- `PORT` - HTTP server port +- `EVM_PRIVATE_KEY` - Ethereum private key (hex with 0x prefix) +- `SVM_PRIVATE_KEY` - Solana private key (base58 encoded) + +### Optional +- `STELLAR_PRIVATE_KEY` - Stellar private key (S... format) - enables Stellar support +- `EVM_NETWORK` - EVM network (default: eip155:84532) +- `SVM_NETWORK` - SVM network (default: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1) +- `STELLAR_NETWORK` - Stellar network (default: stellar:testnet) + +## Package Dependencies + +- `@x402/core` - Core facilitator +- `@x402/server` - Facilitator HTTP router +- `@x402/evm` - EVM facilitator (V2) +- `@x402/evm/v1` - EVM facilitator (V1) + NETWORKS +- `@x402/svm` - SVM facilitator (V2) +- `@x402/svm/v1` - SVM facilitator (V1) + NETWORKS +- `@x402/stellar` - Stellar facilitator (V2, SEP-41) +- `express` - HTTP server +- `viem` - Ethereum transactions +- `@solana/web3.js` - Solana transactions diff --git a/e2e/coinbase/facilitators/typescript/bazaar.ts b/e2e/coinbase/facilitators/typescript/bazaar.ts new file mode 100644 index 00000000..a09f29fc --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/bazaar.ts @@ -0,0 +1,59 @@ +import type { DiscoveryInfo } from "@x402/extensions/bazaar"; +import type { PaymentRequirements } from "@x402/core/types"; + +export interface DiscoveredResource { + resource: string; + type: "http"; + x402Version: number; + accepts: PaymentRequirements[]; + discoveryInfo?: DiscoveryInfo; + lastUpdated: string; + metadata?: Record; +} + +export class BazaarCatalog { + private discoveredResources = new Map(); + + catalogResource( + resourceUrl: string, + method: string, + x402Version: number, + discoveryInfo: DiscoveryInfo, + paymentRequirements: PaymentRequirements, + ): void { + console.log(`📝 Discovered resource: ${resourceUrl}`); + console.log(` Method: ${method}`); + console.log(` x402 Version: ${x402Version}`); + + this.discoveredResources.set(resourceUrl, { + resource: resourceUrl, + type: "http", + x402Version, + accepts: [paymentRequirements], + discoveryInfo, + lastUpdated: new Date().toISOString(), + metadata: {}, + }); + } + + getResources(limit: number = 100, offset: number = 0) { + const allResources = Array.from(this.discoveredResources.values()); + const total = allResources.length; + const items = allResources.slice(offset, offset + limit); + + return { + x402Version: 1, + items, + pagination: { + limit, + offset, + total, + }, + }; + } + + getCount(): number { + return this.discoveredResources.size; + } +} + diff --git a/e2e/coinbase/facilitators/typescript/build.sh b/e2e/coinbase/facilitators/typescript/build.sh new file mode 100755 index 00000000..f526e2c7 --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# TypeScript build handled by pnpm at root level +# This file is intentionally empty +exit 0 + diff --git a/e2e/coinbase/facilitators/typescript/index.ts b/e2e/coinbase/facilitators/typescript/index.ts new file mode 100644 index 00000000..9e6c6507 --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/index.ts @@ -0,0 +1,351 @@ +/** + * TypeScript Facilitator for E2E Testing (Coinbase SDK version) + * + * This facilitator provides HTTP endpoints for payment verification and settlement + * using the official x402 TypeScript SDK. + */ + +import { x402Facilitator } from "@x402/core/facilitator"; +import { + Network, + PaymentPayload, + PaymentRequirements, + SettleResponse, + VerifyResponse, +} from "@x402/core/types"; +import { toFacilitatorEvmSigner } from "@x402/evm"; +import { ExactEvmScheme } from "@x402/evm/exact/facilitator"; +import { BAZAAR, extractDiscoveryInfo } from "@x402/extensions/bazaar"; +import { + EIP2612_GAS_SPONSORING, + createErc20ApprovalGasSponsoringExtension, +} from "@x402/extensions"; +import crypto from "crypto"; +import dotenv from "dotenv"; +import express from "express"; +import { createWalletClient, http, publicActions, Chain } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { bscTestnet, base } from "viem/chains"; +import { BazaarCatalog } from "./bazaar.js"; + +dotenv.config(); + +// Configuration +const PORT = process.env.PORT || "4022"; +const EVM_NETWORK = (process.env.EVM_NETWORK || "eip155:97") as Network; +const EVM_RPC_URL = process.env.EVM_RPC_URL; + +// Map CAIP-2 network IDs to viem chains +function getEvmChain(network: string): Chain { + switch (network) { + case "eip155:8453": + return base; + case "eip155:97": + default: + return bscTestnet; + } +} + +console.log(`🌐 EVM Network: ${EVM_NETWORK}`); +if (EVM_RPC_URL) console.log(`🌐 EVM RPC URL: ${EVM_RPC_URL}`); + +// Validate required environment variables +if (!process.env.EVM_PRIVATE_KEY) { + console.error("❌ EVM_PRIVATE_KEY environment variable is required"); + process.exit(1); +} + +// Initialize the EVM account from private key +const evmAccount = privateKeyToAccount( + process.env.EVM_PRIVATE_KEY as `0x${string}`, +); +console.info(`EVM Facilitator account: ${evmAccount.address}`); + +// Create a Viem client with both wallet and public capabilities +const evmChain = getEvmChain(EVM_NETWORK); +const viemClient = createWalletClient({ + account: evmAccount, + chain: evmChain, + transport: http(EVM_RPC_URL), +}).extend(publicActions); + +// Initialize the x402 Facilitator with EVM support +const evmSigner = toFacilitatorEvmSigner({ + address: evmAccount.address, + readContract: (args: { + address: `0x${string}`; + abi: readonly unknown[]; + functionName: string; + args?: readonly unknown[]; + }) => + viemClient.readContract({ + ...args, + args: args.args || [], + }), + verifyTypedData: (args: { + address: `0x${string}`; + domain: Record; + types: Record; + primaryType: string; + message: Record; + signature: `0x${string}`; + }) => viemClient.verifyTypedData(args as any), + writeContract: (args: { + address: `0x${string}`; + abi: readonly unknown[]; + functionName: string; + args: readonly unknown[]; + }) => + viemClient.writeContract({ + ...args, + args: args.args || [], + }), + sendTransaction: (args: { to: `0x${string}`; data: `0x${string}` }) => + viemClient.sendTransaction(args), + waitForTransactionReceipt: (args: { hash: `0x${string}` }) => + viemClient.waitForTransactionReceipt(args), + getCode: (args: { address: `0x${string}` }) => viemClient.getCode(args), +}); + +const verifiedPayments = new Map(); +const bazaarCatalog = new BazaarCatalog(); + +function createPaymentHash(paymentPayload: PaymentPayload): string { + return crypto + .createHash("sha256") + .update(JSON.stringify(paymentPayload)) + .digest("hex"); +} + +const facilitator = new x402Facilitator(); + +// Register EVM scheme (v2 only) +facilitator.register(EVM_NETWORK, new ExactEvmScheme(evmSigner)); + +facilitator + .registerExtension(BAZAAR) + .registerExtension(EIP2612_GAS_SPONSORING) + .registerExtension(createErc20ApprovalGasSponsoringExtension(evmSigner, viemClient)) + // Lifecycle hooks for payment tracking and discovery + .onAfterVerify(async (context) => { + // Hook 1: Track verified payment for verify→settle flow validation + if (context.result.isValid) { + const paymentHash = createPaymentHash(context.paymentPayload); + verifiedPayments.set(paymentHash, Date.now()); + + // Hook 2: Extract and catalog bazaar discovery info + const discovered = extractDiscoveryInfo( + context.paymentPayload, + context.requirements, + ); + if (discovered && "method" in discovered && discovered.method) { + bazaarCatalog.catalogResource( + discovered.resourceUrl, + discovered.method, + discovered.x402Version, + discovered.discoveryInfo, + context.requirements, + ); + console.log( + `📦 Discovered resource: ${discovered.method} ${discovered.resourceUrl}`, + ); + } + } + }) + .onBeforeSettle(async (context) => { + // Hook 3: Validate payment was previously verified + const paymentHash = createPaymentHash(context.paymentPayload); + const verificationTimestamp = verifiedPayments.get(paymentHash); + + if (!verificationTimestamp) { + return { + abort: true, + reason: "Payment must be verified before settlement", + }; + } + + // Check verification isn't too old (5 minute timeout) + const age = Date.now() - verificationTimestamp; + if (age > 5 * 60 * 1000) { + verifiedPayments.delete(paymentHash); + return { + abort: true, + reason: "Payment verification expired (must settle within 5 minutes)", + }; + } + }) + .onAfterSettle(async (context) => { + // Hook 4: Clean up verified payment tracking after settlement + const paymentHash = createPaymentHash(context.paymentPayload); + verifiedPayments.delete(paymentHash); + + if (context.result.success) { + console.log(`✅ Settlement completed: ${context.result.transaction}`); + } + }) + .onSettleFailure(async (context) => { + // Hook 5: Clean up on settlement failure too + const paymentHash = createPaymentHash(context.paymentPayload); + verifiedPayments.delete(paymentHash); + + console.error(`❌ Settlement failed: ${context.error.message}`); + }); + +// Initialize Express app +const app = express(); +app.use(express.json()); + +/** + * POST /verify + * Verify a payment against requirements + */ +app.post("/verify", async (req, res) => { + try { + const { paymentPayload, paymentRequirements } = req.body as { + paymentPayload: PaymentPayload; + paymentRequirements: PaymentRequirements; + }; + + if (!paymentPayload || !paymentRequirements) { + return res.status(400).json({ + error: "Missing paymentPayload or paymentRequirements", + }); + } + + const response: VerifyResponse = await facilitator.verify( + paymentPayload, + paymentRequirements, + ); + + res.json(response); + } catch (error) { + console.error("Verify error:", error); + res.status(500).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } +}); + +/** + * POST /settle + * Settle a payment on-chain + */ +app.post("/settle", async (req, res) => { + try { + const { paymentPayload, paymentRequirements } = req.body; + + if (!paymentPayload || !paymentRequirements) { + return res.status(400).json({ + error: "Missing paymentPayload or paymentRequirements", + }); + } + + const response: SettleResponse = await facilitator.settle( + paymentPayload as PaymentPayload, + paymentRequirements as PaymentRequirements, + ); + + res.json(response); + } catch (error) { + console.error("Settle error:", error); + + // Check if this was an abort from hook + if ( + error instanceof Error && + error.message.includes("Settlement aborted:") + ) { + return res.json({ + success: false, + errorReason: error.message.replace("Settlement aborted: ", ""), + network: req.body?.paymentPayload?.network || "unknown", + } as SettleResponse); + } + + res.status(500).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } +}); + +/** + * GET /supported + * Get supported payment kinds and extensions + */ +app.get("/supported", async (req, res) => { + try { + const response = facilitator.getSupported(); + res.json(response); + } catch (error) { + console.error("Supported error:", error); + res.status(500).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } +}); + +app.get("/discovery/resources", (req, res) => { + try { + const limit = parseInt(req.query.limit as string) || 100; + const offset = parseInt(req.query.offset as string) || 0; + + const response = bazaarCatalog.getResources(limit, offset); + res.json(response); + } catch (error) { + console.error("Discovery resources error:", error); + res.status(500).json({ + error: error instanceof Error ? error.message : "Unknown error", + }); + } +}); + +/** + * GET /health + * Health check endpoint + */ +app.get("/health", (req, res) => { + res.json({ + status: "ok", + evmNetwork: EVM_NETWORK, + facilitator: "typescript", + version: "2.0.0", + extensions: [BAZAAR.key], + discoveredResources: bazaarCatalog.getCount(), + }); +}); + +/** + * POST /close + * Graceful shutdown endpoint + */ +app.post("/close", (req, res) => { + res.json({ message: "Facilitator shutting down gracefully" }); + console.log("Received shutdown request"); + + setTimeout(() => { + process.exit(0); + }, 100); +}); + +// Start the server +app.listen(parseInt(PORT), () => { + console.log(` +╔════════════════════════════════════════════════════════╗ +║ x402 TypeScript Facilitator (Coinbase SDK) ║ +╠════════════════════════════════════════════════════════╣ +║ Server: http://localhost:${PORT} ║ +║ EVM Network: ${EVM_NETWORK} ║ +║ EVM Address: ${evmAccount.address} ║ +║ Extensions: bazaar ║ +║ ║ +║ Endpoints: ║ +║ • POST /verify (verify payment) ║ +║ • POST /settle (settle payment) ║ +║ • GET /supported (get supported kinds) ║ +║ • GET /discovery/resources (list discovered) ║ +║ • GET /health (health check) ║ +║ • POST /close (shutdown server) ║ +╚════════════════════════════════════════════════════════╝ + `); + + console.log("Facilitator listening"); +}); diff --git a/e2e/coinbase/facilitators/typescript/install.sh b/e2e/coinbase/facilitators/typescript/install.sh new file mode 100755 index 00000000..220dd872 --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/install.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# TypeScript dependencies handled by pnpm install at root level +# This file is intentionally empty +exit 0 + diff --git a/e2e/coinbase/facilitators/typescript/package.json b/e2e/coinbase/facilitators/typescript/package.json new file mode 100644 index 00000000..e3712d27 --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/package.json @@ -0,0 +1,30 @@ +{ + "name": "@bankofai/x402-e2e-coinbase-facilitator-typescript", + "version": "2.0.0", + "type": "module", + "private": true, + "scripts": { + "start": "tsx index.ts", + "dev": "tsx watch index.ts", + "build": "tsc", + "lint": "eslint .", + "format": "prettier --write .", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@x402/core": "^2.6.0", + "@x402/evm": "^2.6.0", + "@x402/extensions": "^2.6.0", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "viem": "^2.21.54" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^22.10.1", + "eslint": "^9.15.0", + "prettier": "^3.3.3", + "tsx": "^4.19.2", + "typescript": "^5.7.2" + } +} \ No newline at end of file diff --git a/e2e/coinbase/facilitators/typescript/run.sh b/e2e/coinbase/facilitators/typescript/run.sh new file mode 100755 index 00000000..2864b3a7 --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +pnpm start diff --git a/e2e/coinbase/facilitators/typescript/test.config.json b/e2e/coinbase/facilitators/typescript/test.config.json new file mode 100644 index 00000000..a8cab090 --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/test.config.json @@ -0,0 +1,28 @@ +{ + "name": "coinbase-typescript", + "type": "facilitator", + "language": "typescript", + "protocolFamilies": [ + "evm" + ], + "x402Versions": [ + 2 + ], + "extensions": [ + "bazaar", + "eip2612GasSponsoring", + "erc20ApprovalGasSponsoring" + ], + "evm": { + "transferMethods": ["eip3009", "permit2"] + }, + "environment": { + "required": [ + "PORT", + "EVM_PRIVATE_KEY" + ], + "optional": [ + "EVM_NETWORK" + ] + } +} diff --git a/e2e/coinbase/facilitators/typescript/tsconfig.json b/e2e/coinbase/facilitators/typescript/tsconfig.json new file mode 100644 index 00000000..e85a5d7c --- /dev/null +++ b/e2e/coinbase/facilitators/typescript/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": [ + "ES2022" + ], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist" + }, + "include": [ + "*.ts" + ], + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file diff --git a/e2e/coinbase/servers/express/.prettierignore b/e2e/coinbase/servers/express/.prettierignore new file mode 100644 index 00000000..5bd240ba --- /dev/null +++ b/e2e/coinbase/servers/express/.prettierignore @@ -0,0 +1,8 @@ +docs/ +dist/ +node_modules/ +coverage/ +.github/ +src/client +**/**/*.json +*.md \ No newline at end of file diff --git a/e2e/coinbase/servers/express/.prettierrc b/e2e/coinbase/servers/express/.prettierrc new file mode 100644 index 00000000..ffb416b7 --- /dev/null +++ b/e2e/coinbase/servers/express/.prettierrc @@ -0,0 +1,11 @@ +{ + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "avoid", + "printWidth": 100, + "proseWrap": "never" +} diff --git a/e2e/coinbase/servers/express/README.md b/e2e/coinbase/servers/express/README.md new file mode 100644 index 00000000..89623ce0 --- /dev/null +++ b/e2e/coinbase/servers/express/README.md @@ -0,0 +1,194 @@ +# E2E Test Server: Express (TypeScript) + +This server demonstrates and tests the x402 Express.js middleware with EVM, SVM, and optional Stellar payment protection. + +## What It Tests + +### Core Functionality +- ✅ **V2 Protocol** - Modern x402 server middleware +- ✅ **Payment Protection** - Middleware protecting specific routes +- ✅ **Multi-chain Support** - EVM, SVM, and (optional) Stellar payment acceptance +- ✅ **Facilitator Integration** - HTTP communication with facilitator +- ✅ **Extension Support** - Bazaar discovery metadata +- ✅ **Settlement Handling** - Payment verification and confirmation + +### Protected Endpoints +- ✅ `GET /protected` - Requires EVM payment (USDC on Base Sepolia) +- ✅ `GET /protected-svm` - Requires SVM payment (USDC on Solana Devnet) +- ✅ `GET /protected-stellar` - Requires Stellar payment (USDC on Stellar Testnet) + +## What It Demonstrates + +### Server Setup + +```typescript +import express from "express"; +import { x402Middleware } from "@x402/server/express"; +import { ExactEvmServer } from "@x402/evm"; +import { ExactSvmServer } from "@x402/svm"; +import { ExactStellarServer } from "@x402/stellar"; + +const app = express(); + +// Define payment requirements for routes +const routes = { + "GET /protected": { + scheme: "exact", + network: "eip155:84532", + payTo: "0xYourAddress", + price: "$0.001", + extensions: { + bazaar: discoveryMetadata + } + }, + "GET /protected-svm": { + scheme: "exact", + network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", + payTo: "YourSolanaAddress", + price: "$0.001", + extensions: { + bazaar: discoveryMetadata + } + }, + "GET /protected-stellar": { + scheme: "exact", + network: "stellar:testnet", + payTo: "YourStellarAddress", + price: "$0.001", + extensions: { + bazaar: discoveryMetadata + } + } +}; + +// Apply x402 middleware with EVM, SVM, and Stellar servers +app.use(x402Middleware({ + routes, + facilitatorUrl: "http://localhost:4023", + servers: { + "eip155:84532": new ExactEvmServer(), + "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": new ExactSvmServer(), + "stellar:testnet": new ExactStellarServer() + } +})); + +// Define protected endpoints +app.get("/protected", (req, res) => { + res.json({ message: "EVM payment successful!" }); +}); + +app.get("/protected-svm", (req, res) => { + res.json({ message: "SVM payment successful!" }); +}); + +app.get("/protected-stellar", (req, res) => { + res.json({ message: "Stellar payment successful!" }); +}); +``` + +### Key Concepts Shown + +1. **Route-Based Configuration** - Payment requirements per route +2. **Multi-Chain Services** - Different services for different networks +3. **Price Parsing** - Dollar amounts converted to token units +4. **Facilitator Client** - HTTP communication with payment processor +5. **Extension Metadata** - Bazaar discovery info embedded in responses +6. **Automatic 402 Responses** - Middleware generates payment requirements + +## Test Scenarios + +This server is tested with: +- **Clients:** TypeScript Fetch, Axios, Python httpx, requests +- **Facilitators:** TypeScript, Python +- **Payment Types:** EVM (Base Sepolia), SVM (Solana Devnet), Stellar (Stellar Testnet) +- **Protocols:** V2 (primary), V1 (via client negotiation) + +### Request Flow +1. Client makes initial request (no payment) +2. Middleware returns 402 with payment requirements +3. Client creates payment payload +4. Client retries with payment signature +5. Middleware verifies payment via facilitator +6. Middleware returns protected content + +## Running + +```bash +# Via e2e test suite +cd e2e +pnpm test --server=express + +# Direct execution +cd e2e/servers/express +export FACILITATOR_URL="http://localhost:4023" +export EVM_PAYEE_ADDRESS="0x..." +export SVM_PAYEE_ADDRESS="..." +export STELLAR_PAYEE_ADDRESS="G..." # optional +export PORT=4022 +pnpm start +``` + +## Environment Variables + +### Required +- `PORT` - HTTP server port (default: 4022) +- `FACILITATOR_URL` - Facilitator endpoint URL +- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments +- `SVM_PAYEE_ADDRESS` - Solana address to receive payments + +### Optional +- `STELLAR_PAYEE_ADDRESS` - Stellar address to receive payments - enables Stellar endpoint +- `EVM_NETWORK` - EVM network (default: eip155:84532) +- `SVM_NETWORK` - SVM network (default: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1) +- `STELLAR_NETWORK` - Stellar network (default: stellar:testnet) + +## Response Examples + +### 402 Payment Required (No Payment Sent) + +``` +HTTP/1.1 402 Payment Required +PAYMENT-REQUIRED: + +{ + "error": "Payment required", + "x402Version": 2, + "accepts": [...] +} +``` + +### 200 Success (Payment Verified) + +``` +HTTP/1.1 200 OK +PAYMENT-RESPONSE: + +{ + "message": "Protected endpoint accessed successfully" +} +``` + +## Package Dependencies + +- `@x402/server` - Express middleware +- `@x402/evm` - EVM server +- `@x402/svm` - SVM server +- `@x402/stellar` - Stellar server +- `@x402/extensions/bazaar` - Discovery extension +- `express` - HTTP server framework + +## Implementation Highlights + +### Middleware Features +- **Automatic 402 Responses** - Generates payment requirements +- **Payment Verification** - Validates via facilitator +- **Settlement Tracking** - Includes payment response headers +- **Extension Support** - Embeds bazaar metadata +- **Error Handling** - Clear error messages for payment failures + +### Service Integration +- **EVM Server** - Handles Base Sepolia USDC payments +- **SVM Server** - Handles Solana Devnet USDC payments +- **Stellar Server** - Handles Stellar Testnet USDC contract payments (SEP-41) +- **Price Conversion** - "$0.001" → token amounts with decimals +- **Asset Resolution** - Automatic USDC contract/mint lookup diff --git a/e2e/coinbase/servers/express/build.sh b/e2e/coinbase/servers/express/build.sh new file mode 100755 index 00000000..f526e2c7 --- /dev/null +++ b/e2e/coinbase/servers/express/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# TypeScript build handled by pnpm at root level +# This file is intentionally empty +exit 0 + diff --git a/e2e/coinbase/servers/express/eslint.config.js b/e2e/coinbase/servers/express/eslint.config.js new file mode 100644 index 00000000..e2fde7b3 --- /dev/null +++ b/e2e/coinbase/servers/express/eslint.config.js @@ -0,0 +1,73 @@ +import js from "@eslint/js"; +import ts from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; +import prettier from "eslint-plugin-prettier"; +import jsdoc from "eslint-plugin-jsdoc"; +import importPlugin from "eslint-plugin-import"; + +export default [ + { + ignores: ["dist/**", "node_modules/**"], + }, + { + files: ["**/*.ts"], + languageOptions: { + parser: tsParser, + sourceType: "module", + ecmaVersion: 2020, + globals: { + process: "readonly", + __dirname: "readonly", + module: "readonly", + require: "readonly", + Buffer: "readonly", + console: "readonly", + exports: "readonly", + setTimeout: "readonly", + clearTimeout: "readonly", + setInterval: "readonly", + clearInterval: "readonly", + }, + }, + plugins: { + "@typescript-eslint": ts, + prettier: prettier, + jsdoc: jsdoc, + import: importPlugin, + }, + rules: { + ...ts.configs.recommended.rules, + "import/first": "error", + "prettier/prettier": "error", + "@typescript-eslint/member-ordering": "error", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }], + "jsdoc/tag-lines": ["error", "any", { startLines: 1 }], + "jsdoc/check-alignment": "error", + "jsdoc/no-undefined-types": "off", + "jsdoc/check-param-names": "error", + "jsdoc/check-tag-names": "error", + "jsdoc/check-types": "error", + "jsdoc/implements-on-classes": "error", + "jsdoc/require-description": "error", + "jsdoc/require-jsdoc": [ + "error", + { + require: { + FunctionDeclaration: true, + MethodDefinition: true, + ClassDeclaration: true, + ArrowFunctionExpression: false, + FunctionExpression: false, + }, + }, + ], + "jsdoc/require-param": "error", + "jsdoc/require-param-description": "error", + "jsdoc/require-param-type": "off", + "jsdoc/require-returns": "error", + "jsdoc/require-returns-description": "error", + "jsdoc/require-returns-type": "off", + "jsdoc/require-hyphen-before-param-description": ["error", "always"], + }, + }, +]; diff --git a/e2e/coinbase/servers/express/index.ts b/e2e/coinbase/servers/express/index.ts new file mode 100644 index 00000000..b3f5b74e --- /dev/null +++ b/e2e/coinbase/servers/express/index.ts @@ -0,0 +1,242 @@ +import express from "express"; +import { paymentMiddleware } from "@x402/express"; +import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server"; +import { ExactEvmScheme } from "@x402/evm/exact/server"; +import { + bazaarResourceServerExtension, + declareDiscoveryExtension, +} from "@x402/extensions/bazaar"; +import { + declareEip2612GasSponsoringExtension, + declareErc20ApprovalGasSponsoringExtension, +} from "@x402/extensions"; +import dotenv from "dotenv"; + +dotenv.config(); + +/** + * Express E2E Test Server with x402 Payment Middleware (Coinbase SDK version) + * + * This server demonstrates how to integrate x402 payment middleware + * with an Express application for end-to-end testing using official SDK. + */ + +const PORT = process.env.PORT || "4021"; +const EVM_NETWORK = (process.env.EVM_NETWORK || "eip155:97") as `${string}:${string}`; +const EVM_PAYEE_ADDRESS = process.env.EVM_PAYEE_ADDRESS as `0x${string}`; +const facilitatorUrl = process.env.FACILITATOR_URL; + +if (!EVM_PAYEE_ADDRESS) { + console.error("❌ EVM_PAYEE_ADDRESS environment variable is required"); + process.exit(1); +} + +if (!facilitatorUrl) { + console.error("❌ FACILITATOR_URL environment variable is required"); + process.exit(1); +} + +// Initialize Express app +const app = express(); + +// Create HTTP facilitator client +const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl }); + +// Create x402 resource server +const server = new x402ResourceServer(facilitatorClient); + +// Create ExactEvmScheme and register a MoneyParser for the DHLU token +// DHLU supports EIP-3009, EIP-2612, and standard ERC-20. +const evmScheme = new ExactEvmScheme().registerMoneyParser(async (amount, network) => { + if (network === EVM_NETWORK) { + // Map scalar price (e.g. $0.001) to 1000 DHLU (decimals: 6) + return { + amount: Math.floor(amount * 1_000_000).toString(), + asset: "0x375cADdd2cB68cE82e3D9B075D551067a7b4B816", + extra: { name: "DA HULU", version: "1", supportsEip2612: true } + }; + } + return null; +}); + +// Register server schemes +server.register("eip155:*", evmScheme); + +// Register Bazaar discovery extension +server.registerExtension(bazaarResourceServerExtension); + +console.log( + `Facilitator account: ${process.env.EVM_PRIVATE_KEY ? process.env.EVM_PRIVATE_KEY.substring(0, 10) + "..." : "not configured"}`, +); +console.log(`Using remote facilitator at: ${facilitatorUrl}`); + +/** + * Configure x402 payment middleware using builder pattern + */ +app.use( + paymentMiddleware( + { + // Route-specific payment configuration + "GET /protected": { + accepts: { + payTo: EVM_PAYEE_ADDRESS, + scheme: "exact", + network: EVM_NETWORK, + assets: ["DHLU"], + price: "$0.001", + }, + extensions: { + ...declareDiscoveryExtension({ + output: { + example: { + message: "Protected endpoint accessed successfully", + timestamp: "2024-01-01T00:00:00Z", + }, + schema: { + properties: { + message: { type: "string" }, + timestamp: { type: "string" }, + }, + required: ["message", "timestamp"], + }, + }, + }), + }, + }, + // Permit2 endpoint for generic ERC-20 tokens (no EIP-2612, uses raw approve tx) + "GET /protected-permit2-erc20": { + accepts: { + payTo: EVM_PAYEE_ADDRESS, + scheme: "exact", + network: EVM_NETWORK, + assets: ["DHLU"], + price: { + amount: "1000", + asset: "0x375cADdd2cB68cE82e3D9B075D551067a7b4B816", // DHLU (ERC-20 approval path, no name/version) + extra: { + assetTransferMethod: "permit2", + }, + }, + }, + extensions: { + ...declareErc20ApprovalGasSponsoringExtension(), + }, + }, + // Permit2 endpoint - explicitly requires Permit2 flow instead of EIP-3009 + "GET /protected-permit2": { + accepts: { + payTo: EVM_PAYEE_ADDRESS, + scheme: "exact", + network: EVM_NETWORK, + assets: ["DHLU"], + price: { + amount: "1000", + asset: "0x375cADdd2cB68cE82e3D9B075D551067a7b4B816", // DHLU (permit2 + EIP-2612 path) + extra: { + name: "DA HULU", + version: "1", + assetTransferMethod: "permit2", + }, + }, + }, + extensions: { + ...declareDiscoveryExtension({ + output: { + example: { + message: "Permit2 endpoint accessed successfully", + timestamp: "2024-01-01T00:00:00Z", + method: "permit2", + }, + schema: { + properties: { + message: { type: "string" }, + timestamp: { type: "string" }, + method: { type: "string" }, + }, + required: ["message", "timestamp", "method"], + }, + }, + }), + ...declareEip2612GasSponsoringExtension(), + }, + }, + }, + server, // Pass pre-configured server instance + ), +); + +/** + * Protected endpoint - requires payment to access + */ +app.get("/protected", (req, res) => { + res.json({ + message: "Protected endpoint accessed successfully", + timestamp: new Date().toISOString(), + }); +}); + +/** + * Protected Permit2 ERC-20 endpoint - requires payment via Permit2 flow with ERC-20 approval + */ +app.get("/protected-permit2-erc20", (req, res) => { + res.json({ + message: "Permit2 ERC-20 approval endpoint accessed successfully", + timestamp: new Date().toISOString(), + method: "permit2-erc20-approval", + }); +}); + +/** + * Protected Permit2 endpoint - requires payment via Permit2 flow + */ +app.get("/protected-permit2", (req, res) => { + res.json({ + message: "Permit2 endpoint accessed successfully", + timestamp: new Date().toISOString(), + method: "permit2", + }); +}); + +/** + * Health check endpoint - no payment required + */ +app.get("/health", (req, res) => { + res.json({ + status: "ok", + network: EVM_NETWORK, + payee: EVM_PAYEE_ADDRESS, + version: "2.0.0", + }); +}); + +/** + * Shutdown endpoint - used by e2e tests + */ +app.post("/close", (req, res) => { + res.json({ message: "Server shutting down gracefully" }); + console.log("Received shutdown request"); + + setTimeout(() => { + process.exit(0); + }, 100); +}); + +// Start the server +app.listen(parseInt(PORT), () => { + console.log(` +╔════════════════════════════════════════════════════════╗ +║ x402 Express E2E Test Server (Coinbase SDK) ║ +╠════════════════════════════════════════════════════════╣ +║ Server: http://localhost:${PORT} ║ +║ EVM Network: ${EVM_NETWORK} ║ +║ EVM Payee: ${EVM_PAYEE_ADDRESS} ║ +║ ║ +║ Endpoints: ║ +║ • GET /protected (EIP-3009 payment - EVM) ║ +║ • GET /protected-permit2 (Permit2 payment - EVM) ║ +║ • GET /protected-permit2-erc20 (Permit2 + ERC-20 approval) ║ +║ • GET /health (no payment required) ║ +║ • POST /close (shutdown server) ║ +╚════════════════════════════════════════════════════════╝ + `); +}); diff --git a/e2e/coinbase/servers/express/install.sh b/e2e/coinbase/servers/express/install.sh new file mode 100755 index 00000000..220dd872 --- /dev/null +++ b/e2e/coinbase/servers/express/install.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# TypeScript dependencies handled by pnpm install at root level +# This file is intentionally empty +exit 0 + diff --git a/e2e/coinbase/servers/express/package.json b/e2e/coinbase/servers/express/package.json new file mode 100644 index 00000000..2bfa6685 --- /dev/null +++ b/e2e/coinbase/servers/express/package.json @@ -0,0 +1,34 @@ +{ + "name": "@bankofai/x402-coinbase-express-e2e", + "private": true, + "type": "module", + "scripts": { + "dev": "tsx index.ts", + "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"", + "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"", + "lint": "eslint . --ext .ts --fix", + "lint:check": "eslint . --ext .ts" + }, + "dependencies": { + "@x402/core": "^2.6.0", + "@x402/express": "^2.6.0", + "@x402/evm": "^2.6.0", + "@x402/extensions": "^2.6.0", + "dotenv": "^16.6.1", + "express": "^4.18.2" + }, + "devDependencies": { + "@eslint/js": "^9.24.0", + "@types/express": "^5.0.1", + "@typescript-eslint/eslint-plugin": "^8.29.1", + "@typescript-eslint/parser": "^8.29.1", + "eslint": "^9.24.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.6.9", + "eslint-plugin-prettier": "^5.2.6", + "prettier": "3.5.2", + "tsup": "^7.2.0", + "tsx": "^4.7.0", + "typescript": "^5.3.0" + } +} diff --git a/e2e/coinbase/servers/express/run.sh b/e2e/coinbase/servers/express/run.sh new file mode 100755 index 00000000..aa2c67d6 --- /dev/null +++ b/e2e/coinbase/servers/express/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +pnpm dev \ No newline at end of file diff --git a/e2e/coinbase/servers/express/test.config.json b/e2e/coinbase/servers/express/test.config.json new file mode 100644 index 00000000..b1aaf614 --- /dev/null +++ b/e2e/coinbase/servers/express/test.config.json @@ -0,0 +1,50 @@ +{ + "name": "coinbase-express", + "type": "server", + "language": "typescript", + "x402Version": 2, + "extensions": ["bazaar", "eip2612GasSponsoring", "erc20ApprovalGasSponsoring"], + "endpoints": [ + { + "path": "/protected", + "method": "GET", + "description": "Protected endpoint requiring EIP-3009 payment", + "requiresPayment": true, + "protocolFamily": "evm", + "transferMethod": "eip3009" + }, + { + "path": "/protected-permit2", + "method": "GET", + "description": "Protected endpoint requiring Permit2 payment", + "requiresPayment": true, + "protocolFamily": "evm", + "transferMethod": "permit2" + }, + { + "path": "/protected-permit2-erc20", + "method": "GET", + "description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring", + "requiresPayment": true, + "protocolFamily": "evm", + "transferMethod": "permit2", + "extensions": ["erc20ApprovalGasSponsoring"] + }, + { + "path": "/health", + "method": "GET", + "description": "Health check endpoint", + "health": true + }, + { + "path": "/close", + "method": "POST", + "description": "Graceful shutdown endpoint", + "close": true + } + ], + "environment": { + "required": ["PORT", "EVM_PAYEE_ADDRESS", "FACILITATOR_URL"], + "optional": [] + } +} diff --git a/e2e/coinbase/servers/express/tsconfig.json b/e2e/coinbase/servers/express/tsconfig.json new file mode 100644 index 00000000..78f9479b --- /dev/null +++ b/e2e/coinbase/servers/express/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "bundler", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "strict": true, + "resolveJsonModule": true, + "baseUrl": ".", + "types": ["node"] + }, + "include": ["index.ts"] +} diff --git a/e2e/package.json b/e2e/package.json index feaefc68..b47bd45e 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -8,6 +8,7 @@ "test:watch": "tsc --watch", "setup": "./setup.sh", "setup:legacy": "./setup.sh --legacy", + "setup:coinbase": "./setup.sh --coinbase", "install:all": "pnpm install && ./setup.sh", "install:all:legacy": "pnpm install && ./setup.sh --legacy", "clean": "rm -rf dist", diff --git a/e2e/pnpm-lock.yaml b/e2e/pnpm-lock.yaml index 9bc3446e..8469cdbb 100644 --- a/e2e/pnpm-lock.yaml +++ b/e2e/pnpm-lock.yaml @@ -1361,18 +1361,51 @@ importers: specifier: ^2.39.3 version: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) devDependencies: + '@eslint/js': + specifier: ^9.24.0 + version: 9.38.0 '@types/node': specifier: ^22.13.4 version: 22.16.0 + '@typescript-eslint/eslint-plugin': + specifier: ^8.29.1 + version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/parser': + specifier: ^8.29.1 + version: 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3) + eslint: + specifier: ^9.24.0 + version: 9.38.0(jiti@1.21.7) + eslint-plugin-import: + specifier: ^2.31.0 + version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@1.21.7)) + eslint-plugin-jsdoc: + specifier: ^50.6.9 + version: 50.8.0(eslint@9.38.0(jiti@1.21.7)) + eslint-plugin-prettier: + specifier: ^5.2.6 + version: 5.5.4(eslint@9.38.0(jiti@1.21.7))(prettier@3.5.2) + prettier: + specifier: 3.5.2 + version: 3.5.2 tsup: - specifier: ^8.0.0 + specifier: ^8.4.0 version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.1) + tsx: + specifier: ^4.19.2 + version: 4.20.3 typescript: - specifier: ^5.0.0 + specifier: ^5.7.3 version: 5.8.3 + vite: + specifier: ^6.2.6 + version: 6.4.1(@types/node@22.16.0)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.1) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.8.3)(vite@6.4.1(@types/node@22.16.0)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.1)) vitest: - specifier: ^1.0.0 - version: 1.6.1(@types/node@22.16.0)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + specifier: ^3.0.5 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.1) ../typescript/packages/x402-deprecated: dependencies: @@ -1585,6 +1618,104 @@ importers: specifier: ^5.3.0 version: 5.8.3 + coinbase/facilitators/typescript: + dependencies: + '@x402/core': + specifier: ^2.6.0 + version: 2.6.0 + '@x402/evm': + specifier: ^2.6.0 + version: 2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10) + '@x402/extensions': + specifier: ^2.6.0 + version: 2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10) + dotenv: + specifier: ^16.4.5 + version: 16.6.1 + express: + specifier: ^4.19.2 + version: 4.21.2 + viem: + specifier: ^2.21.54 + version: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.12) + devDependencies: + '@types/express': + specifier: ^4.17.21 + version: 4.17.23 + '@types/node': + specifier: ^22.10.1 + version: 22.16.0 + eslint: + specifier: ^9.15.0 + version: 9.38.0(jiti@1.21.7) + prettier: + specifier: ^3.3.3 + version: 3.5.2 + tsx: + specifier: ^4.19.2 + version: 4.20.3 + typescript: + specifier: ^5.7.2 + version: 5.8.3 + + coinbase/servers/express: + dependencies: + '@x402/core': + specifier: ^2.6.0 + version: 2.6.0 + '@x402/evm': + specifier: ^2.6.0 + version: 2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10) + '@x402/express': + specifier: ^2.6.0 + version: 2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(express@4.21.2)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@x402/extensions': + specifier: ^2.6.0 + version: 2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10) + dotenv: + specifier: ^16.6.1 + version: 16.6.1 + express: + specifier: ^4.18.2 + version: 4.21.2 + devDependencies: + '@eslint/js': + specifier: ^9.24.0 + version: 9.38.0 + '@types/express': + specifier: ^5.0.1 + version: 5.0.3 + '@typescript-eslint/eslint-plugin': + specifier: ^8.29.1 + version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/parser': + specifier: ^8.29.1 + version: 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3) + eslint: + specifier: ^9.24.0 + version: 9.38.0(jiti@1.21.7) + eslint-plugin-import: + specifier: ^2.31.0 + version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@1.21.7)) + eslint-plugin-jsdoc: + specifier: ^50.6.9 + version: 50.8.0(eslint@9.38.0(jiti@1.21.7)) + eslint-plugin-prettier: + specifier: ^5.2.6 + version: 5.5.4(eslint@9.38.0(jiti@1.21.7))(prettier@3.5.2) + prettier: + specifier: 3.5.2 + version: 3.5.2 + tsup: + specifier: ^7.2.0 + version: 7.3.0(postcss@8.5.6)(typescript@5.8.3) + tsx: + specifier: ^4.7.0 + version: 4.20.3 + typescript: + specifier: ^5.3.0 + version: 5.8.3 + facilitators/typescript: dependencies: '@aptos-labs/ts-sdk': @@ -5989,6 +6120,24 @@ packages: '@walletconnect/window-metadata@1.0.1': resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} + '@x402/core@2.6.0': + resolution: {integrity: sha512-ISC/JeVss6xlKvor2rp18tJf9K5OQlIDDfZW1VZJQGDI2F4gy+HWxxkFfcQalCsPp4YUlwqh0YOkUxP+LTZWVg==} + + '@x402/evm@2.6.0': + resolution: {integrity: sha512-wPkNHf483gie1Up2sJSvERnW+VIEvMoT1KRXr09wSoSWgelglsJm+ug3gPPXKnT3C6AcmNAKZ12rBnu9Paff7g==} + + '@x402/express@2.6.0': + resolution: {integrity: sha512-Y7If4JD86/z+EpcvlS25REhOutww3O87B4/KBdePC2T+gLIZIjFK0/L8+3wgFINait6276y1FQEMoyNSuM2Gbg==} + peerDependencies: + '@x402/paywall': 2.6.0 + express: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + '@x402/paywall': + optional: true + + '@x402/extensions@2.6.0': + resolution: {integrity: sha512-aLY9xAOOiRLKDN9HT2r9TYUXbD+IsoBces9qPZNVJGO2TBi2rfmbIBc3pcKCtWKn3iTvG2QFr3gpOFdpJRzqww==} + abitype@1.0.6: resolution: {integrity: sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==} peerDependencies: @@ -10923,7 +11072,7 @@ snapshots: jose: 6.1.3 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) zod: 3.25.71 transitivePeerDependencies: - bufferutil @@ -10937,9 +11086,9 @@ snapshots: '@coinbase/onchainkit@0.38.19(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(bufferutil@4.0.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(zod@3.25.71)': dependencies: '@farcaster/frame-sdk': 0.1.12(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) - '@farcaster/miniapp-wagmi-connector': 1.1.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) + '@farcaster/miniapp-wagmi-connector': 1.1.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) '@tanstack/react-query': 5.90.8(react@19.2.1) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) clsx: 2.1.1 graphql: 16.11.0 graphql-request: 6.1.0(graphql@16.11.0) @@ -10947,8 +11096,8 @@ snapshots: react: 19.2.1 react-dom: 19.2.1(react@19.2.1) tailwind-merge: 2.6.0 - viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) - wagmi: 2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + wagmi: 2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11418,11 +11567,11 @@ snapshots: - utf-8-validate - zod - '@farcaster/miniapp-wagmi-connector@1.1.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))': + '@farcaster/miniapp-wagmi-connector@1.1.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))': dependencies: '@farcaster/miniapp-sdk': 0.2.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) - viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) '@farcaster/quick-auth@0.0.6(typescript@5.8.3)': dependencies: @@ -11467,11 +11616,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@gemini-wallet/core@0.2.0(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))': + '@gemini-wallet/core@0.2.0(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))': dependencies: '@metamask/rpc-errors': 7.0.2 eventemitter3: 5.0.1 - viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) transitivePeerDependencies: - supports-color @@ -15358,19 +15507,19 @@ snapshots: - wagmi - zod - '@wagmi/connectors@6.1.0(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71))(zod@3.25.71)': + '@wagmi/connectors@6.1.0(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71))(zod@3.25.71)': dependencies: '@base-org/account': 1.1.1(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(zod@3.25.71) '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(zod@3.25.71) - '@gemini-wallet/core': 0.2.0(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) + '@gemini-wallet/core': 0.2.0(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.19(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71)) - viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + porto: 0.2.19(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71)) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -15435,11 +15584,11 @@ snapshots: - react - use-sync-external-store - '@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))': + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.8.3) - viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) zustand: 5.0.0(@types/react@19.2.2)(react@19.2.1)(use-sync-external-store@1.4.0(react@19.2.1)) optionalDependencies: '@tanstack/query-core': 5.90.8 @@ -16038,6 +16187,50 @@ snapshots: '@walletconnect/window-getters': 1.0.1 tslib: 1.14.1 + '@x402/core@2.6.0': + dependencies: + zod: 3.25.71 + + '@x402/evm@2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@x402/core': 2.6.0 + '@x402/extensions': 2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + zod: 3.25.71 + transitivePeerDependencies: + - bufferutil + - ethers + - typescript + - utf-8-validate + + '@x402/express@2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(express@4.21.2)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@x402/core': 2.6.0 + '@x402/extensions': 2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10) + express: 4.21.2 + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + zod: 3.25.71 + transitivePeerDependencies: + - bufferutil + - ethers + - typescript + - utf-8-validate + + '@x402/extensions@2.6.0(bufferutil@4.0.9)(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@scure/base': 1.2.6 + '@x402/core': 2.6.0 + ajv: 8.17.1 + siwe: 2.3.2(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + tweetnacl: 1.0.3 + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + zod: 3.25.71 + transitivePeerDependencies: + - bufferutil + - ethers + - typescript + - utf-8-validate + abitype@1.0.6(typescript@5.8.3)(zod@3.25.71): optionalDependencies: typescript: 5.8.3 @@ -19196,14 +19389,14 @@ snapshots: - immer - use-sync-external-store - porto@0.2.19(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71)): + porto@0.2.19(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71)): dependencies: '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) hono: 4.10.2 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.8.3) ox: 0.9.12(typescript@5.8.3)(zod@4.1.12) - viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) zod: 4.1.12 zustand: 5.0.8(@types/react@19.2.2)(react@19.2.1)(use-sync-external-store@1.4.0(react@19.2.1)) optionalDependencies: @@ -20631,24 +20824,6 @@ snapshots: - supports-color - terser - vite-node@1.6.1(@types/node@22.16.0): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - pathe: 1.1.2 - picocolors: 1.1.1 - vite: 5.4.21(@types/node@22.16.0) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - vite-node@3.2.4(@types/node@22.16.0)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.1): dependencies: cac: 6.7.14 @@ -20690,15 +20865,6 @@ snapshots: '@types/node': 20.19.4 fsevents: 2.3.3 - vite@5.4.21(@types/node@22.16.0): - dependencies: - esbuild: 0.21.5 - postcss: 8.5.6 - rollup: 4.52.5 - optionalDependencies: - '@types/node': 22.16.0 - fsevents: 2.3.3 - vite@6.4.1(@types/node@22.16.0)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.1): dependencies: esbuild: 0.25.5 @@ -20749,41 +20915,6 @@ snapshots: - supports-color - terser - vitest@1.6.1(@types/node@22.16.0)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): - dependencies: - '@vitest/expect': 1.6.1 - '@vitest/runner': 1.6.1 - '@vitest/snapshot': 1.6.1 - '@vitest/spy': 1.6.1 - '@vitest/utils': 1.6.1 - acorn-walk: 8.3.5 - chai: 4.5.0 - debug: 4.4.3 - execa: 8.0.1 - local-pkg: 0.5.1 - magic-string: 0.30.19 - pathe: 1.1.2 - picocolors: 1.1.1 - std-env: 3.10.0 - strip-literal: 2.1.1 - tinybench: 2.9.0 - tinypool: 0.8.4 - vite: 5.4.21(@types/node@22.16.0) - vite-node: 1.6.1(@types/node@22.16.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 22.16.0 - jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.16.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.1): dependencies: '@types/chai': 5.2.3 @@ -20909,14 +21040,14 @@ snapshots: - utf-8-validate - zod - wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71): + wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71): dependencies: '@tanstack/react-query': 5.90.8(react@19.2.1) - '@wagmi/connectors': 6.1.0(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71))(zod@3.25.71) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) + '@wagmi/connectors': 6.1.0(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)))(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(wagmi@2.18.2(@tanstack/query-core@5.90.8)(@tanstack/react-query@5.90.8(react@19.2.1))(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))(zod@3.25.71))(zod@3.25.71) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.8)(@types/react@19.2.2)(react@19.2.1)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)) react: 19.2.1 use-sync-external-store: 1.4.0(react@19.2.1) - viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) + viem: 2.47.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: diff --git a/e2e/pnpm-workspace.yaml b/e2e/pnpm-workspace.yaml index 190cf9d2..144a752f 100644 --- a/e2e/pnpm-workspace.yaml +++ b/e2e/pnpm-workspace.yaml @@ -7,6 +7,8 @@ packages: - "clients/external/*" - "legacy/servers/*" - "legacy/clients/*" + - "coinbase/servers/*" + - "coinbase/facilitators/*" - "../typescript/packages/*" - "../typescript/packages/http/*" - "../typescript/packages/mechanisms/*" diff --git a/e2e/setup.sh b/e2e/setup.sh index 05233b9d..1f04a3f3 100755 --- a/e2e/setup.sh +++ b/e2e/setup.sh @@ -3,6 +3,7 @@ set -e # Parse command line arguments INCLUDE_LEGACY=false +INCLUDE_COINBASE=false VERBOSE=false for arg in "$@"; do @@ -11,6 +12,10 @@ for arg in "$@"; do INCLUDE_LEGACY=true shift ;; + --coinbase) + INCLUDE_COINBASE=true + shift + ;; -v|--verbose) VERBOSE=true shift @@ -22,12 +27,14 @@ for arg in "$@"; do echo "" echo "Options:" echo " --legacy Include legacy (v1) implementations" + echo " --coinbase Include coinbase implementations" echo " -v, --verbose Show detailed output" echo " -h, --help Show this help message" echo "" echo "Examples:" echo " ./setup.sh # Setup v2 implementations only" echo " ./setup.sh --legacy # Setup v2 and legacy" + echo " ./setup.sh --coinbase # Setup v2 and coinbase" echo " ./setup.sh --legacy -v # Setup with verbose output" exit 0 ;; @@ -43,6 +50,11 @@ if [ "$INCLUDE_LEGACY" = true ]; then echo "" fi +if [ "$INCLUDE_COINBASE" = true ]; then + echo "📚 Including coinbase implementations" + echo "" +fi + # Track results TOTAL=0 SUCCESS=0 @@ -159,6 +171,13 @@ if [ "$INCLUDE_LEGACY" = true ]; then process_directory "legacy/facilitators" "facilitator" fi +# Setup coinbase if requested +if [ "$INCLUDE_COINBASE" = true ]; then + process_directory "coinbase/servers" "server" + process_directory "coinbase/clients" "client" + process_directory "coinbase/facilitators" "facilitator" +fi + # Print summary echo "" echo "" diff --git a/e2e/src/discovery.ts b/e2e/src/discovery.ts index 3e21f682..2950367b 100644 --- a/e2e/src/discovery.ts +++ b/e2e/src/discovery.ts @@ -16,10 +16,12 @@ import { export class TestDiscovery { private baseDir: string; private includeLegacy: boolean; + private includeCoinbase: boolean; - constructor(baseDir: string = '.', includeLegacy: boolean = false) { + constructor(baseDir: string = '.', includeLegacy: boolean = false, includeCoinbase: boolean = true) { this.baseDir = baseDir; this.includeLegacy = includeLegacy; + this.includeCoinbase = includeCoinbase; } /** @@ -42,6 +44,14 @@ export class TestDiscovery { } } + // Discover servers from coinbase directory if flag is set + if (this.includeCoinbase) { + const coinbaseServersDir = join(this.baseDir, 'coinbase', 'servers'); + if (existsSync(coinbaseServersDir)) { + this.discoverServersInDirectory(coinbaseServersDir, servers, 'coinbase-'); + } + } + return servers; } @@ -97,6 +107,14 @@ export class TestDiscovery { } } + // Discover clients from coinbase directory if flag is set + if (this.includeCoinbase) { + const coinbaseClientsDir = join(this.baseDir, 'coinbase', 'clients'); + if (existsSync(coinbaseClientsDir)) { + this.discoverClientsInDirectory(coinbaseClientsDir, clients, 'coinbase-'); + } + } + return clients; } @@ -120,6 +138,14 @@ export class TestDiscovery { } } + // Discover facilitators from coinbase directory if flag is set + if (this.includeCoinbase) { + const coinbaseFacilitatorsDir = join(this.baseDir, 'coinbase', 'facilitators'); + if (existsSync(coinbaseFacilitatorsDir)) { + this.discoverFacilitatorsInDirectory(coinbaseFacilitatorsDir, facilitators, 'coinbase-'); + } + } + return facilitators; } @@ -330,6 +356,9 @@ export class TestDiscovery { if (this.includeLegacy) { log('🔄 Legacy mode enabled - including legacy implementations'); } + if (this.includeCoinbase) { + log('🔄 Coinbase mode enabled - including coinbase implementations'); + } log(`📡 Servers found: ${servers.length}`); servers.forEach(server => { const paidEndpoints = server.config.endpoints?.filter(e => e.requiresPayment).length || 0;