wolverine-ai 5.4.3 → 5.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "5.4.3",
3
+ "version": "5.5.0",
4
4
  "description": "Self-healing Node.js server framework powered by AI. Catches crashes, diagnoses errors, generates fixes, verifies, and restarts — automatically.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -4,15 +4,16 @@ const path = require("path");
4
4
  /**
5
5
  * x402 Fastify Plugin — add crypto payments to any route with one flag.
6
6
  *
7
- * Uses @coinbase/x402 facilitator + x402/verify for payment verification
8
- * and on-chain settlement. Matches the working pattern from blockaid-scanner.
7
+ * Uses x402 v2 protocol (@x402/core + @x402/evm) with @coinbase/x402
8
+ * facilitator for CDP authentication. Verify + settle both happen in
9
+ * preHandler — the route handler only runs after USDC moves on-chain.
9
10
  *
10
- * Two modes:
11
- * Fixed price: { x402: { price: "$0.01" } }
12
- * Variable price: { x402: { variable: true, min: "$1", max: "$10000" } }
11
+ * Setup:
12
+ * 1. wolverine --init-vault
13
+ * 2. Set CDP_API_KEY_ID + CDP_API_KEY_SECRET in .env.local
14
+ * 3. Add { config: { x402: { price: "$0.01" } } } to any route
13
15
  */
14
16
 
15
- // Node 18 needs globalThis.crypto
16
17
  if (!globalThis.crypto) {
17
18
  globalThis.crypto = require("crypto").webcrypto;
18
19
  }
@@ -21,8 +22,8 @@ const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
21
22
  const USDC_EIP712 = { name: "USD Coin", version: "2" };
22
23
 
23
24
  let _payTo = null;
24
- let _network = "base"; // v1 format, not CAIP-2
25
- let _facilitatorClient = null;
25
+ let _network = "eip155:8453";
26
+ let _x402Server = null;
26
27
 
27
28
  async function x402Plugin(fastify, opts) {
28
29
  _payTo = opts.payTo || null;
@@ -43,14 +44,18 @@ async function x402Plugin(fastify, opts) {
43
44
  } catch {}
44
45
  }
45
46
 
46
- // Initialize facilitator from @coinbase/x402 (ESM packages, need dynamic import)
47
+ // Initialize v2 x402 SDK with @coinbase/x402 facilitator auth
47
48
  try {
48
- const { facilitator } = await import("@coinbase/x402");
49
- const { useFacilitator } = await import("x402/verify");
50
- _facilitatorClient = useFacilitator(facilitator);
51
- console.log(` 💰 x402: facilitator loaded (@coinbase/x402)`);
49
+ const { facilitator } = require("@coinbase/x402");
50
+ const { x402ResourceServer, HTTPFacilitatorClient } = require("@x402/core/server");
51
+ const { ExactEvmScheme } = require("@x402/evm/exact/server");
52
+
53
+ const client = new HTTPFacilitatorClient(facilitator);
54
+ _x402Server = new x402ResourceServer(client);
55
+ _x402Server.register("eip155:*", new ExactEvmScheme());
56
+ console.log(` 💰 x402: v2 SDK loaded (ExactEvmScheme + CDP facilitator)`);
52
57
  } catch (err) {
53
- console.log(` ⚠️ x402: facilitator init failed (${err.message}) — install @coinbase/x402 x402`);
58
+ console.log(` ⚠️ x402: SDK init failed (${err.message})`);
54
59
  }
55
60
 
56
61
  if (_payTo) {
@@ -61,14 +66,8 @@ async function x402Plugin(fastify, opts) {
61
66
  fastify.addHook("preHandler", async (request, reply) => {
62
67
  const routeConfig = request.routeOptions?.config?.x402 || request.routeConfig?.x402 || request.context?.config?.x402;
63
68
  if (!routeConfig) return;
64
- if (!_payTo) {
65
- reply.code(500).send({ error: "x402 not configuredno wallet address" });
66
- return;
67
- }
68
- if (!_facilitatorClient) {
69
- reply.code(500).send({ error: "x402 facilitator not loaded — install @coinbase/x402 x402" });
70
- return;
71
- }
69
+ if (!_payTo) { reply.code(500).send({ error: "x402 not configured — run wolverine --init-vault" }); return; }
70
+ if (!_x402Server) { reply.code(500).send({ error: "x402 SDK not loadedinstall @coinbase/x402 @x402/core @x402/evm" }); return; }
72
71
 
73
72
  // Determine dollar amount
74
73
  let dollarAmount;
@@ -91,36 +90,29 @@ async function x402Plugin(fastify, opts) {
91
90
  if (!dollarAmount) { reply.code(500).send({ error: "x402 route missing price config" }); return; }
92
91
  }
93
92
 
94
- const usdcAtomicAmount = String(Math.round(dollarAmount * 1e6));
93
+ const usdcAmount = String(Math.round(dollarAmount * 1e6));
95
94
  const price = "$" + dollarAmount.toFixed(2);
96
-
97
- // Check for payment header (X-PAYMENT for v1 compat, payment-signature for v2)
98
95
  const paymentHeader = request.headers["x-payment"] || request.headers["payment-signature"];
99
96
 
100
- // Build payment requirements (v1 format matching @coinbase/x402)
97
+ // Build v2 payment requirements
101
98
  const { getAddress } = await import("viem");
102
- // Build full resource URL (required by facilitator)
103
99
  const proto = request.headers["x-forwarded-proto"] || "https";
104
100
  const host = request.headers["x-forwarded-host"] || request.headers.host || "localhost";
105
- const resourceUrl = `${proto}://${host}${request.url}`;
106
101
 
107
102
  const paymentRequirements = {
108
103
  scheme: "exact",
109
104
  network: _network,
110
- maxAmountRequired: usdcAtomicAmount,
111
- resource: resourceUrl,
112
- description: routeConfig.description || `Payment of $${dollarAmount.toFixed(2)} USDC`,
113
- mimeType: "application/json",
105
+ amount: usdcAmount,
106
+ asset: getAddress(USDC_ADDRESS),
114
107
  payTo: getAddress(_payTo),
115
108
  maxTimeoutSeconds: 60,
116
- asset: getAddress(USDC_ADDRESS),
117
109
  extra: USDC_EIP712,
118
110
  };
119
111
 
120
112
  // No payment — return 402
121
113
  if (!paymentHeader) {
122
114
  reply.code(402).send({
123
- x402Version: 1,
115
+ x402Version: 2,
124
116
  error: "Payment Required",
125
117
  accepts: [paymentRequirements],
126
118
  price,
@@ -132,55 +124,52 @@ async function x402Plugin(fastify, opts) {
132
124
  return;
133
125
  }
134
126
 
135
- // Decode payment — parse raw payload directly (matching working project pattern)
136
- let decodedPayment;
127
+ // Decode payment
128
+ let paymentPayload;
137
129
  try {
138
130
  const raw = JSON.parse(Buffer.from(paymentHeader, "base64").toString("utf-8"));
131
+ if (!raw.payload?.authorization || !raw.payload?.signature) throw new Error("Missing authorization or signature");
139
132
 
140
- // Validate required fields
141
- if (!raw.payload?.authorization || !raw.payload?.signature) {
142
- throw new Error("Missing authorization or signature");
143
- }
144
-
145
- decodedPayment = {
146
- x402Version: raw.x402Version || 1,
133
+ paymentPayload = {
134
+ x402Version: raw.x402Version || 2,
147
135
  scheme: raw.scheme || "exact",
148
136
  network: raw.network || _network,
149
137
  payload: raw.payload,
150
138
  };
139
+ if (raw.accepted) paymentPayload.accepted = raw.accepted;
140
+ if (raw.resource) paymentPayload.resource = raw.resource;
151
141
  } catch (err) {
152
- reply.code(402).send({ error: "Invalid payment format: " + err.message, accepts: [paymentRequirements] });
142
+ reply.code(402).send({ error: "Invalid payment: " + err.message, accepts: [paymentRequirements] });
153
143
  return;
154
144
  }
155
145
 
156
- // For variable pricing, use user's actual payment value as maxAmountRequired
157
- const userValue = decodedPayment.payload.authorization.value;
158
- const actualRequirements = { ...paymentRequirements, maxAmountRequired: userValue };
146
+ // For variable pricing, match maxAmountRequired to user's payment
147
+ const userValue = paymentPayload.payload.authorization.value;
148
+ const actualRequirements = { ...paymentRequirements, amount: userValue };
159
149
 
160
150
  // Verify via facilitator
161
151
  try {
162
- console.log(` 💰 x402 verify: from=${decodedPayment.payload?.authorization?.from} value=${decodedPayment.payload?.authorization?.value} network=${decodedPayment.network}`);
163
- const verifyResult = await _facilitatorClient.verify(decodedPayment, actualRequirements);
152
+ console.log(` 💰 x402 verify: from=${paymentPayload.payload?.authorization?.from} value=${userValue} network=${paymentPayload.network}`);
153
+ const verifyResult = await _x402Server.verifyPayment(paymentPayload, actualRequirements);
164
154
  if (!verifyResult.isValid) {
165
155
  console.log(` ⚠️ x402 verify failed: ${verifyResult.invalidReason} ${verifyResult.invalidMessage || ""}`);
166
- reply.code(402).send({ error: verifyResult.invalidReason || "Payment verification failed", message: verifyResult.invalidMessage, accepts: [paymentRequirements], payer: verifyResult.payer });
156
+ reply.code(402).send({ error: verifyResult.invalidReason, message: verifyResult.invalidMessage, payer: verifyResult.payer });
167
157
  return;
168
158
  }
169
159
  } catch (err) {
170
- // Extract detailed error from the x402 SDK
171
160
  const detail = err.invalidReason || err.invalidMessage || err.cause?.message || "";
172
161
  console.log(` ⚠️ x402 verify error: ${err.message} | ${detail}`);
173
- reply.code(402).send({ error: err.invalidReason || err.message, message: err.invalidMessage || detail, accepts: [paymentRequirements] });
162
+ reply.code(402).send({ error: err.invalidReason || err.message, message: err.invalidMessage || detail });
174
163
  return;
175
164
  }
176
165
 
177
- // Settle via facilitator BEFORE handler runs — USDC must move before granting access
178
- const payer = decodedPayment.payload.authorization.from;
166
+ // Settle via facilitator — USDC moves on-chain BEFORE handler runs
167
+ const payer = paymentPayload.payload.authorization.from;
179
168
  try {
180
- const settleResult = await _facilitatorClient.settle(decodedPayment, actualRequirements);
169
+ const settleResult = await _x402Server.settlePayment(paymentPayload, actualRequirements);
181
170
  if (!settleResult.success) {
182
171
  console.log(` ⚠️ x402 settle failed: ${settleResult.errorReason || "unknown"}`);
183
- reply.code(402).send({ error: "Payment settlement failed", reason: settleResult.errorReason });
172
+ reply.code(402).send({ error: "Settlement failed", reason: settleResult.errorReason });
184
173
  return;
185
174
  }
186
175
  const txHash = settleResult.transaction || null;
@@ -190,17 +179,15 @@ async function x402Plugin(fastify, opts) {
190
179
  _logPayment({ route: request.url, method: request.method, amount: price, from: payer, txHash, verified: true, settled: true, timestamp: Date.now() });
191
180
  } catch (err) {
192
181
  console.log(` ⚠️ x402 settle error: ${err.message}`);
193
- reply.code(402).send({ error: "Payment settlement failed: " + err.message });
182
+ reply.code(402).send({ error: "Settlement failed: " + err.message });
194
183
  return;
195
184
  }
196
185
  });
197
186
 
198
- // No onSend settle needed — settlement happens in preHandler before handler runs
199
-
200
187
  // Public info endpoint
201
188
  fastify.get("/x402/info", async () => ({
202
- payTo: _payTo, network: _network, protocol: "x402", x402Version: 1,
203
- facilitatorLoaded: !!_facilitatorClient,
189
+ payTo: _payTo, network: _network, protocol: "x402", x402Version: 2,
190
+ sdkLoaded: !!_x402Server,
204
191
  docs: "https://docs.cdp.coinbase.com/x402/welcome",
205
192
  }));
206
193
  }