@warppay402/server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @warppay402/server ⚡
2
+
3
+ > Instant x402 V2 monetization SDK and self-hosted infrastructure for Model Context Protocol (MCP) AI tools, Hono HTTP APIs, Cloudflare Monetization Gateway, and Base.
4
+
5
+ `@warppay402/server` allows developers to monetize any MCP tool or HTTP API route in a few lines of code. It automatically generates standard x402 V2 HTTP payment challenges, verifies gasless EIP-712 signatures, enforces platform fee splits, protects against prompt injection threats, and prevents signature replay attacks.
6
+
7
+ Official Site: [https://www.warppay402.com](https://www.warppay402.com)
8
+
9
+ ---
10
+
11
+ ## Features
12
+
13
+ - **x402 V2 Compliant:** Standardized payment challenge headers (`PAYMENT-REQUIRED`, `PAYMENT-SIGNATURE`, `PAYMENT-RESPONSE`).
14
+ - **Base Network Native:** Uses USDC on Base mainnet (`eip155:8453`) by default.
15
+ - **Gasless Off-Chain Signing:** Clients sign `TransferWithAuthorization` payloads without paying gas fees.
16
+ - **Automated Platform Fee Split:** Configurable fee split in basis points (e.g. 50 BPS = 0.5%) routed directly during facilitator settlement.
17
+ - **x402-Guard Security:** Pre-flight middleware protecting endpoints against prompt injection attacks, payload buffer overruns, and high-velocity traffic spikes.
18
+ - **MCP Native Decorator:** Seamlessly wraps tools written for the Model Context Protocol.
19
+ - **Replay Protection:** Includes built-in `MemoryNonceStore` and distributed `RedisNonceStore` drivers to prevent double-spending or signature reuse.
20
+ - **Edge Compatible:** Zero Node.js-only API dependencies; runs seamlessly on Cloudflare Workers, Vercel Edge, Node.js (18+), and Deno.
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @warppay402/server
28
+ ```
29
+ ## Usage Case 1: Monetizing Hono HTTP APIs
30
+ ```typescript
31
+ import { Hono } from "hono";
32
+ import { monetize } from "@warppay402/server";
33
+
34
+ const app = new Hono();
35
+
36
+ app.use(
37
+ "/api/data",
38
+ monetize({
39
+ price: "0.01", // $0.01 USDC
40
+ payTo: "0xYourMerchantWalletAddress",
41
+ platformWallet: "0xYourPlatformTreasuryWallet",
42
+ platformFeeBps: 50, // 0.5% cut
43
+ guard: true // Enables x402-guard security scanning
44
+ })
45
+ );
46
+
47
+ app.get("/api/data", (c) => c.json({ status: "success", content: "Monetized Payload" }));
48
+
49
+ export default app;
50
+ ```
51
+ ## Usage Case 2: Monetizing MCP (Model Context Protocol) Tools
52
+ ```typescript
53
+ import { createMonetizedMCPTool } from "@warppay402/server";
54
+
55
+ const originalTool = {
56
+ name: "web_scraper",
57
+ description: "Extract clean Markdown content from URLs",
58
+ inputSchema: { type: "object", properties: { url: { type: "string" } } },
59
+ handler: async ({ url }: { url: string }) => {
60
+ return { markdown: "# Cleaned Markdown Content" };
61
+ }
62
+ };
63
+
64
+ export const monetizedTool = createMonetizedMCPTool(originalTool, {
65
+ price: "0.02", // $0.02 USDC
66
+ payTo: "0xYourMerchantWalletAddress",
67
+ platformWallet: "0xYourPlatformTreasuryWallet",
68
+ platformFeeBps: 50
69
+ });
70
+ ```
71
+
72
+ ## Usage Case 3: Distributed Nonce Locking with Redis / Upstash
73
+ ```typescript
74
+ import { Redis } from "@upstash/redis";
75
+ import { monetize, RedisNonceStore } from "@warppay402/server";
76
+
77
+ const redis = new Redis({
78
+ url: process.env.UPSTASH_REDIS_REST_URL!,
79
+ token: process.env.UPSTASH_REDIS_REST_TOKEN!
80
+ });
81
+
82
+ app.use(
83
+ "/api/v1/compute",
84
+ monetize({
85
+ price: "0.10",
86
+ payTo: "0xYourMerchantWalletAddress",
87
+ platformWallet: "0xYourPlatformTreasuryWallet",
88
+ nonceStore: new RedisNonceStore(redis),
89
+ nonceTtlSeconds: 300
90
+ })
91
+ );
92
+ ```
93
+ ## License: MIT © WarpPay402
package/dist/demo.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/demo.js ADDED
@@ -0,0 +1,53 @@
1
+ import { Hono } from "hono";
2
+ import { monetize, createMonetizedMCPTool } from "./index";
3
+ // ============================================================================
4
+ // 1. Test Web API Middleware
5
+ // ============================================================================
6
+ const app = new Hono();
7
+ app.use("/api/weather", monetize({
8
+ price: "0.05",
9
+ payTo: "0x1111111111111111111111111111111111111111",
10
+ platformFeeBps: 50
11
+ }));
12
+ app.get("/api/weather", (c) => {
13
+ return c.json({ city: "Austin", temperature: "78F", condition: "Sunny" });
14
+ });
15
+ // ============================================================================
16
+ // 2. Test MCP Tool Decorator
17
+ // ============================================================================
18
+ const monetizedSearchTool = createMonetizedMCPTool({
19
+ name: "web_search",
20
+ description: "Perform deep web searches for AI agents",
21
+ inputSchema: { type: "object" },
22
+ handler: async (args) => {
23
+ return { status: "success", data: `Search results for: ${args.query}` };
24
+ }
25
+ }, {
26
+ price: "0.10",
27
+ payTo: "0x1111111111111111111111111111111111111111",
28
+ platformFeeBps: 50
29
+ });
30
+ // ============================================================================
31
+ // 3. Execution Simulation
32
+ // ============================================================================
33
+ async function runLocalVerification() {
34
+ console.log("==================================================");
35
+ console.log("1. TESTING HTTP API MIDDLEWARE WITHOUT PAYMENT");
36
+ console.log("==================================================");
37
+ const unauthenticatedRes = await app.request("http://localhost/api/weather");
38
+ console.log("HTTP Status Code:", unauthenticatedRes.status, "(Expected: 402)");
39
+ console.log("PAYMENT-REQUIRED Header:", unauthenticatedRes.headers.get("PAYMENT-REQUIRED") ? "RECEIVED ✓" : "MISSING ✗");
40
+ const challengeBody = await unauthenticatedRes.json();
41
+ console.log("Challenge Payload:", JSON.stringify(challengeBody, null, 2));
42
+ console.log("\n==================================================");
43
+ console.log("2. TESTING MCP TOOL DECORATOR WITHOUT PAYMENT");
44
+ console.log("==================================================");
45
+ try {
46
+ await monetizedSearchTool.handler({ query: "Base network x402" }, {});
47
+ }
48
+ catch (err) {
49
+ console.log("Caught Expected MCP 402 Error ✓");
50
+ console.log("MCP Error Payload:", JSON.parse(err.message));
51
+ }
52
+ }
53
+ runLocalVerification();
@@ -0,0 +1,4 @@
1
+ import "dotenv/config";
2
+ import { Hono } from "hono";
3
+ declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ export default app;
@@ -0,0 +1,25 @@
1
+ import "dotenv/config";
2
+ import { serve } from "@hono/node-server";
3
+ import { monetize } from "../index.js";
4
+ import { Hono } from "hono";
5
+ const app = new Hono();
6
+ // Define the developer's wallet address (where 99.5% of funds go)
7
+ const developerWallet = "0x2bd4e0ea72e21155ec41f8613eafd433193c4d8b";
8
+ app.use("/api/weather", monetize({
9
+ price: "0.01",
10
+ payTo: developerWallet,
11
+ platformWallet: "0x2bd4e0ea72e21155ec41f8613eafd433193c4d8b",
12
+ platformFeeBps: 50,
13
+ facilitatorUrl: "https://warppay402.com"
14
+ }));
15
+ app.get("/api/weather", (c) => {
16
+ return c.json({
17
+ status: "success",
18
+ timestamp: new Date().toISOString(),
19
+ data: { city: "Austin", temperature: "78°F", condition: "Sunny" }
20
+ });
21
+ });
22
+ serve({ fetch: app.fetch, port: 3000 }, (info) => {
23
+ console.log(`[API Server] Running on http://localhost:${info.port}`);
24
+ });
25
+ export default app;
@@ -0,0 +1 @@
1
+ import "dotenv/config";
@@ -0,0 +1,118 @@
1
+ import "dotenv/config";
2
+ import { serve } from "@hono/node-server";
3
+ import { Hono } from "hono";
4
+ import { createWalletClient, http, parseAbi, verifyTypedData } from "viem";
5
+ import { privateKeyToAccount } from "viem/accounts";
6
+ import { base } from "viem/chains";
7
+ const app = new Hono();
8
+ // Log every incoming request URL to debug the exact path hit by the SDK
9
+ app.use("*", async (c, next) => {
10
+ console.log(`[Facilitator Debug] Incoming ${c.req.method} request to path: ${c.req.path}`);
11
+ await next();
12
+ });
13
+ const FACILITATOR_PRIVATE_KEY = process.env.FACILITATOR_PRIVATE_KEY;
14
+ if (!FACILITATOR_PRIVATE_KEY) {
15
+ console.error("Please set FACILITATOR_PRIVATE_KEY with a Base gas-holding key.");
16
+ process.exit(1);
17
+ }
18
+ const account = privateKeyToAccount(FACILITATOR_PRIVATE_KEY);
19
+ const walletClient = createWalletClient({
20
+ account,
21
+ chain: base,
22
+ transport: http(process.env.RPC_URL || "https://mainnet.base.org"),
23
+ });
24
+ const usdcAbi = parseAbi([
25
+ "function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external",
26
+ ]);
27
+ // Shared handler logic for signature verification and on-chain broadcasting
28
+ const handleSettle = async (c) => {
29
+ try {
30
+ const body = await c.req.json();
31
+ // Extract authorization & signature regardless of payload nesting depth
32
+ const authorization = body.authorization ||
33
+ body.payload?.authorization ||
34
+ body.paymentPayload?.authorization ||
35
+ body.paymentPayload?.payload?.authorization ||
36
+ body.payload?.payload?.authorization;
37
+ const signature = body.signature ||
38
+ body.payload?.signature ||
39
+ body.paymentPayload?.signature ||
40
+ body.paymentPayload?.payload?.signature ||
41
+ body.payload?.payload?.signature;
42
+ if (!signature || !authorization) {
43
+ console.log("[Facilitator Debug] Received Body:", JSON.stringify(body, null, 2));
44
+ return c.json({ success: false, error: "Missing signature or authorization payload" }, 400);
45
+ }
46
+ // 1. Verify EIP-712 Signature Off-Chain
47
+ const domain = {
48
+ name: "USD Coin",
49
+ version: "2",
50
+ chainId: 8453,
51
+ verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
52
+ };
53
+ const types = {
54
+ TransferWithAuthorization: [
55
+ { name: "from", type: "address" },
56
+ { name: "to", type: "address" },
57
+ { name: "value", type: "uint256" },
58
+ { name: "validAfter", type: "uint256" },
59
+ { name: "validBefore", type: "uint256" },
60
+ { name: "nonce", type: "bytes32" },
61
+ ],
62
+ };
63
+ const isValid = await verifyTypedData({
64
+ address: authorization.from,
65
+ domain,
66
+ types,
67
+ primaryType: "TransferWithAuthorization",
68
+ message: {
69
+ from: authorization.from,
70
+ to: authorization.to,
71
+ value: BigInt(authorization.value),
72
+ validAfter: BigInt(authorization.validAfter),
73
+ validBefore: BigInt(authorization.validBefore),
74
+ nonce: authorization.nonce,
75
+ },
76
+ signature,
77
+ });
78
+ if (!isValid) {
79
+ return c.json({ success: false, error: "Invalid EIP-712 signature" }, 402);
80
+ }
81
+ // 2. Extract v, r, s
82
+ const r = `0x${signature.slice(2, 66)}`;
83
+ const s = `0x${signature.slice(66, 130)}`;
84
+ const v = parseInt(signature.slice(130, 132), 16);
85
+ // 3. Broadcast to Base Mainnet using transferWithAuthorization
86
+ const txHash = await walletClient.writeContract({
87
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
88
+ abi: usdcAbi,
89
+ functionName: "transferWithAuthorization",
90
+ args: [
91
+ authorization.from,
92
+ authorization.to,
93
+ BigInt(authorization.value),
94
+ BigInt(authorization.validAfter),
95
+ BigInt(authorization.validBefore),
96
+ authorization.nonce,
97
+ v,
98
+ r,
99
+ s,
100
+ ],
101
+ });
102
+ return c.json({
103
+ success: true,
104
+ txHash,
105
+ network: "eip155:8453",
106
+ });
107
+ }
108
+ catch (err) {
109
+ return c.json({ success: false, error: err.message }, 500);
110
+ }
111
+ };
112
+ // Listen on all standard routes to prevent 404 mismatch
113
+ app.post("/", handleSettle);
114
+ app.post("/settle", handleSettle);
115
+ app.post("/verify", handleSettle);
116
+ serve({ fetch: app.fetch, port: 3001 }, (info) => {
117
+ console.log(`[Self-Hosted Facilitator] Running on http://localhost:${info.port}`);
118
+ });
@@ -0,0 +1,29 @@
1
+ import { Context, Next } from "hono";
2
+ export interface GuardOptions {
3
+ /** Maximum requests allowed within the window per IP/Wallet */
4
+ rateLimitMax?: number;
5
+ /** Rate limit window in milliseconds (default: 60000 / 1 minute) */
6
+ rateLimitWindowMs?: number;
7
+ /** Custom forbidden keywords/patterns for prompt injection scanning */
8
+ customBlockedPatterns?: RegExp[];
9
+ /** Maximum length allowed for input strings (prevents buffer overload) */
10
+ maxPayloadLength?: number;
11
+ /** Action on detected threat: 'block' (422) or 'log-only' */
12
+ mode?: "block" | "log-only";
13
+ }
14
+ export interface GuardAuditResult {
15
+ passed: boolean;
16
+ reason?: string;
17
+ threatType?: "PROMPT_INJECTION" | "VELOCITY_EXCEEDED" | "PAYLOAD_TOO_LARGE";
18
+ }
19
+ /**
20
+ * Scans string inputs recursively within a JSON request body
21
+ */
22
+ export declare function scanPayloadForInjection(payload: unknown, customPatterns?: RegExp[]): {
23
+ safe: boolean;
24
+ matchedPattern?: string;
25
+ };
26
+ /**
27
+ * Pre-flight Hono Middleware for x402-guard
28
+ */
29
+ export declare function x402Guard(options?: GuardOptions): (c: Context, next: Next) => Promise<Response | void>;
package/dist/guard.js ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Built-in heuristic signatures for common indirect prompt injection attacks
3
+ */
4
+ const DEFAULT_INJECTION_PATTERNS = [
5
+ /ignore\s+(all\s+)?previous\s+instructions/i,
6
+ /override\s+(system\s+)?prompt/i,
7
+ /system:\s*you\s+must/i,
8
+ /bypass\s+(payment|verification|guard|x402)/i,
9
+ /pay\s+(maximum|all|unlimited)\s+(usdc|funds|balance)/i,
10
+ /repeat\s+this\s+request\s+infinitely/i,
11
+ /<script[\s\S]*?>[\s\S]*?<\/script>/i,
12
+ /eval\(.*?\)/i,
13
+ ];
14
+ /**
15
+ * In-memory sliding window store for velocity protection
16
+ */
17
+ class VelocityStore {
18
+ requests = new Map();
19
+ isExceeded(key, limit, windowMs) {
20
+ const now = Date.now();
21
+ const timestamps = this.requests.get(key) || [];
22
+ const validTimestamps = timestamps.filter((time) => now - time < windowMs);
23
+ if (validTimestamps.length >= limit) {
24
+ return true;
25
+ }
26
+ validTimestamps.push(now);
27
+ this.requests.set(key, validTimestamps);
28
+ return false;
29
+ }
30
+ }
31
+ const velocityStore = new VelocityStore();
32
+ /**
33
+ * Scans string inputs recursively within a JSON request body
34
+ */
35
+ export function scanPayloadForInjection(payload, customPatterns = []) {
36
+ const patterns = [...DEFAULT_INJECTION_PATTERNS, ...customPatterns];
37
+ const inspectValue = (val) => {
38
+ if (typeof val === "string") {
39
+ for (const pattern of patterns) {
40
+ if (pattern.test(val)) {
41
+ return { safe: false, matchedPattern: pattern.toString() };
42
+ }
43
+ }
44
+ }
45
+ else if (typeof val === "object" && val !== null) {
46
+ for (const key of Object.keys(val)) {
47
+ const result = inspectValue(val[key]);
48
+ if (!result.safe)
49
+ return result;
50
+ }
51
+ }
52
+ return { safe: true };
53
+ };
54
+ return inspectValue(payload);
55
+ }
56
+ /**
57
+ * Pre-flight Hono Middleware for x402-guard
58
+ */
59
+ export function x402Guard(options = {}) {
60
+ const { rateLimitMax = 20, rateLimitWindowMs = 60000, customBlockedPatterns = [], maxPayloadLength = 50000, mode = "block", } = options;
61
+ return async (c, next) => {
62
+ const clientKey = c.req.header("x-forwarded-for") || "unknown-client";
63
+ let body = {};
64
+ try {
65
+ body = await c.req.json();
66
+ }
67
+ catch {
68
+ // Body parsing optional if request has no body
69
+ }
70
+ // 1. Payload Size Check
71
+ const rawBody = JSON.stringify(body || {});
72
+ if (rawBody.length > maxPayloadLength) {
73
+ if (mode === "block") {
74
+ c.header("X-Guard-Blocked", "PAYLOAD_TOO_LARGE");
75
+ return c.json({
76
+ error: "Payload Too Large",
77
+ message: `Request body exceeds maximum safe size of ${maxPayloadLength} characters.`,
78
+ code: "PAYLOAD_TOO_LARGE",
79
+ }, 413);
80
+ }
81
+ }
82
+ // 2. Velocity Check
83
+ if (velocityStore.isExceeded(clientKey, rateLimitMax, rateLimitWindowMs)) {
84
+ c.header("X-Guard-Blocked", "VELOCITY_EXCEEDED");
85
+ if (mode === "block") {
86
+ return c.json({
87
+ error: "Rate Limit Exceeded",
88
+ message: "Anomalous transaction velocity detected. Request blocked before x402 payment.",
89
+ code: "VELOCITY_EXCEEDED",
90
+ }, 429);
91
+ }
92
+ }
93
+ // 3. Prompt Injection Pre-Flight Scan
94
+ const scanResult = scanPayloadForInjection(body, customBlockedPatterns);
95
+ if (!scanResult.safe) {
96
+ c.header("X-Guard-Blocked", "PROMPT_INJECTION");
97
+ if (mode === "block") {
98
+ return c.json({
99
+ error: "Security Violation",
100
+ message: "Potential prompt injection or payload manipulation detected in request body.",
101
+ code: "PROMPT_INJECTION_DETECTED",
102
+ }, 422);
103
+ }
104
+ }
105
+ c.header("X-Guard-Inspected", "true");
106
+ await next();
107
+ };
108
+ }
@@ -0,0 +1,4 @@
1
+ export { monetize, MemoryNonceStore, RedisNonceStore, type MonetizeOptions, type NonceStore } from "./monetize";
2
+ export { createMonetizedMCPTool, type MCPToolDefinition } from "./mcpWrapper";
3
+ export { x402Guard, scanPayloadForInjection } from "./guard.js";
4
+ export type { GuardOptions } from "./guard.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { monetize, MemoryNonceStore, RedisNonceStore } from "./monetize";
2
+ export { createMonetizedMCPTool } from "./mcpWrapper";
3
+ export { x402Guard, scanPayloadForInjection } from "./guard.js";
@@ -0,0 +1,13 @@
1
+ import { MonetizeOptions } from "./monetize";
2
+ export interface MCPToolDefinition {
3
+ name: string;
4
+ description: string;
5
+ inputSchema: Record<string, any>;
6
+ handler: (args: any, extraContext?: any) => Promise<any>;
7
+ }
8
+ export declare function createMonetizedMCPTool(tool: MCPToolDefinition, options: MonetizeOptions): {
9
+ name: string;
10
+ inputSchema: Record<string, any>;
11
+ description: string;
12
+ handler(args: any, context: any): Promise<any>;
13
+ };
@@ -0,0 +1,169 @@
1
+ import { MemoryNonceStore } from "./monetize";
2
+ const DEFAULT_USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
3
+ const DEFAULT_NETWORK = "eip155:8453";
4
+ const DEFAULT_FACILITATOR = "https://warppay402.com";
5
+ const DEFAULT_PLATFORM_WALLET = "0x2bd4e0ea72e21155ec41f8613eafd433193c4d8b";
6
+ const defaultMemoryStore = new MemoryNonceStore();
7
+ function parseTokenUnits(priceStr) {
8
+ const normalized = String(priceStr).trim();
9
+ if (!normalized || isNaN(Number(normalized)) || Number(normalized) <= 0) {
10
+ throw new Error(`[Monetize] Invalid price option: "${priceStr}". Must be a positive decimal number.`);
11
+ }
12
+ const [whole, fraction = ""] = normalized.split(".");
13
+ const paddedFraction = fraction.padEnd(6, "0").slice(0, 6);
14
+ return BigInt(whole + paddedFraction);
15
+ }
16
+ async function hashSignature(signature) {
17
+ const encoder = new TextEncoder();
18
+ const data = encoder.encode(signature);
19
+ const cryptoObj = globalThis.crypto;
20
+ if (!cryptoObj?.subtle) {
21
+ throw new Error("[Monetize] Web Crypto API (crypto.subtle) is unavailable in this environment.");
22
+ }
23
+ const hashBuffer = await cryptoObj.subtle.digest("SHA-256", data);
24
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
25
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
26
+ }
27
+ export function createMonetizedMCPTool(tool, options) {
28
+ // Validate EVM wallet addresses
29
+ if (!options.payTo || !options.payTo.startsWith("0x") || options.payTo.length !== 42) {
30
+ throw new Error("[Monetize] A valid 42-character EVM wallet address is required for 'payTo'.");
31
+ }
32
+ const asset = options.asset || DEFAULT_USDC_BASE;
33
+ const network = options.network || DEFAULT_NETWORK;
34
+ const facilitatorUrl = options.facilitatorUrl || DEFAULT_FACILITATOR;
35
+ const platformFeeBps = options.platformFeeBps ?? 50;
36
+ const platformWallet = options.platformWallet || DEFAULT_PLATFORM_WALLET;
37
+ const nonceStore = options.nonceStore || defaultMemoryStore;
38
+ const nonceTtlSeconds = options.nonceTtlSeconds ?? 300;
39
+ const timeoutMs = options.timeoutMs ?? 8000;
40
+ if (platformFeeBps > 0 && (!platformWallet || !platformWallet.startsWith("0x") || platformWallet.length !== 42)) {
41
+ throw new Error("[Monetize] A valid EVM 'platformWallet' address is required when platformFeeBps > 0.");
42
+ }
43
+ const baseUnits = parseTokenUnits(options.price);
44
+ const platformFeeUnits = (baseUnits * BigInt(platformFeeBps)) / BigInt(10_000);
45
+ const merchantUnits = baseUnits - platformFeeUnits;
46
+ return {
47
+ ...tool,
48
+ description: `${tool.description} [Requires Payment: $${options.price} USDC]`,
49
+ async handler(args, context) {
50
+ // Extract payment headers passed via MCP JSON-RPC request context
51
+ const headers = context?.requestHeaders || context?.headers || {};
52
+ const paymentSignature = headers["payment-signature"] || headers["PAYMENT-SIGNATURE"];
53
+ const targetUrl = context?.requestUrl || `mcp://tools/${tool.name}`;
54
+ // STEP 1: If no signature is provided, throw standard 402 challenge
55
+ if (!paymentSignature) {
56
+ const challengePayload = {
57
+ code: 402,
58
+ message: "HTTP 402 Payment Required",
59
+ x402Challenge: {
60
+ x402Version: 2,
61
+ resource: {
62
+ url: targetUrl,
63
+ description: options.description || tool.description,
64
+ mimeType: "application/json"
65
+ },
66
+ accepts: [
67
+ {
68
+ scheme: "exact",
69
+ network,
70
+ amount: merchantUnits.toString(),
71
+ asset,
72
+ payTo: options.payTo,
73
+ maxTimeoutSeconds: nonceTtlSeconds,
74
+ extra: {
75
+ platformFee: platformFeeUnits.toString(),
76
+ platformWallet: platformWallet
77
+ }
78
+ }
79
+ ]
80
+ }
81
+ };
82
+ throw new Error(JSON.stringify(challengePayload));
83
+ }
84
+ // STEP 2: Format Validation
85
+ if (paymentSignature.length < 10) {
86
+ throw new Error(JSON.stringify({ code: 400, message: "Invalid PAYMENT-SIGNATURE format" }));
87
+ }
88
+ // STEP 3: Nonce Check - Prevent Replay Attacks across MCP calls
89
+ const signatureHash = await hashSignature(paymentSignature);
90
+ const isUnique = await nonceStore.claim(signatureHash, nonceTtlSeconds);
91
+ if (!isUnique) {
92
+ throw new Error(JSON.stringify({
93
+ code: 409,
94
+ message: "Replay attack detected. PAYMENT-SIGNATURE has already been used."
95
+ }));
96
+ }
97
+ // STEP 4: Settle via Facilitator
98
+ try {
99
+ const controller = new AbortController();
100
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
101
+ let parsedPayload = {};
102
+ try {
103
+ const decodedString = atob(paymentSignature);
104
+ parsedPayload = JSON.parse(decodedString);
105
+ }
106
+ catch {
107
+ await nonceStore.release(signatureHash);
108
+ throw new Error(JSON.stringify({ code: 400, message: "Invalid base64 payload in PAYMENT-SIGNATURE" }));
109
+ }
110
+ const settleResponse = await fetch(`${facilitatorUrl}/settle`, {
111
+ method: "POST",
112
+ headers: { "Content-Type": "application/json" },
113
+ signal: controller.signal,
114
+ body: JSON.stringify({
115
+ paymentPayload: parsedPayload.paymentPayload || parsedPayload.payload,
116
+ paymentRequirements: parsedPayload.paymentRequirements || {
117
+ scheme: "exact",
118
+ network,
119
+ amount: merchantUnits.toString(),
120
+ asset,
121
+ payTo: options.payTo
122
+ },
123
+ resource: { url: targetUrl }
124
+ })
125
+ });
126
+ clearTimeout(timeoutId);
127
+ const rawText = await settleResponse.text();
128
+ let settlement = {};
129
+ try {
130
+ settlement = JSON.parse(rawText);
131
+ }
132
+ catch {
133
+ await nonceStore.release(signatureHash);
134
+ throw new Error(JSON.stringify({ code: 502, message: "Facilitator returned invalid non-JSON response" }));
135
+ }
136
+ if (!settleResponse.ok || !settlement.success) {
137
+ await nonceStore.release(signatureHash);
138
+ throw new Error(JSON.stringify({
139
+ code: 402,
140
+ message: "Payment verification failed",
141
+ details: settlement.error
142
+ }));
143
+ }
144
+ // STEP 5: Payment verified and settled -> Execute tool handler
145
+ const result = await tool.handler(args, context);
146
+ // Append settlement metadata to response if result is an object
147
+ if (typeof result === "object" && result !== null) {
148
+ return {
149
+ ...result,
150
+ _x402Settlement: settlement
151
+ };
152
+ }
153
+ return result;
154
+ }
155
+ catch (err) {
156
+ await nonceStore.release(signatureHash);
157
+ if (err.message?.startsWith("{")) {
158
+ throw err;
159
+ }
160
+ const isTimeout = err.name === "AbortError";
161
+ throw new Error(JSON.stringify({
162
+ code: 504,
163
+ message: isTimeout ? "Facilitator settlement timed out" : "Facilitator network error during settlement",
164
+ details: err.message
165
+ }));
166
+ }
167
+ }
168
+ };
169
+ }
@@ -0,0 +1,38 @@
1
+ import { Context, Next } from "hono";
2
+ import { GuardOptions } from "./guard.js";
3
+ export interface NonceStore {
4
+ claim(key: string, ttlSeconds: number): Promise<boolean>;
5
+ release(key: string): Promise<void>;
6
+ }
7
+ export declare class MemoryNonceStore implements NonceStore {
8
+ private cache;
9
+ claim(key: string, ttlSeconds: number): Promise<boolean>;
10
+ release(key: string): Promise<void>;
11
+ private cleanup;
12
+ }
13
+ export declare class RedisNonceStore implements NonceStore {
14
+ private redisClient;
15
+ private keyPrefix;
16
+ constructor(redisClient: {
17
+ set: (key: string, value: string, ...args: any[]) => Promise<any>;
18
+ del: (key: string) => Promise<any>;
19
+ }, keyPrefix?: string);
20
+ claim(key: string, ttlSeconds: number): Promise<boolean>;
21
+ release(key: string): Promise<void>;
22
+ }
23
+ export interface MonetizeOptions {
24
+ price: string;
25
+ payTo: string;
26
+ asset?: string;
27
+ network?: string;
28
+ platformFeeBps?: number;
29
+ platformWallet?: string;
30
+ facilitatorUrl?: string;
31
+ description?: string;
32
+ nonceStore?: NonceStore;
33
+ nonceTtlSeconds?: number;
34
+ timeoutMs?: number;
35
+ enableCorsHeaders?: boolean;
36
+ guard?: boolean | GuardOptions;
37
+ }
38
+ export declare function monetize(options: MonetizeOptions): (c: Context, next: Next) => Promise<void | Response>;
@@ -0,0 +1,206 @@
1
+ import { x402Guard } from "./guard.js";
2
+ async function hashSignature(signature) {
3
+ const encoder = new TextEncoder();
4
+ const data = encoder.encode(signature);
5
+ const cryptoObj = globalThis.crypto;
6
+ if (!cryptoObj?.subtle) {
7
+ throw new Error("[Monetize] Web Crypto API (crypto.subtle) is unavailable in this environment.");
8
+ }
9
+ const hashBuffer = await cryptoObj.subtle.digest("SHA-256", data);
10
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
11
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
12
+ }
13
+ export class MemoryNonceStore {
14
+ cache = new Map();
15
+ async claim(key, ttlSeconds) {
16
+ const now = Date.now();
17
+ const expiry = this.cache.get(key);
18
+ if (expiry !== undefined && expiry > now) {
19
+ return false;
20
+ }
21
+ this.cache.set(key, now + ttlSeconds * 1000);
22
+ this.cleanup(now);
23
+ return true;
24
+ }
25
+ async release(key) {
26
+ this.cache.delete(key);
27
+ }
28
+ cleanup(now) {
29
+ if (this.cache.size > 2_000) {
30
+ for (const [k, exp] of this.cache.entries()) {
31
+ if (exp <= now)
32
+ this.cache.delete(k);
33
+ }
34
+ }
35
+ }
36
+ }
37
+ export class RedisNonceStore {
38
+ redisClient;
39
+ keyPrefix;
40
+ constructor(redisClient, keyPrefix = "x402:nonce:") {
41
+ this.redisClient = redisClient;
42
+ this.keyPrefix = keyPrefix;
43
+ }
44
+ async claim(key, ttlSeconds) {
45
+ const redisKey = `${this.keyPrefix}${key}`;
46
+ const result = await this.redisClient.set(redisKey, "1", "EX", ttlSeconds, "NX");
47
+ return result === "OK" || result === 1 || result === true;
48
+ }
49
+ async release(key) {
50
+ const redisKey = `${this.keyPrefix}${key}`;
51
+ await this.redisClient.del(redisKey);
52
+ }
53
+ }
54
+ const defaultMemoryStore = new MemoryNonceStore();
55
+ const DEFAULT_USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
56
+ const DEFAULT_NETWORK = "eip155:8453";
57
+ const DEFAULT_FACILITATOR = "https://warppay402.com";
58
+ const DEFAULT_PLATFORM_WALLET = "0x2bd4e0ea72e21155ec41f8613eafd433193c4d8b";
59
+ function toBase64(str) {
60
+ const bytes = new TextEncoder().encode(str);
61
+ let binary = "";
62
+ for (let i = 0; i < bytes.byteLength; i++) {
63
+ binary += String.fromCharCode(bytes[i]);
64
+ }
65
+ return btoa(binary);
66
+ }
67
+ function parseTokenUnits(priceStr) {
68
+ const normalized = String(priceStr).trim();
69
+ if (!normalized || isNaN(Number(normalized)) || Number(normalized) <= 0) {
70
+ throw new Error(`[Monetize] Invalid price option: "${priceStr}". Must be a positive decimal number.`);
71
+ }
72
+ const [whole, fraction = ""] = normalized.split(".");
73
+ const paddedFraction = fraction.padEnd(6, "0").slice(0, 6);
74
+ return BigInt(whole + paddedFraction);
75
+ }
76
+ export function monetize(options) {
77
+ if (!options.payTo || !options.payTo.startsWith("0x") || options.payTo.length !== 42) {
78
+ throw new Error("[Monetize] A valid 42-character EVM wallet address is required for 'payTo'.");
79
+ }
80
+ const asset = options.asset || DEFAULT_USDC_BASE;
81
+ const network = options.network || DEFAULT_NETWORK;
82
+ const facilitatorUrl = options.facilitatorUrl || DEFAULT_FACILITATOR;
83
+ const platformFeeBps = options.platformFeeBps ?? 50;
84
+ const platformWallet = options.platformWallet || DEFAULT_PLATFORM_WALLET;
85
+ const nonceStore = options.nonceStore || defaultMemoryStore;
86
+ const nonceTtlSeconds = options.nonceTtlSeconds ?? 300;
87
+ const timeoutMs = options.timeoutMs ?? 8000;
88
+ const enableCors = options.enableCorsHeaders ?? true;
89
+ if (platformFeeBps > 0 && (!platformWallet || !platformWallet.startsWith("0x") || platformWallet.length !== 42)) {
90
+ throw new Error("[Monetize] A valid EVM 'platformWallet' address is required when platformFeeBps > 0.");
91
+ }
92
+ const baseUnits = parseTokenUnits(options.price);
93
+ const platformFeeUnits = (baseUnits * BigInt(platformFeeBps)) / BigInt(10_000);
94
+ const merchantUnits = baseUnits - platformFeeUnits;
95
+ const guardMiddleware = options.guard
96
+ ? x402Guard(typeof options.guard === "object" ? options.guard : {})
97
+ : null;
98
+ return async (c, next) => {
99
+ if (guardMiddleware) {
100
+ let guardPassed = false;
101
+ const guardRes = await guardMiddleware(c, async () => {
102
+ guardPassed = true;
103
+ });
104
+ if (!guardPassed) {
105
+ return guardRes;
106
+ }
107
+ }
108
+ if (enableCors) {
109
+ c.header("Access-Control-Expose-Headers", "PAYMENT-REQUIRED, PAYMENT-RESPONSE");
110
+ }
111
+ const paymentSignature = c.req.header("payment-signature") || c.req.header("PAYMENT-SIGNATURE");
112
+ const targetUrl = c.req.url;
113
+ // STEP 1: Return HTTP 402 Challenge if signature is missing
114
+ if (!paymentSignature) {
115
+ const challengePayload = {
116
+ x402Version: 2,
117
+ resource: {
118
+ url: targetUrl,
119
+ description: options.description || "Monetized API Access",
120
+ mimeType: "application/json"
121
+ },
122
+ accepts: [
123
+ {
124
+ scheme: "exact",
125
+ network,
126
+ amount: merchantUnits.toString(),
127
+ asset,
128
+ payTo: options.payTo,
129
+ maxTimeoutSeconds: nonceTtlSeconds,
130
+ extra: {
131
+ platformFee: platformFeeUnits.toString(),
132
+ platformWallet: platformWallet
133
+ }
134
+ }
135
+ ]
136
+ };
137
+ c.header("PAYMENT-REQUIRED", toBase64(JSON.stringify(challengePayload)));
138
+ return c.json({ error: "Payment required", x402: challengePayload }, 402);
139
+ }
140
+ // STEP 2: Signature format check
141
+ if (paymentSignature.length < 10) {
142
+ return c.json({ error: "Invalid PAYMENT-SIGNATURE format" }, 400);
143
+ }
144
+ // STEP 3: Nonce Claim
145
+ const signatureHash = await hashSignature(paymentSignature);
146
+ const isUnique = await nonceStore.claim(signatureHash, nonceTtlSeconds);
147
+ if (!isUnique) {
148
+ return c.json({
149
+ error: "Replay attack detected",
150
+ details: "This PAYMENT-SIGNATURE has already been processed or submitted."
151
+ }, 409);
152
+ }
153
+ // STEP 4: Verify & Settle via Facilitator
154
+ try {
155
+ const controller = new AbortController();
156
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
157
+ let parsedPayload = {};
158
+ try {
159
+ const decodedString = atob(paymentSignature);
160
+ parsedPayload = JSON.parse(decodedString);
161
+ }
162
+ catch {
163
+ await nonceStore.release(signatureHash);
164
+ return c.json({ error: "Invalid base64 payload in PAYMENT-SIGNATURE" }, 400);
165
+ }
166
+ const settleResponse = await fetch(`${facilitatorUrl}/settle`, {
167
+ method: "POST",
168
+ headers: { "Content-Type": "application/json" },
169
+ signal: controller.signal,
170
+ body: JSON.stringify({
171
+ signature: parsedPayload.signature || parsedPayload.paymentPayload?.signature,
172
+ authorization: parsedPayload.authorization || parsedPayload.paymentPayload?.authorization,
173
+ paymentPayload: parsedPayload,
174
+ paymentRequirements: parsedPayload.paymentRequirements,
175
+ resource: { url: targetUrl }
176
+ })
177
+ });
178
+ clearTimeout(timeoutId);
179
+ const rawText = await settleResponse.text();
180
+ let settlement = {};
181
+ try {
182
+ settlement = JSON.parse(rawText);
183
+ }
184
+ catch {
185
+ await nonceStore.release(signatureHash);
186
+ return c.json({ error: "Facilitator returned an invalid non-JSON response", status: settleResponse.status }, 502);
187
+ }
188
+ if (!settleResponse.ok || !settlement.success) {
189
+ await nonceStore.release(signatureHash);
190
+ c.header("PAYMENT-RESPONSE", toBase64(JSON.stringify({ success: false, error: settlement.error })));
191
+ return c.json({ error: "Payment verification failed", details: settlement.error }, 402);
192
+ }
193
+ // STEP 5: Success
194
+ c.header("PAYMENT-RESPONSE", toBase64(JSON.stringify(settlement)));
195
+ await next();
196
+ }
197
+ catch (err) {
198
+ await nonceStore.release(signatureHash);
199
+ const isTimeout = err.name === "AbortError";
200
+ return c.json({
201
+ error: isTimeout ? "Facilitator settlement timed out" : "Facilitator network error during settlement",
202
+ message: err.message
203
+ }, 504);
204
+ }
205
+ };
206
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,94 @@
1
+ import { createWalletClient, http } from "viem";
2
+ import { privateKeyToAccount } from "viem/accounts";
3
+ import { base } from "viem/chains";
4
+ const PRIVATE_KEY = process.env.PRIVATE_KEY;
5
+ if (!PRIVATE_KEY) {
6
+ console.error("Please set process.env.PRIVATE_KEY");
7
+ process.exit(1);
8
+ }
9
+ const account = privateKeyToAccount(PRIVATE_KEY);
10
+ const walletClient = createWalletClient({
11
+ account,
12
+ chain: base,
13
+ transport: http("https://mainnet.base.org")
14
+ });
15
+ const API_URL = process.env.API_URL || "http://localhost:3000/api/weather";
16
+ async function executeAgentPayment() {
17
+ console.log(`[Agent] Probing endpoint: ${API_URL}`);
18
+ // Step 1: Retrieve 402 challenge
19
+ const initialRes = await fetch(API_URL);
20
+ if (initialRes.status !== 402) {
21
+ console.log("[Agent] Endpoint did not request x402 payment.");
22
+ return;
23
+ }
24
+ const paymentRequiredHeader = initialRes.headers.get("PAYMENT-REQUIRED");
25
+ if (!paymentRequiredHeader) {
26
+ throw new Error("Missing PAYMENT-REQUIRED header from server");
27
+ }
28
+ const challenge = JSON.parse(atob(paymentRequiredHeader));
29
+ const requirement = challenge.accepts[0];
30
+ console.log(`[Agent] Payment required: $${Number(requirement.amount) / 1e6} USDC on Base`);
31
+ // Step 2: Sign EIP-712 payment authorization
32
+ const domain = {
33
+ name: "USD Coin",
34
+ version: "2",
35
+ chainId: 8453,
36
+ verifyingContract: requirement.asset
37
+ };
38
+ const types = {
39
+ TransferWithAuthorization: [
40
+ { name: "from", type: "address" },
41
+ { name: "to", type: "address" },
42
+ { name: "value", type: "uint256" },
43
+ { name: "validAfter", type: "uint256" },
44
+ { name: "validBefore", type: "uint256" },
45
+ { name: "nonce", type: "bytes32" }
46
+ ]
47
+ };
48
+ const now = Math.floor(Date.now() / 1000);
49
+ const nonce = `0x${Array.from(crypto.getRandomValues(new Uint8Array(32)))
50
+ .map((b) => b.toString(16).padStart(2, "0"))
51
+ .join("")}`;
52
+ const authorization = {
53
+ from: account.address,
54
+ to: requirement.payTo,
55
+ value: requirement.amount,
56
+ validAfter: (now - 60).toString(),
57
+ validBefore: (now + requirement.maxTimeoutSeconds).toString(),
58
+ nonce
59
+ };
60
+ const signatureHex = await walletClient.signTypedData({
61
+ domain,
62
+ types,
63
+ primaryType: "TransferWithAuthorization",
64
+ message: {
65
+ from: account.address,
66
+ to: requirement.payTo,
67
+ value: BigInt(requirement.amount),
68
+ validAfter: BigInt(now - 60),
69
+ validBefore: BigInt(now + requirement.maxTimeoutSeconds),
70
+ nonce
71
+ }
72
+ });
73
+ // Step 3: Construct root-level payment payload compatible with XPay relayer
74
+ const paymentSignaturePayload = JSON.stringify({
75
+ x402Version: 2,
76
+ scheme: "exact",
77
+ network: "eip155:8453",
78
+ signature: signatureHex,
79
+ authorization,
80
+ paymentRequirements: requirement
81
+ });
82
+ const encodedSignature = btoa(paymentSignaturePayload);
83
+ // Step 4: Resubmit with PAYMENT-SIGNATURE header
84
+ console.log("[Agent] Submitting signed authorization to server...");
85
+ const paidRes = await fetch(API_URL, {
86
+ headers: {
87
+ "PAYMENT-SIGNATURE": encodedSignature
88
+ }
89
+ });
90
+ const responseData = await paidRes.json();
91
+ console.log(`[Server Status]: ${paidRes.status}`);
92
+ console.log("[Server Payload]:", responseData);
93
+ }
94
+ executeAgentPayment().catch(console.error);
@@ -0,0 +1,4 @@
1
+ import "dotenv/config";
2
+ import { Hono } from "hono";
3
+ declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ export default app;
package/dist/server.js ADDED
@@ -0,0 +1,25 @@
1
+ import "dotenv/config";
2
+ import { serve } from "@hono/node-server";
3
+ import { monetize } from "./monetize.js";
4
+ import { Hono } from "hono";
5
+ const app = new Hono();
6
+ // Define the developer's wallet address (where 99.5% of funds go)
7
+ const developerWallet = "0x2bd4e0ea72e21155ec41f8613eafd433193c4d8b";
8
+ app.use("/api/weather", monetize({
9
+ price: "0.01",
10
+ payTo: developerWallet,
11
+ platformWallet: "0x2bd4e0ea72e21155ec41f8613eafd433193c4d8b",
12
+ platformFeeBps: 50,
13
+ facilitatorUrl: "https://warppay402.com"
14
+ }));
15
+ app.get("/api/weather", (c) => {
16
+ return c.json({
17
+ status: "success",
18
+ timestamp: new Date().toISOString(),
19
+ data: { city: "Austin", temperature: "78°F", condition: "Sunny" }
20
+ });
21
+ });
22
+ serve({ fetch: app.fetch, port: 3000 }, (info) => {
23
+ console.log(`[API Server] Running on http://localhost:${info.port}`);
24
+ });
25
+ export default app;
@@ -0,0 +1 @@
1
+ {"version":"7.0.2","root":["../demo.ts","../facilitator.ts","../guard.ts","../index.ts","../mcpWrapper.ts","../monetize.ts","../pay_client.ts","../server.ts","../examples/basic-monetized-server.ts"],"packageJsons":["../node_modules/@hono/node-server/package.json","../node_modules/@noble/curves/esm/package.json","../node_modules/@noble/curves/package.json","../node_modules/@noble/hashes/esm/package.json","../node_modules/@noble/hashes/package.json","../node_modules/@scure/bip32/lib/esm/package.json","../node_modules/@scure/bip32/package.json","../node_modules/@scure/bip39/esm/package.json","../node_modules/@scure/bip39/package.json","../node_modules/@types/node/package.json","../node_modules/abitype/package.json","../node_modules/dotenv/package.json","../node_modules/eventemitter3/package.json","../node_modules/hono/dist/types/package.json","../node_modules/hono/package.json","../node_modules/ox/AbiFunction/package.json","../node_modules/ox/BlockOverrides/package.json","../node_modules/ox/Hex/package.json","../node_modules/ox/RpcResponse/package.json","../node_modules/ox/WebAuthnP256/package.json","../node_modules/ox/package.json","../node_modules/ox/tempo/KeyAuthorization/package.json","../node_modules/ox/tempo/MultisigConfig/package.json","../node_modules/ox/tempo/SignatureEnvelope/package.json","../node_modules/ox/tempo/TempoAddress/package.json","../node_modules/ox/tempo/TokenId/package.json","../node_modules/ox/tempo/TxEnvelopeTempo/package.json","../node_modules/ox/tempo/package.json","../node_modules/typescript/package.json","../node_modules/undici-types/package.json","../node_modules/viem/_types/package.json","../node_modules/viem/accounts/package.json","../node_modules/viem/chains/package.json","../node_modules/viem/package.json","../package.json"],"missingPackageJsons":["../node_modules/@hono/node-server/dist/package.json","../node_modules/@noble/curves/_shortw_utils/package.json","../node_modules/@noble/curves/abstract/bls/package.json","../node_modules/@noble/curves/abstract/edwards/package.json","../node_modules/@noble/curves/abstract/montgomery/package.json","../node_modules/@noble/curves/esm/abstract/package.json","../node_modules/@noble/hashes/utils/package.json","../node_modules/@scure/bip39/esm/wordlists/package.json","../node_modules/@scure/bip39/wordlists/czech/package.json","../node_modules/@scure/bip39/wordlists/english/package.json","../node_modules/@scure/bip39/wordlists/french/package.json","../node_modules/@scure/bip39/wordlists/italian/package.json","../node_modules/@scure/bip39/wordlists/japanese/package.json","../node_modules/@scure/bip39/wordlists/korean/package.json","../node_modules/@scure/bip39/wordlists/portuguese/package.json","../node_modules/@scure/bip39/wordlists/simplified-chinese/package.json","../node_modules/@scure/bip39/wordlists/spanish/package.json","../node_modules/@scure/bip39/wordlists/traditional-chinese/package.json","../node_modules/@types/assert/package.json","../node_modules/@types/assert/strict/package.json","../node_modules/@types/async_hooks/package.json","../node_modules/@types/buffer/package.json","../node_modules/@types/child_process/package.json","../node_modules/@types/cluster/package.json","../node_modules/@types/console/package.json","../node_modules/@types/constants/package.json","../node_modules/@types/crypto/package.json","../node_modules/@types/dgram/package.json","../node_modules/@types/diagnostics_channel/package.json","../node_modules/@types/dns/package.json","../node_modules/@types/dns/promises/package.json","../node_modules/@types/domain/package.json","../node_modules/@types/events/package.json","../node_modules/@types/fs/package.json","../node_modules/@types/fs/promises/package.json","../node_modules/@types/http/package.json","../node_modules/@types/http2/package.json","../node_modules/@types/https/package.json","../node_modules/@types/inspector/package.json","../node_modules/@types/inspector/promises/package.json","../node_modules/@types/module/package.json","../node_modules/@types/net/package.json","../node_modules/@types/node/assert/package.json","../node_modules/@types/node/dns/package.json","../node_modules/@types/node/fs/package.json","../node_modules/@types/node/inspector/package.json","../node_modules/@types/node/path/package.json","../node_modules/@types/node/readline/package.json","../node_modules/@types/node/stream/package.json","../node_modules/@types/node/test/package.json","../node_modules/@types/node/timers/package.json","../node_modules/@types/node/util/package.json","../node_modules/@types/node/web-globals/package.json","../node_modules/@types/node/zlib/package.json","../node_modules/@types/os/package.json","../node_modules/@types/path/package.json","../node_modules/@types/path/posix/package.json","../node_modules/@types/path/win32/package.json","../node_modules/@types/perf_hooks/package.json","../node_modules/@types/process/package.json","../node_modules/@types/punycode/package.json","../node_modules/@types/querystring/package.json","../node_modules/@types/readline/package.json","../node_modules/@types/readline/promises/package.json","../node_modules/@types/repl/package.json","../node_modules/@types/stream/consumers/package.json","../node_modules/@types/stream/iter/package.json","../node_modules/@types/stream/package.json","../node_modules/@types/stream/promises/package.json","../node_modules/@types/stream/web/package.json","../node_modules/@types/string_decoder/package.json","../node_modules/@types/timers/package.json","../node_modules/@types/timers/promises/package.json","../node_modules/@types/tls/package.json","../node_modules/@types/trace_events/package.json","../node_modules/@types/tty/package.json","../node_modules/@types/url/package.json","../node_modules/@types/util/package.json","../node_modules/@types/util/types/package.json","../node_modules/@types/v8/package.json","../node_modules/@types/vm/package.json","../node_modules/@types/wasi/package.json","../node_modules/@types/worker_threads/package.json","../node_modules/@types/zlib/package.json","../node_modules/abitype/dist/package.json","../node_modules/abitype/dist/types/exports/package.json","../node_modules/abitype/dist/types/human-readable/errors/package.json","../node_modules/abitype/dist/types/human-readable/package.json","../node_modules/abitype/dist/types/human-readable/types/package.json","../node_modules/abitype/dist/types/package.json","../node_modules/assert/package.json","../node_modules/assert/strict/package.json","../node_modules/async_hooks/package.json","../node_modules/buffer/package.json","../node_modules/child_process/package.json","../node_modules/cluster/package.json","../node_modules/console/package.json","../node_modules/constants/package.json","../node_modules/crypto/package.json","../node_modules/dgram/package.json","../node_modules/diagnostics_channel/package.json","../node_modules/dns/package.json","../node_modules/dns/promises/package.json","../node_modules/domain/package.json","../node_modules/dotenv/config/package.json","../node_modules/events/package.json","../node_modules/fs/package.json","../node_modules/fs/promises/package.json","../node_modules/hono/dist/types/client/package.json","../node_modules/hono/dist/types/helper/package.json","../node_modules/hono/dist/types/helper/websocket/package.json","../node_modules/hono/dist/types/request/package.json","../node_modules/hono/dist/types/utils/package.json","../node_modules/hono/ws/package.json","../node_modules/http/package.json","../node_modules/http2/package.json","../node_modules/https/package.json","../node_modules/inspector/package.json","../node_modules/inspector/promises/package.json","../node_modules/module/package.json","../node_modules/net/package.json","../node_modules/os/package.json","../node_modules/ox/_types/core/internal/mnemonic/package.json","../node_modules/ox/_types/core/internal/package.json","../node_modules/ox/_types/core/internal/rpcSchemas/package.json","../node_modules/ox/_types/core/package.json","../node_modules/ox/_types/package.json","../node_modules/ox/_types/tempo/package.json","../node_modules/ox/_types/webauthn/package.json","../node_modules/path/package.json","../node_modules/path/posix/package.json","../node_modules/path/win32/package.json","../node_modules/perf_hooks/package.json","../node_modules/process/package.json","../node_modules/punycode/package.json","../node_modules/querystring/package.json","../node_modules/readline/package.json","../node_modules/readline/promises/package.json","../node_modules/repl/package.json","../node_modules/stream/consumers/package.json","../node_modules/stream/iter/package.json","../node_modules/stream/package.json","../node_modules/stream/promises/package.json","../node_modules/stream/web/package.json","../node_modules/string_decoder/package.json","../node_modules/timers/package.json","../node_modules/timers/promises/package.json","../node_modules/tls/package.json","../node_modules/trace_events/package.json","../node_modules/tty/package.json","../node_modules/url/package.json","../node_modules/util/package.json","../node_modules/util/types/package.json","../node_modules/v8/package.json","../node_modules/viem/_types/account-abstraction/accounts/package.json","../node_modules/viem/_types/account-abstraction/package.json","../node_modules/viem/_types/account-abstraction/types/package.json","../node_modules/viem/_types/accounts/package.json","../node_modules/viem/_types/accounts/utils/package.json","../node_modules/viem/_types/actions/ens/package.json","../node_modules/viem/_types/actions/package.json","../node_modules/viem/_types/actions/public/package.json","../node_modules/viem/_types/actions/siwe/package.json","../node_modules/viem/_types/actions/test/package.json","../node_modules/viem/_types/actions/token/package.json","../node_modules/viem/_types/actions/wallet/package.json","../node_modules/viem/_types/celo/package.json","../node_modules/viem/_types/chains/definitions/package.json","../node_modules/viem/_types/chains/definitions/skale/package.json","../node_modules/viem/_types/chains/package.json","../node_modules/viem/_types/clients/decorators/package.json","../node_modules/viem/_types/clients/package.json","../node_modules/viem/_types/clients/transports/package.json","../node_modules/viem/_types/constants/package.json","../node_modules/viem/_types/errors/package.json","../node_modules/viem/_types/experimental/erc7895/actions/package.json","../node_modules/viem/_types/experimental/erc7895/package.json","../node_modules/viem/_types/experimental/package.json","../node_modules/viem/_types/op-stack/package.json","../node_modules/viem/_types/op-stack/types/package.json","../node_modules/viem/_types/tempo/package.json","../node_modules/viem/_types/tokens/package.json","../node_modules/viem/_types/types/package.json","../node_modules/viem/_types/utils/abi/package.json","../node_modules/viem/_types/utils/address/package.json","../node_modules/viem/_types/utils/authorization/package.json","../node_modules/viem/_types/utils/blob/package.json","../node_modules/viem/_types/utils/block/package.json","../node_modules/viem/_types/utils/chain/package.json","../node_modules/viem/_types/utils/data/package.json","../node_modules/viem/_types/utils/encoding/package.json","../node_modules/viem/_types/utils/ens/avatar/package.json","../node_modules/viem/_types/utils/ens/package.json","../node_modules/viem/_types/utils/errors/package.json","../node_modules/viem/_types/utils/formatters/package.json","../node_modules/viem/_types/utils/hash/package.json","../node_modules/viem/_types/utils/kzg/package.json","../node_modules/viem/_types/utils/package.json","../node_modules/viem/_types/utils/promise/package.json","../node_modules/viem/_types/utils/rpc/package.json","../node_modules/viem/_types/utils/signature/package.json","../node_modules/viem/_types/utils/siwe/package.json","../node_modules/viem/_types/utils/transaction/package.json","../node_modules/viem/_types/utils/unit/package.json","../node_modules/viem/_types/zksync/accounts/package.json","../node_modules/viem/_types/zksync/actions/package.json","../node_modules/viem/_types/zksync/constants/package.json","../node_modules/viem/_types/zksync/decorators/package.json","../node_modules/viem/_types/zksync/errors/package.json","../node_modules/viem/_types/zksync/package.json","../node_modules/viem/_types/zksync/types/package.json","../node_modules/viem/_types/zksync/utils/abi/package.json","../node_modules/viem/_types/zksync/utils/bridge/package.json","../node_modules/viem/_types/zksync/utils/package.json","../node_modules/viem/_types/zksync/utils/paymaster/package.json","../node_modules/vm/package.json","../node_modules/wasi/package.json","../node_modules/worker_threads/package.json","../node_modules/zlib/package.json","../node_modules/zod/package.json"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@warppay402/server",
3
+ "version": "1.0.0",
4
+ "description": "Instant MCP Tool & x402 Monetization SDK for Cloudflare Gateway and Base",
5
+ "homepage": "https://www.warppay402.com",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "license": "MIT",
17
+ "keywords": [
18
+ "x402",
19
+ "mcp",
20
+ "monetization",
21
+ "base",
22
+ "usdc",
23
+ "hono",
24
+ "ai-agents",
25
+ "warppay402"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/golfgolfgolf200/warppay402-server.git"
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsc --build",
36
+ "prepare": "npm run build",
37
+ "test": "vitest run",
38
+ "example": "tsx examples/basic-monetized-server.ts"
39
+ },
40
+ "dependencies": {
41
+ "@hono/node-server": "^2.1.0",
42
+ "@xpaysh/x402": "^0.1.2",
43
+ "dotenv": "^17.4.2",
44
+ "hono": "^4.13.0",
45
+ "viem": "^2.55.17"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^26.2.0",
49
+ "tsx": "^4.23.9",
50
+ "typescript": "^7.0.2",
51
+ "vitest": "^4.1.10"
52
+ }
53
+ }