moltspay 0.8.0 → 0.8.1

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/dist/index.mjs CHANGED
@@ -30332,7 +30332,6 @@ init_esm_shims();
30332
30332
  init_esm_shims();
30333
30333
  import { readFileSync } from "fs";
30334
30334
  import { createServer } from "http";
30335
- import { ethers } from "ethers";
30336
30335
 
30337
30336
  // src/chains/index.ts
30338
30337
  init_esm_shims();
@@ -30419,34 +30418,26 @@ init_esm_shims();
30419
30418
  var X402_VERSION = 2;
30420
30419
  var PAYMENT_REQUIRED_HEADER = "x-payment-required";
30421
30420
  var PAYMENT_HEADER = "x-payment";
30421
+ var PAYMENT_RESPONSE_HEADER = "x-payment-response";
30422
+ var DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
30422
30423
  var MoltsPayServer = class {
30423
30424
  manifest;
30424
30425
  skills = /* @__PURE__ */ new Map();
30425
30426
  options;
30426
- provider = null;
30427
- wallet = null;
30427
+ facilitatorUrl;
30428
30428
  constructor(servicesPath, options = {}) {
30429
30429
  const content = readFileSync(servicesPath, "utf-8");
30430
30430
  this.manifest = JSON.parse(content);
30431
30431
  this.options = {
30432
30432
  port: options.port || 3e3,
30433
- host: options.host || "0.0.0.0",
30434
- privateKey: options.privateKey || process.env.MOLTSPAY_PRIVATE_KEY
30433
+ host: options.host || "0.0.0.0"
30435
30434
  };
30436
- if (this.options.privateKey) {
30437
- try {
30438
- const chain2 = getChain(this.manifest.provider.chain);
30439
- this.provider = new ethers.JsonRpcProvider(chain2.rpc);
30440
- this.wallet = new ethers.Wallet(this.options.privateKey, this.provider);
30441
- console.log(`[MoltsPay] Payment wallet: ${this.wallet.address}`);
30442
- } catch (err) {
30443
- console.warn("[MoltsPay] Warning: Could not initialize wallet for payment claims");
30444
- }
30445
- }
30435
+ this.facilitatorUrl = options.facilitatorUrl || DEFAULT_FACILITATOR_URL;
30446
30436
  console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
30447
30437
  console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
30448
30438
  console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
30449
- console.log(`[MoltsPay] Protocol: x402 (gasless, pay-for-success)`);
30439
+ console.log(`[MoltsPay] Facilitator: ${this.facilitatorUrl}`);
30440
+ console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
30450
30441
  }
30451
30442
  /**
30452
30443
  * Register a skill handler for a service
@@ -30456,48 +30447,45 @@ var MoltsPayServer = class {
30456
30447
  if (!config) {
30457
30448
  throw new Error(`Service '${serviceId}' not found in manifest`);
30458
30449
  }
30459
- this.skills.set(serviceId, {
30460
- id: serviceId,
30461
- config,
30462
- handler
30463
- });
30464
- console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
30450
+ this.skills.set(serviceId, { id: serviceId, config, handler });
30465
30451
  return this;
30466
30452
  }
30467
30453
  /**
30468
- * Start the server
30454
+ * Start HTTP server
30469
30455
  */
30470
30456
  listen(port) {
30471
- const p = port || this.options.port;
30457
+ const p = port || this.options.port || 3e3;
30458
+ const host = this.options.host || "0.0.0.0";
30472
30459
  const server = createServer((req, res) => this.handleRequest(req, res));
30473
- server.listen(p, this.options.host, () => {
30474
- console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);
30460
+ server.listen(p, host, () => {
30461
+ console.log(`[MoltsPay] Server listening on http://${host}:${p}`);
30475
30462
  console.log(`[MoltsPay] Endpoints:`);
30476
30463
  console.log(` GET /services - List available services`);
30477
30464
  console.log(` POST /execute - Execute service (x402 payment)`);
30478
30465
  });
30479
30466
  }
30467
+ /**
30468
+ * Handle incoming request
30469
+ */
30480
30470
  async handleRequest(req, res) {
30481
- const url = new URL(req.url || "/", `http://${req.headers.host}`);
30482
- const path3 = url.pathname;
30483
- const method = req.method || "GET";
30484
30471
  res.setHeader("Access-Control-Allow-Origin", "*");
30485
30472
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
30486
30473
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment");
30487
30474
  res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response");
30488
- if (method === "OPTIONS") {
30475
+ if (req.method === "OPTIONS") {
30489
30476
  res.writeHead(204);
30490
30477
  res.end();
30491
30478
  return;
30492
30479
  }
30493
30480
  try {
30494
- if (method === "GET" && path3 === "/services") {
30481
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
30482
+ if (url.pathname === "/services" && req.method === "GET") {
30495
30483
  return this.handleGetServices(res);
30496
30484
  }
30497
- if (method === "POST" && path3 === "/execute") {
30485
+ if (url.pathname === "/execute" && req.method === "POST") {
30498
30486
  const body = await this.readBody(req);
30499
30487
  const paymentHeader = req.headers[PAYMENT_HEADER];
30500
- return this.handleExecute(body, paymentHeader, res);
30488
+ return await this.handleExecute(body, paymentHeader, res);
30501
30489
  }
30502
30490
  this.sendJson(res, 404, { error: "Not found" });
30503
30491
  } catch (err) {
@@ -30526,7 +30514,8 @@ var MoltsPayServer = class {
30526
30514
  x402: {
30527
30515
  version: X402_VERSION,
30528
30516
  network: `eip155:${chain2.chainId}`,
30529
- schemes: ["exact"]
30517
+ schemes: ["exact"],
30518
+ facilitator: this.facilitatorUrl
30530
30519
  }
30531
30520
  });
30532
30521
  }
@@ -30563,6 +30552,11 @@ var MoltsPayServer = class {
30563
30552
  if (!validation.valid) {
30564
30553
  return this.sendJson(res, 402, { error: validation.error });
30565
30554
  }
30555
+ console.log(`[MoltsPay] Verifying payment with facilitator...`);
30556
+ const verifyResult = await this.verifyWithFacilitator(payment, skill.config);
30557
+ if (!verifyResult.valid) {
30558
+ return this.sendJson(res, 402, { error: `Payment verification failed: ${verifyResult.error}` });
30559
+ }
30566
30560
  console.log(`[MoltsPay] Executing skill: ${service}`);
30567
30561
  let result;
30568
30562
  try {
@@ -30574,19 +30568,30 @@ var MoltsPayServer = class {
30574
30568
  message: err.message
30575
30569
  });
30576
30570
  }
30577
- console.log(`[MoltsPay] Skill succeeded, claiming payment...`);
30578
- let txHash = null;
30571
+ console.log(`[MoltsPay] Skill succeeded, settling payment...`);
30572
+ let settlement = null;
30579
30573
  try {
30580
- txHash = await this.claimPayment(payment);
30581
- console.log(`[MoltsPay] Payment claimed: ${txHash}`);
30574
+ settlement = await this.settleWithFacilitator(payment, skill.config);
30575
+ console.log(`[MoltsPay] Payment settled: ${settlement.transaction || "pending"}`);
30582
30576
  } catch (err) {
30583
- console.error("[MoltsPay] Payment claim failed:", err.message);
30577
+ console.error("[MoltsPay] Settlement failed:", err.message);
30578
+ }
30579
+ const responseHeaders = {};
30580
+ if (settlement) {
30581
+ const responsePayload = {
30582
+ success: true,
30583
+ transaction: settlement.transaction,
30584
+ network: payment.network
30585
+ };
30586
+ responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(
30587
+ JSON.stringify(responsePayload)
30588
+ ).toString("base64");
30584
30589
  }
30585
30590
  this.sendJson(res, 200, {
30586
30591
  success: true,
30587
30592
  result,
30588
- payment: txHash ? { txHash, status: "claimed" } : { status: "pending" }
30589
- });
30593
+ payment: settlement ? { transaction: settlement.transaction, status: "settled" } : { status: "pending" }
30594
+ }, responseHeaders);
30590
30595
  }
30591
30596
  /**
30592
30597
  * Return 402 with x402 payment requirements
@@ -30599,7 +30604,9 @@ var MoltsPayServer = class {
30599
30604
  network: `eip155:${chain2.chainId}`,
30600
30605
  maxAmountRequired: amountInUnits,
30601
30606
  resource: this.manifest.provider.wallet,
30602
- description: `${config.name} - $${config.price} ${config.currency}`
30607
+ description: `${config.name} - $${config.price} ${config.currency}`,
30608
+ // Include facilitator info for client
30609
+ extra: JSON.stringify({ facilitator: this.facilitatorUrl })
30603
30610
  }];
30604
30611
  const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
30605
30612
  res.writeHead(402, {
@@ -30613,7 +30620,7 @@ var MoltsPayServer = class {
30613
30620
  }, null, 2));
30614
30621
  }
30615
30622
  /**
30616
- * Validate x402 payment payload
30623
+ * Basic payment validation (before calling facilitator)
30617
30624
  */
30618
30625
  validatePayment(payment, config) {
30619
30626
  if (payment.x402Version !== X402_VERSION) {
@@ -30627,51 +30634,66 @@ var MoltsPayServer = class {
30627
30634
  if (payment.network !== expectedNetwork) {
30628
30635
  return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
30629
30636
  }
30630
- const auth = payment.payload.authorization;
30631
- if (auth.to.toLowerCase() !== this.manifest.provider.wallet.toLowerCase()) {
30632
- return { valid: false, error: "Payment recipient mismatch" };
30633
- }
30634
- const amount = Number(auth.value) / 1e6;
30635
- if (amount < config.price) {
30636
- return { valid: false, error: `Insufficient amount: $${amount} < $${config.price}` };
30637
- }
30638
- const now = Math.floor(Date.now() / 1e3);
30639
- if (Number(auth.validBefore) < now) {
30640
- return { valid: false, error: "Payment authorization expired" };
30641
- }
30642
- if (Number(auth.validAfter) > now) {
30643
- return { valid: false, error: "Payment authorization not yet valid" };
30644
- }
30645
30637
  return { valid: true };
30646
30638
  }
30647
30639
  /**
30648
- * Claim payment using transferWithAuthorization
30640
+ * Verify payment with facilitator
30649
30641
  */
30650
- async claimPayment(payment) {
30651
- if (!this.wallet || !this.provider) {
30652
- throw new Error("Wallet not configured for payment claims");
30642
+ async verifyWithFacilitator(payment, config) {
30643
+ try {
30644
+ const chain2 = getChain(this.manifest.provider.chain);
30645
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
30646
+ const requirements = {
30647
+ scheme: "exact",
30648
+ network: `eip155:${chain2.chainId}`,
30649
+ maxAmountRequired: amountInUnits,
30650
+ resource: this.manifest.provider.wallet
30651
+ };
30652
+ const response = await fetch(`${this.facilitatorUrl}/verify`, {
30653
+ method: "POST",
30654
+ headers: { "Content-Type": "application/json" },
30655
+ body: JSON.stringify({
30656
+ paymentPayload: payment,
30657
+ paymentRequirements: requirements
30658
+ })
30659
+ });
30660
+ const result = await response.json();
30661
+ if (!response.ok || !result.isValid) {
30662
+ return { valid: false, error: result.invalidReason || "Verification failed" };
30663
+ }
30664
+ return { valid: true };
30665
+ } catch (err) {
30666
+ return { valid: false, error: `Facilitator error: ${err.message}` };
30653
30667
  }
30668
+ }
30669
+ /**
30670
+ * Settle payment with facilitator (execute on-chain transfer)
30671
+ */
30672
+ async settleWithFacilitator(payment, config) {
30654
30673
  const chain2 = getChain(this.manifest.provider.chain);
30655
- const auth = payment.payload.authorization;
30656
- const sig = payment.payload.signature;
30657
- const { r, s, v } = ethers.Signature.from(sig);
30658
- const usdcAbi = [
30659
- "function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)"
30660
- ];
30661
- const usdc = new ethers.Contract(chain2.usdc, usdcAbi, this.wallet);
30662
- const tx = await usdc.transferWithAuthorization(
30663
- auth.from,
30664
- auth.to,
30665
- auth.value,
30666
- auth.validAfter,
30667
- auth.validBefore,
30668
- auth.nonce,
30669
- v,
30670
- r,
30671
- s
30672
- );
30673
- const receipt = await tx.wait();
30674
- return receipt.hash;
30674
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
30675
+ const requirements = {
30676
+ scheme: "exact",
30677
+ network: `eip155:${chain2.chainId}`,
30678
+ maxAmountRequired: amountInUnits,
30679
+ resource: this.manifest.provider.wallet
30680
+ };
30681
+ const response = await fetch(`${this.facilitatorUrl}/settle`, {
30682
+ method: "POST",
30683
+ headers: { "Content-Type": "application/json" },
30684
+ body: JSON.stringify({
30685
+ paymentPayload: payment,
30686
+ paymentRequirements: requirements
30687
+ })
30688
+ });
30689
+ const result = await response.json();
30690
+ if (!response.ok) {
30691
+ throw new Error(result.error || "Settlement failed");
30692
+ }
30693
+ return {
30694
+ transaction: result.transaction,
30695
+ status: result.status || "settled"
30696
+ };
30675
30697
  }
30676
30698
  async readBody(req) {
30677
30699
  return new Promise((resolve, reject) => {
@@ -30687,8 +30709,12 @@ var MoltsPayServer = class {
30687
30709
  req.on("error", reject);
30688
30710
  });
30689
30711
  }
30690
- sendJson(res, status, data) {
30691
- res.writeHead(status, { "Content-Type": "application/json" });
30712
+ sendJson(res, status, data, extraHeaders) {
30713
+ const headers = { "Content-Type": "application/json" };
30714
+ if (extraHeaders) {
30715
+ Object.assign(headers, extraHeaders);
30716
+ }
30717
+ res.writeHead(status, headers);
30692
30718
  res.end(JSON.stringify(data, null, 2));
30693
30719
  }
30694
30720
  };
@@ -30698,7 +30724,7 @@ init_esm_shims();
30698
30724
  import { existsSync, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
30699
30725
  import { homedir } from "os";
30700
30726
  import { join } from "path";
30701
- import { Wallet, ethers as ethers2 } from "ethers";
30727
+ import { Wallet, ethers } from "ethers";
30702
30728
 
30703
30729
  // src/client/types.ts
30704
30730
  init_esm_shims();
@@ -30847,7 +30873,7 @@ var MoltsPayClient = class {
30847
30873
  async signEIP3009(to, amount, chain2) {
30848
30874
  const validAfter = 0;
30849
30875
  const validBefore = Math.floor(Date.now() / 1e3) + 3600;
30850
- const nonce = ethers2.hexlify(ethers2.randomBytes(32));
30876
+ const nonce = ethers.hexlify(ethers.randomBytes(32));
30851
30877
  const value = BigInt(Math.floor(amount * 1e6)).toString();
30852
30878
  const authorization = {
30853
30879
  from: this.wallet.address,
@@ -30961,14 +30987,14 @@ var MoltsPayClient = class {
30961
30987
  } catch {
30962
30988
  throw new Error(`Unknown chain: ${this.config.chain}`);
30963
30989
  }
30964
- const provider = new ethers2.JsonRpcProvider(chain2.rpc);
30990
+ const provider = new ethers.JsonRpcProvider(chain2.rpc);
30965
30991
  const nativeBalance = await provider.getBalance(this.wallet.address);
30966
30992
  const usdcAbi = ["function balanceOf(address) view returns (uint256)"];
30967
- const usdc = new ethers2.Contract(chain2.usdc, usdcAbi, provider);
30993
+ const usdc = new ethers.Contract(chain2.usdc, usdcAbi, provider);
30968
30994
  const usdcBalance = await usdc.balanceOf(this.wallet.address);
30969
30995
  return {
30970
- usdc: parseFloat(ethers2.formatUnits(usdcBalance, 6)),
30971
- native: parseFloat(ethers2.formatEther(nativeBalance))
30996
+ usdc: parseFloat(ethers.formatUnits(usdcBalance, 6)),
30997
+ native: parseFloat(ethers.formatEther(nativeBalance))
30972
30998
  };
30973
30999
  }
30974
31000
  };
@@ -30978,11 +31004,11 @@ init_esm_shims();
30978
31004
 
30979
31005
  // src/wallet/Wallet.ts
30980
31006
  init_esm_shims();
30981
- import { ethers as ethers3 } from "ethers";
31007
+ import { ethers as ethers2 } from "ethers";
30982
31008
 
30983
31009
  // src/wallet/createWallet.ts
30984
31010
  init_esm_shims();
30985
- import { ethers as ethers4 } from "ethers";
31011
+ import { ethers as ethers3 } from "ethers";
30986
31012
  import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
30987
31013
  import { join as join2, dirname } from "path";
30988
31014
  import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
@@ -31027,7 +31053,7 @@ function createWallet(options = {}) {
31027
31053
  }
31028
31054
  }
31029
31055
  try {
31030
- const wallet = ethers4.Wallet.createRandom();
31056
+ const wallet = ethers3.Wallet.createRandom();
31031
31057
  const walletData = {
31032
31058
  address: wallet.address,
31033
31059
  label: options.label,
@@ -31100,8 +31126,8 @@ function walletExists(storagePath) {
31100
31126
 
31101
31127
  // src/verify/index.ts
31102
31128
  init_esm_shims();
31103
- import { ethers as ethers5 } from "ethers";
31104
- var TRANSFER_EVENT_TOPIC = ethers5.id("Transfer(address,address,uint256)");
31129
+ import { ethers as ethers4 } from "ethers";
31130
+ var TRANSFER_EVENT_TOPIC = ethers4.id("Transfer(address,address,uint256)");
31105
31131
  async function verifyPayment(params) {
31106
31132
  const { txHash, expectedAmount, expectedTo } = params;
31107
31133
  let chain2;
@@ -31118,7 +31144,7 @@ async function verifyPayment(params) {
31118
31144
  return { verified: false, error: `Unsupported chain: ${params.chain}` };
31119
31145
  }
31120
31146
  try {
31121
- const provider = new ethers5.JsonRpcProvider(chain2.rpc);
31147
+ const provider = new ethers4.JsonRpcProvider(chain2.rpc);
31122
31148
  const receipt = await provider.getTransactionReceipt(txHash);
31123
31149
  if (!receipt) {
31124
31150
  return { verified: false, error: "Transaction not found or not confirmed" };
@@ -31178,7 +31204,7 @@ async function getTransactionStatus(txHash, chain2 = "base") {
31178
31204
  return { status: "not_found" };
31179
31205
  }
31180
31206
  try {
31181
- const provider = new ethers5.JsonRpcProvider(chainConfig.rpc);
31207
+ const provider = new ethers4.JsonRpcProvider(chainConfig.rpc);
31182
31208
  const receipt = await provider.getTransactionReceipt(txHash);
31183
31209
  if (!receipt) {
31184
31210
  const tx = await provider.getTransaction(txHash);
@@ -31215,7 +31241,7 @@ async function waitForTransaction(txHash, chain2 = "base", confirmations = 1, ti
31215
31241
  } catch (e) {
31216
31242
  return { verified: false, confirmed: false, error: `Unsupported chain: ${chain2}` };
31217
31243
  }
31218
- const provider = new ethers5.JsonRpcProvider(chainConfig.rpc);
31244
+ const provider = new ethers4.JsonRpcProvider(chainConfig.rpc);
31219
31245
  try {
31220
31246
  const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
31221
31247
  if (!receipt) {
@@ -31356,9 +31382,9 @@ var CDPWallet = class {
31356
31382
  * Get USDC balance
31357
31383
  */
31358
31384
  async getBalance() {
31359
- const { ethers: ethers6 } = await import("ethers");
31360
- const provider = new ethers6.JsonRpcProvider(this.chainConfig.rpc);
31361
- const usdcContract = new ethers6.Contract(
31385
+ const { ethers: ethers5 } = await import("ethers");
31386
+ const provider = new ethers5.JsonRpcProvider(this.chainConfig.rpc);
31387
+ const usdcContract = new ethers5.Contract(
31362
31388
  this.chainConfig.usdc,
31363
31389
  ["function balanceOf(address) view returns (uint256)"],
31364
31390
  provider
@@ -31369,7 +31395,7 @@ var CDPWallet = class {
31369
31395
  ]);
31370
31396
  return {
31371
31397
  usdc: (Number(usdcBalance) / 1e6).toFixed(2),
31372
- eth: ethers6.formatEther(ethBalance)
31398
+ eth: ethers5.formatEther(ethBalance)
31373
31399
  };
31374
31400
  }
31375
31401
  /**
@@ -31387,7 +31413,7 @@ var CDPWallet = class {
31387
31413
  }
31388
31414
  try {
31389
31415
  const { CdpClient } = await import("@coinbase/cdp-sdk");
31390
- const { ethers: ethers6 } = await import("ethers");
31416
+ const { ethers: ethers5 } = await import("ethers");
31391
31417
  const cdp = new CdpClient({
31392
31418
  apiKeyId: creds.apiKeyId,
31393
31419
  apiKeySecret: creds.apiKeySecret,
@@ -31395,7 +31421,7 @@ var CDPWallet = class {
31395
31421
  });
31396
31422
  const account = await cdp.evm.getAccount({ address: this.address });
31397
31423
  const amountWei = BigInt(Math.floor(params.amount * 1e6));
31398
- const iface = new ethers6.Interface([
31424
+ const iface = new ethers5.Interface([
31399
31425
  "function transfer(address to, uint256 amount) returns (bool)"
31400
31426
  ]);
31401
31427
  const callData = iface.encodeFunctionData("transfer", [params.to, amountWei]);