wolverine-ai 5.0.2 → 5.0.3

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.0.2",
3
+ "version": "5.0.3",
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": {
@@ -130,11 +130,11 @@ async function x402Plugin(fastify, opts) {
130
130
  return;
131
131
  }
132
132
 
133
- // Payment present — verify
133
+ // Payment present — verify via facilitator or direct signature check
134
134
  const verified = await _verifyPayment(paymentSig, price);
135
135
  if (verified.valid) {
136
136
  reply.header("Payment-Response", JSON.stringify(verified.receipt || {}));
137
- request.x402 = { paid: true, amount: price, receipt: verified.receipt, txHash: verified.txHash };
137
+ request.x402 = { paid: true, amount: price, receipt: verified.receipt, txHash: verified.txHash, from: verified.from };
138
138
  return; // continue to route handler
139
139
  }
140
140
 
@@ -152,45 +152,114 @@ async function x402Plugin(fastify, opts) {
152
152
  }
153
153
 
154
154
  async function _verifyPayment(paymentSig, price) {
155
+ // Decode the payment signature (base64 JSON payload from frontend)
156
+ let payload;
155
157
  try {
156
- // Try @x402/core if available
157
- const { HTTPFacilitatorClient } = require("@x402/core/server");
158
- const facilitator = new HTTPFacilitatorClient({ url: _facilitatorUrl });
159
- const result = await facilitator.verify({
158
+ payload = JSON.parse(Buffer.from(paymentSig, "base64").toString());
159
+ } catch {
160
+ // Not base64 might be raw x402 format, try facilitator
161
+ return _verifyViaFacilitator(paymentSig, price);
162
+ }
163
+
164
+ // Direct verification: validate the EIP-3009 TransferWithAuthorization signature
165
+ if (payload.payload?.authorization && payload.payload?.signature) {
166
+ const auth = payload.payload.authorization;
167
+ const sig = payload.payload.signature;
168
+
169
+ // Verify the amount matches the price
170
+ const expectedUsdc = Math.round(parseFloat(price.replace("$", "")) * 1e6);
171
+ const actualUsdc = parseInt(auth.value, 16) || parseInt(auth.value, 10) || 0;
172
+ if (actualUsdc < expectedUsdc * 0.99) { // 1% tolerance for rounding
173
+ return { valid: false, reason: "Amount mismatch" };
174
+ }
175
+
176
+ // Verify payTo matches
177
+ if (auth.to?.toLowerCase() !== _payTo?.toLowerCase()) {
178
+ return { valid: false, reason: "Wrong recipient" };
179
+ }
180
+
181
+ // Verify not expired
182
+ if (auth.validBefore && auth.validBefore < Math.floor(Date.now() / 1000)) {
183
+ return { valid: false, reason: "Payment expired" };
184
+ }
185
+
186
+ // Recover signer from EIP-712 typed data signature
187
+ try {
188
+ const { ethers } = require("ethers");
189
+ const domain = {
190
+ name: "USD Coin",
191
+ version: "2",
192
+ chainId: 8453,
193
+ verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
194
+ };
195
+ const types = {
196
+ TransferWithAuthorization: [
197
+ { name: "from", type: "address" },
198
+ { name: "to", type: "address" },
199
+ { name: "value", type: "uint256" },
200
+ { name: "validAfter", type: "uint256" },
201
+ { name: "validBefore", type: "uint256" },
202
+ { name: "nonce", type: "bytes32" },
203
+ ],
204
+ };
205
+ const message = {
206
+ from: auth.from,
207
+ to: auth.to,
208
+ value: auth.value,
209
+ validAfter: auth.validAfter,
210
+ validBefore: auth.validBefore,
211
+ nonce: auth.nonce,
212
+ };
213
+
214
+ const recoveredAddress = ethers.verifyTypedData(domain, types, message, sig);
215
+ if (recoveredAddress.toLowerCase() !== auth.from.toLowerCase()) {
216
+ return { valid: false, reason: "Signature mismatch" };
217
+ }
218
+
219
+ // Signature valid — the user authorized this USDC transfer
220
+ return {
221
+ valid: true,
222
+ from: auth.from,
223
+ receipt: { authorization: auth, signature: sig, verified: "direct" },
224
+ txHash: null, // on-chain tx happens when we call transferWithAuthorization
225
+ };
226
+ } catch (err) {
227
+ return { valid: false, reason: "Signature verification error: " + err.message };
228
+ }
229
+ }
230
+
231
+ // Unknown format — try facilitator
232
+ return _verifyViaFacilitator(paymentSig, price);
233
+ }
234
+
235
+ async function _verifyViaFacilitator(paymentSig, price) {
236
+ try {
237
+ const https = require("https");
238
+ const http = require("http");
239
+ const url = new (require("url").URL)(_facilitatorUrl + "/verify");
240
+ const body = JSON.stringify({
160
241
  paymentSignature: paymentSig,
161
242
  routeConfig: { accepts: [{ scheme: "exact", price, network: _network, payTo: _payTo }] },
162
243
  });
163
- return { valid: result.valid, receipt: result.receipt, txHash: result.txHash };
164
- } catch {
165
- // Fallback: raw HTTP to facilitator
166
- try {
167
- const https = require("https");
168
- const http = require("http");
169
- const url = new (require("url").URL)(_facilitatorUrl + "/verify");
170
- const body = JSON.stringify({
171
- paymentSignature: paymentSig,
172
- routeConfig: { accepts: [{ scheme: "exact", price, network: _network, payTo: _payTo }] },
173
- });
174
- return new Promise((resolve) => {
175
- const client = url.protocol === "https:" ? https : http;
176
- const req = client.request({
177
- hostname: url.hostname, port: url.port, path: url.pathname, method: "POST",
178
- headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) },
179
- timeout: 10000,
180
- }, (res) => {
181
- let data = "";
182
- res.on("data", (c) => data += c);
183
- res.on("end", () => {
184
- try { const p = JSON.parse(data); resolve({ valid: p.valid || p.success, receipt: p, txHash: p.txHash }); }
185
- catch { resolve({ valid: false }); }
186
- });
244
+ return new Promise((resolve) => {
245
+ const client = url.protocol === "https:" ? https : http;
246
+ const req = client.request({
247
+ hostname: url.hostname, port: url.port, path: url.pathname, method: "POST",
248
+ headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) },
249
+ timeout: 10000,
250
+ }, (res) => {
251
+ let data = "";
252
+ res.on("data", (c) => data += c);
253
+ res.on("end", () => {
254
+ try { const p = JSON.parse(data); resolve({ valid: p.valid || p.success, receipt: p, txHash: p.txHash }); }
255
+ catch { resolve({ valid: false }); }
187
256
  });
188
- req.on("error", () => resolve({ valid: false }));
189
- req.write(body);
190
- req.end();
191
257
  });
192
- } catch { return { valid: false }; }
193
- }
258
+ req.on("error", () => resolve({ valid: false }));
259
+ req.write(body);
260
+ req.end();
261
+ });
262
+ } catch { return { valid: false }; }
194
263
  }
195
264
 
196
265
  x402Plugin[Symbol.for("skip-override")] = true;