moltspay 0.8.0 → 0.8.2

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.js CHANGED
@@ -2905,7 +2905,7 @@ function alphabet(letters) {
2905
2905
  };
2906
2906
  }
2907
2907
  // @__NO_SIDE_EFFECTS__
2908
- function join3(separator = "") {
2908
+ function join4(separator = "") {
2909
2909
  astr("join", separator);
2910
2910
  return {
2911
2911
  encode: (from) => {
@@ -3112,10 +3112,10 @@ var init_esm = __esm({
3112
3112
  convertRadix2,
3113
3113
  radix,
3114
3114
  radix2,
3115
- join: join3,
3115
+ join: join4,
3116
3116
  padding
3117
3117
  };
3118
- genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain(/* @__PURE__ */ radix(58), /* @__PURE__ */ alphabet(abc), /* @__PURE__ */ join3(""));
3118
+ genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => /* @__PURE__ */ chain(/* @__PURE__ */ radix(58), /* @__PURE__ */ alphabet(abc), /* @__PURE__ */ join4(""));
3119
3119
  base58 = /* @__PURE__ */ genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
3120
3120
  createBase58check = (sha2566) => /* @__PURE__ */ chain(checksum(4, (data) => sha2566(sha2566(data))), base58);
3121
3121
  }
@@ -3263,14 +3263,14 @@ var init_esm2 = __esm({
3263
3263
  }
3264
3264
  this.pubHash = hash160(this.pubKey);
3265
3265
  }
3266
- derive(path2) {
3267
- if (!/^[mM]'?/.test(path2)) {
3266
+ derive(path3) {
3267
+ if (!/^[mM]'?/.test(path3)) {
3268
3268
  throw new Error('Path must start with "m" or "M"');
3269
3269
  }
3270
- if (/^[mM]'?$/.test(path2)) {
3270
+ if (/^[mM]'?$/.test(path3)) {
3271
3271
  return this;
3272
3272
  }
3273
- const parts = path2.replace(/^[mM]'?\//, "").split("/");
3273
+ const parts = path3.replace(/^[mM]'?\//, "").split("/");
3274
3274
  let child = this;
3275
3275
  for (const c of parts) {
3276
3276
  const m = /^(\d+)('?)$/.exec(c);
@@ -9631,8 +9631,8 @@ var init_privateKeyToAccount = __esm({
9631
9631
  });
9632
9632
 
9633
9633
  // node_modules/viem/_esm/accounts/hdKeyToAccount.js
9634
- function hdKeyToAccount(hdKey_, { accountIndex = 0, addressIndex = 0, changeIndex = 0, path: path2, ...options } = {}) {
9635
- const hdKey = hdKey_.derive(path2 || `m/44'/60'/${accountIndex}'/${changeIndex}/${addressIndex}`);
9634
+ function hdKeyToAccount(hdKey_, { accountIndex = 0, addressIndex = 0, changeIndex = 0, path: path3, ...options } = {}) {
9635
+ const hdKey = hdKey_.derive(path3 || `m/44'/60'/${accountIndex}'/${changeIndex}/${addressIndex}`);
9636
9636
  const account = privateKeyToAccount(toHex(hdKey.privateKey), options);
9637
9637
  return {
9638
9638
  ...account,
@@ -30369,85 +30369,7 @@ init_cjs_shims();
30369
30369
  init_cjs_shims();
30370
30370
  var import_fs = require("fs");
30371
30371
  var import_http = require("http");
30372
- var import_ethers = require("ethers");
30373
-
30374
- // src/chains/index.ts
30375
- init_cjs_shims();
30376
- var CHAINS = {
30377
- // ============ Mainnet ============
30378
- base: {
30379
- name: "Base",
30380
- chainId: 8453,
30381
- rpc: "https://mainnet.base.org",
30382
- usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
30383
- explorer: "https://basescan.org/address/",
30384
- explorerTx: "https://basescan.org/tx/",
30385
- avgBlockTime: 2
30386
- },
30387
- polygon: {
30388
- name: "Polygon",
30389
- chainId: 137,
30390
- rpc: "https://polygon-rpc.com",
30391
- usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
30392
- explorer: "https://polygonscan.com/address/",
30393
- explorerTx: "https://polygonscan.com/tx/",
30394
- avgBlockTime: 2
30395
- },
30396
- ethereum: {
30397
- name: "Ethereum",
30398
- chainId: 1,
30399
- rpc: "https://eth.llamarpc.com",
30400
- usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
30401
- explorer: "https://etherscan.io/address/",
30402
- explorerTx: "https://etherscan.io/tx/",
30403
- avgBlockTime: 12
30404
- },
30405
- // ============ Testnet ============
30406
- base_sepolia: {
30407
- name: "Base Sepolia",
30408
- chainId: 84532,
30409
- rpc: "https://sepolia.base.org",
30410
- usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
30411
- explorer: "https://sepolia.basescan.org/address/",
30412
- explorerTx: "https://sepolia.basescan.org/tx/",
30413
- avgBlockTime: 2
30414
- },
30415
- sepolia: {
30416
- name: "Sepolia",
30417
- chainId: 11155111,
30418
- rpc: "https://rpc.sepolia.org",
30419
- usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
30420
- explorer: "https://sepolia.etherscan.io/address/",
30421
- explorerTx: "https://sepolia.etherscan.io/tx/",
30422
- avgBlockTime: 12
30423
- }
30424
- };
30425
- function getChain(name) {
30426
- const config = CHAINS[name];
30427
- if (!config) {
30428
- throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(", ")}`);
30429
- }
30430
- return config;
30431
- }
30432
- function listChains() {
30433
- return Object.keys(CHAINS);
30434
- }
30435
- function getChainById(chainId) {
30436
- return Object.values(CHAINS).find((c) => c.chainId === chainId);
30437
- }
30438
- var ERC20_ABI = [
30439
- "function balanceOf(address owner) view returns (uint256)",
30440
- "function transfer(address to, uint256 amount) returns (bool)",
30441
- "function approve(address spender, uint256 amount) returns (bool)",
30442
- "function allowance(address owner, address spender) view returns (uint256)",
30443
- "function decimals() view returns (uint8)",
30444
- "function symbol() view returns (string)",
30445
- "function name() view returns (string)",
30446
- "function nonces(address owner) view returns (uint256)",
30447
- "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
30448
- "event Transfer(address indexed from, address indexed to, uint256 value)",
30449
- "event Approval(address indexed owner, address indexed spender, uint256 value)"
30450
- ];
30372
+ var path = __toESM(require("path"));
30451
30373
 
30452
30374
  // src/server/types.ts
30453
30375
  init_cjs_shims();
@@ -30456,34 +30378,92 @@ init_cjs_shims();
30456
30378
  var X402_VERSION = 2;
30457
30379
  var PAYMENT_REQUIRED_HEADER = "x-payment-required";
30458
30380
  var PAYMENT_HEADER = "x-payment";
30381
+ var PAYMENT_RESPONSE_HEADER = "x-payment-response";
30382
+ var FACILITATOR_TESTNET = "https://www.x402.org/facilitator";
30383
+ var FACILITATOR_MAINNET = "https://api.cdp.coinbase.com/platform/v2/x402";
30384
+ function loadEnvFiles() {
30385
+ try {
30386
+ const dotenv = require("dotenv");
30387
+ const envPaths = [
30388
+ path.join(process.cwd(), ".env"),
30389
+ path.join(process.env.HOME || "", ".moltspay", ".env")
30390
+ ];
30391
+ for (const envPath of envPaths) {
30392
+ if ((0, import_fs.existsSync)(envPath)) {
30393
+ dotenv.config({ path: envPath });
30394
+ console.log(`[MoltsPay] Loaded config from ${envPath}`);
30395
+ break;
30396
+ }
30397
+ }
30398
+ } catch {
30399
+ }
30400
+ }
30401
+ function getCDPConfig() {
30402
+ loadEnvFiles();
30403
+ return {
30404
+ useMainnet: process.env.USE_MAINNET?.toLowerCase() === "true",
30405
+ apiKeyId: process.env.CDP_API_KEY_ID,
30406
+ apiKeySecret: process.env.CDP_API_KEY_SECRET
30407
+ };
30408
+ }
30409
+ async function getCDPAuthHeaders(method, urlPath, body) {
30410
+ const config = getCDPConfig();
30411
+ if (!config.apiKeyId || !config.apiKeySecret) {
30412
+ throw new Error("CDP_API_KEY_ID and CDP_API_KEY_SECRET required for mainnet");
30413
+ }
30414
+ try {
30415
+ const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
30416
+ const headers = await getAuthHeaders({
30417
+ apiKeyId: config.apiKeyId,
30418
+ apiKeySecret: config.apiKeySecret,
30419
+ requestMethod: method,
30420
+ requestHost: "api.cdp.coinbase.com",
30421
+ requestPath: urlPath,
30422
+ requestBody: body
30423
+ });
30424
+ return headers;
30425
+ } catch (err) {
30426
+ console.error("[MoltsPay] Failed to generate CDP auth headers:", err.message);
30427
+ throw err;
30428
+ }
30429
+ }
30459
30430
  var MoltsPayServer = class {
30460
30431
  manifest;
30461
30432
  skills = /* @__PURE__ */ new Map();
30462
30433
  options;
30463
- provider = null;
30464
- wallet = null;
30434
+ cdpConfig;
30435
+ facilitatorUrl;
30436
+ networkId;
30465
30437
  constructor(servicesPath, options = {}) {
30438
+ this.cdpConfig = getCDPConfig();
30466
30439
  const content = (0, import_fs.readFileSync)(servicesPath, "utf-8");
30467
30440
  this.manifest = JSON.parse(content);
30468
30441
  this.options = {
30469
30442
  port: options.port || 3e3,
30470
- host: options.host || "0.0.0.0",
30471
- privateKey: options.privateKey || process.env.MOLTSPAY_PRIVATE_KEY
30443
+ host: options.host || "0.0.0.0"
30472
30444
  };
30473
- if (this.options.privateKey) {
30474
- try {
30475
- const chain2 = getChain(this.manifest.provider.chain);
30476
- this.provider = new import_ethers.ethers.JsonRpcProvider(chain2.rpc);
30477
- this.wallet = new import_ethers.ethers.Wallet(this.options.privateKey, this.provider);
30478
- console.log(`[MoltsPay] Payment wallet: ${this.wallet.address}`);
30479
- } catch (err) {
30480
- console.warn("[MoltsPay] Warning: Could not initialize wallet for payment claims");
30445
+ if (this.cdpConfig.useMainnet) {
30446
+ if (!this.cdpConfig.apiKeyId || !this.cdpConfig.apiKeySecret) {
30447
+ console.warn("[MoltsPay] WARNING: USE_MAINNET=true but CDP keys not set!");
30448
+ console.warn("[MoltsPay] Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in ~/.moltspay/.env");
30481
30449
  }
30450
+ this.facilitatorUrl = FACILITATOR_MAINNET;
30451
+ this.networkId = "eip155:8453";
30452
+ } else {
30453
+ this.facilitatorUrl = options.facilitatorUrl || FACILITATOR_TESTNET;
30454
+ this.networkId = "eip155:84532";
30482
30455
  }
30456
+ const networkName = this.cdpConfig.useMainnet ? "Base mainnet" : "Base Sepolia (testnet)";
30457
+ const facilitatorName = this.cdpConfig.useMainnet ? "CDP" : "x402.org";
30483
30458
  console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
30484
30459
  console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
30485
30460
  console.log(`[MoltsPay] Receive wallet: ${this.manifest.provider.wallet}`);
30486
- console.log(`[MoltsPay] Protocol: x402 (gasless, pay-for-success)`);
30461
+ console.log(`[MoltsPay] Network: ${this.networkId} (${networkName})`);
30462
+ console.log(`[MoltsPay] Facilitator: ${facilitatorName} (${this.facilitatorUrl})`);
30463
+ if (this.cdpConfig.useMainnet && this.cdpConfig.apiKeyId) {
30464
+ console.log(`[MoltsPay] CDP API Key: ${this.cdpConfig.apiKeyId.slice(0, 8)}...`);
30465
+ }
30466
+ console.log(`[MoltsPay] Protocol: x402 (gasless for both client AND server)`);
30487
30467
  }
30488
30468
  /**
30489
30469
  * Register a skill handler for a service
@@ -30493,48 +30473,45 @@ var MoltsPayServer = class {
30493
30473
  if (!config) {
30494
30474
  throw new Error(`Service '${serviceId}' not found in manifest`);
30495
30475
  }
30496
- this.skills.set(serviceId, {
30497
- id: serviceId,
30498
- config,
30499
- handler
30500
- });
30501
- console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
30476
+ this.skills.set(serviceId, { id: serviceId, config, handler });
30502
30477
  return this;
30503
30478
  }
30504
30479
  /**
30505
- * Start the server
30480
+ * Start HTTP server
30506
30481
  */
30507
30482
  listen(port) {
30508
- const p = port || this.options.port;
30483
+ const p = port || this.options.port || 3e3;
30484
+ const host = this.options.host || "0.0.0.0";
30509
30485
  const server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
30510
- server.listen(p, this.options.host, () => {
30511
- console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);
30486
+ server.listen(p, host, () => {
30487
+ console.log(`[MoltsPay] Server listening on http://${host}:${p}`);
30512
30488
  console.log(`[MoltsPay] Endpoints:`);
30513
30489
  console.log(` GET /services - List available services`);
30514
30490
  console.log(` POST /execute - Execute service (x402 payment)`);
30515
30491
  });
30516
30492
  }
30493
+ /**
30494
+ * Handle incoming request
30495
+ */
30517
30496
  async handleRequest(req, res) {
30518
- const url = new URL(req.url || "/", `http://${req.headers.host}`);
30519
- const path2 = url.pathname;
30520
- const method = req.method || "GET";
30521
30497
  res.setHeader("Access-Control-Allow-Origin", "*");
30522
30498
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
30523
30499
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment");
30524
30500
  res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response");
30525
- if (method === "OPTIONS") {
30501
+ if (req.method === "OPTIONS") {
30526
30502
  res.writeHead(204);
30527
30503
  res.end();
30528
30504
  return;
30529
30505
  }
30530
30506
  try {
30531
- if (method === "GET" && path2 === "/services") {
30507
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
30508
+ if (url.pathname === "/services" && req.method === "GET") {
30532
30509
  return this.handleGetServices(res);
30533
30510
  }
30534
- if (method === "POST" && path2 === "/execute") {
30511
+ if (url.pathname === "/execute" && req.method === "POST") {
30535
30512
  const body = await this.readBody(req);
30536
30513
  const paymentHeader = req.headers[PAYMENT_HEADER];
30537
- return this.handleExecute(body, paymentHeader, res);
30514
+ return await this.handleExecute(body, paymentHeader, res);
30538
30515
  }
30539
30516
  this.sendJson(res, 404, { error: "Not found" });
30540
30517
  } catch (err) {
@@ -30546,7 +30523,6 @@ var MoltsPayServer = class {
30546
30523
  * GET /services - List available services
30547
30524
  */
30548
30525
  handleGetServices(res) {
30549
- const chain2 = getChain(this.manifest.provider.chain);
30550
30526
  const services = this.manifest.services.map((s) => ({
30551
30527
  id: s.id,
30552
30528
  name: s.name,
@@ -30562,15 +30538,15 @@ var MoltsPayServer = class {
30562
30538
  services,
30563
30539
  x402: {
30564
30540
  version: X402_VERSION,
30565
- network: `eip155:${chain2.chainId}`,
30566
- schemes: ["exact"]
30541
+ network: this.networkId,
30542
+ schemes: ["exact"],
30543
+ facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
30544
+ mainnet: this.cdpConfig.useMainnet
30567
30545
  }
30568
30546
  });
30569
30547
  }
30570
30548
  /**
30571
30549
  * POST /execute - Execute service with x402 payment
30572
- * Body: { service: string, params: object }
30573
- * Header: X-Payment (optional - if missing, returns 402)
30574
30550
  */
30575
30551
  async handleExecute(body, paymentHeader, res) {
30576
30552
  const { service, params } = body;
@@ -30600,6 +30576,11 @@ var MoltsPayServer = class {
30600
30576
  if (!validation.valid) {
30601
30577
  return this.sendJson(res, 402, { error: validation.error });
30602
30578
  }
30579
+ console.log(`[MoltsPay] Verifying payment with facilitator...`);
30580
+ const verifyResult = await this.verifyWithFacilitator(payment, skill.config);
30581
+ if (!verifyResult.valid) {
30582
+ return this.sendJson(res, 402, { error: `Payment verification failed: ${verifyResult.error}` });
30583
+ }
30603
30584
  console.log(`[MoltsPay] Executing skill: ${service}`);
30604
30585
  let result;
30605
30586
  try {
@@ -30611,32 +30592,46 @@ var MoltsPayServer = class {
30611
30592
  message: err.message
30612
30593
  });
30613
30594
  }
30614
- console.log(`[MoltsPay] Skill succeeded, claiming payment...`);
30615
- let txHash = null;
30595
+ console.log(`[MoltsPay] Skill succeeded, settling payment...`);
30596
+ let settlement = null;
30616
30597
  try {
30617
- txHash = await this.claimPayment(payment);
30618
- console.log(`[MoltsPay] Payment claimed: ${txHash}`);
30598
+ settlement = await this.settleWithFacilitator(payment, skill.config);
30599
+ console.log(`[MoltsPay] Payment settled: ${settlement.transaction || "pending"}`);
30619
30600
  } catch (err) {
30620
- console.error("[MoltsPay] Payment claim failed:", err.message);
30601
+ console.error("[MoltsPay] Settlement failed:", err.message);
30602
+ }
30603
+ const responseHeaders = {};
30604
+ if (settlement) {
30605
+ const responsePayload = {
30606
+ success: true,
30607
+ transaction: settlement.transaction,
30608
+ network: payment.network
30609
+ };
30610
+ responseHeaders[PAYMENT_RESPONSE_HEADER] = Buffer.from(
30611
+ JSON.stringify(responsePayload)
30612
+ ).toString("base64");
30621
30613
  }
30622
30614
  this.sendJson(res, 200, {
30623
30615
  success: true,
30624
30616
  result,
30625
- payment: txHash ? { txHash, status: "claimed" } : { status: "pending" }
30626
- });
30617
+ payment: settlement ? { transaction: settlement.transaction, status: "settled" } : { status: "pending" }
30618
+ }, responseHeaders);
30627
30619
  }
30628
30620
  /**
30629
30621
  * Return 402 with x402 payment requirements
30630
30622
  */
30631
30623
  sendPaymentRequired(config, res) {
30632
- const chain2 = getChain(this.manifest.provider.chain);
30633
30624
  const amountInUnits = Math.floor(config.price * 1e6).toString();
30634
30625
  const requirements = [{
30635
30626
  scheme: "exact",
30636
- network: `eip155:${chain2.chainId}`,
30627
+ network: this.networkId,
30637
30628
  maxAmountRequired: amountInUnits,
30638
30629
  resource: this.manifest.provider.wallet,
30639
- description: `${config.name} - $${config.price} ${config.currency}`
30630
+ description: `${config.name} - $${config.price} ${config.currency}`,
30631
+ extra: JSON.stringify({
30632
+ facilitator: this.cdpConfig.useMainnet ? "cdp" : "x402.org",
30633
+ mainnet: this.cdpConfig.useMainnet
30634
+ })
30640
30635
  }];
30641
30636
  const encoded = Buffer.from(JSON.stringify(requirements)).toString("base64");
30642
30637
  res.writeHead(402, {
@@ -30650,7 +30645,7 @@ var MoltsPayServer = class {
30650
30645
  }, null, 2));
30651
30646
  }
30652
30647
  /**
30653
- * Validate x402 payment payload
30648
+ * Basic payment validation
30654
30649
  */
30655
30650
  validatePayment(payment, config) {
30656
30651
  if (payment.x402Version !== X402_VERSION) {
@@ -30659,56 +30654,89 @@ var MoltsPayServer = class {
30659
30654
  if (payment.scheme !== "exact") {
30660
30655
  return { valid: false, error: `Unsupported scheme: ${payment.scheme}` };
30661
30656
  }
30662
- const chain2 = getChain(this.manifest.provider.chain);
30663
- const expectedNetwork = `eip155:${chain2.chainId}`;
30664
- if (payment.network !== expectedNetwork) {
30665
- return { valid: false, error: `Network mismatch: expected ${expectedNetwork}` };
30666
- }
30667
- const auth = payment.payload.authorization;
30668
- if (auth.to.toLowerCase() !== this.manifest.provider.wallet.toLowerCase()) {
30669
- return { valid: false, error: "Payment recipient mismatch" };
30670
- }
30671
- const amount = Number(auth.value) / 1e6;
30672
- if (amount < config.price) {
30673
- return { valid: false, error: `Insufficient amount: $${amount} < $${config.price}` };
30674
- }
30675
- const now = Math.floor(Date.now() / 1e3);
30676
- if (Number(auth.validBefore) < now) {
30677
- return { valid: false, error: "Payment authorization expired" };
30678
- }
30679
- if (Number(auth.validAfter) > now) {
30680
- return { valid: false, error: "Payment authorization not yet valid" };
30657
+ if (payment.network !== this.networkId) {
30658
+ return { valid: false, error: `Network mismatch: expected ${this.networkId}, got ${payment.network}` };
30681
30659
  }
30682
30660
  return { valid: true };
30683
30661
  }
30684
30662
  /**
30685
- * Claim payment using transferWithAuthorization
30663
+ * Verify payment with facilitator (testnet or CDP)
30686
30664
  */
30687
- async claimPayment(payment) {
30688
- if (!this.wallet || !this.provider) {
30689
- throw new Error("Wallet not configured for payment claims");
30665
+ async verifyWithFacilitator(payment, config) {
30666
+ try {
30667
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
30668
+ const requirements = {
30669
+ scheme: "exact",
30670
+ network: this.networkId,
30671
+ maxAmountRequired: amountInUnits,
30672
+ resource: this.manifest.provider.wallet,
30673
+ payTo: this.manifest.provider.wallet
30674
+ };
30675
+ const requestBody = {
30676
+ paymentPayload: payment,
30677
+ paymentRequirements: requirements
30678
+ };
30679
+ let headers = { "Content-Type": "application/json" };
30680
+ if (this.cdpConfig.useMainnet) {
30681
+ const authHeaders = await getCDPAuthHeaders(
30682
+ "POST",
30683
+ "/platform/v2/x402/verify",
30684
+ requestBody
30685
+ );
30686
+ headers = { ...headers, ...authHeaders };
30687
+ }
30688
+ const response = await fetch(`${this.facilitatorUrl}/verify`, {
30689
+ method: "POST",
30690
+ headers,
30691
+ body: JSON.stringify(requestBody)
30692
+ });
30693
+ const result = await response.json();
30694
+ if (!response.ok || !result.isValid) {
30695
+ return { valid: false, error: result.invalidReason || result.error || "Verification failed" };
30696
+ }
30697
+ return { valid: true };
30698
+ } catch (err) {
30699
+ return { valid: false, error: `Facilitator error: ${err.message}` };
30690
30700
  }
30691
- const chain2 = getChain(this.manifest.provider.chain);
30692
- const auth = payment.payload.authorization;
30693
- const sig = payment.payload.signature;
30694
- const { r, s, v } = import_ethers.ethers.Signature.from(sig);
30695
- const usdcAbi = [
30696
- "function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)"
30697
- ];
30698
- const usdc = new import_ethers.ethers.Contract(chain2.usdc, usdcAbi, this.wallet);
30699
- const tx = await usdc.transferWithAuthorization(
30700
- auth.from,
30701
- auth.to,
30702
- auth.value,
30703
- auth.validAfter,
30704
- auth.validBefore,
30705
- auth.nonce,
30706
- v,
30707
- r,
30708
- s
30709
- );
30710
- const receipt = await tx.wait();
30711
- return receipt.hash;
30701
+ }
30702
+ /**
30703
+ * Settle payment with facilitator (execute on-chain transfer)
30704
+ */
30705
+ async settleWithFacilitator(payment, config) {
30706
+ const amountInUnits = Math.floor(config.price * 1e6).toString();
30707
+ const requirements = {
30708
+ scheme: "exact",
30709
+ network: this.networkId,
30710
+ maxAmountRequired: amountInUnits,
30711
+ resource: this.manifest.provider.wallet,
30712
+ payTo: this.manifest.provider.wallet
30713
+ };
30714
+ const requestBody = {
30715
+ paymentPayload: payment,
30716
+ paymentRequirements: requirements
30717
+ };
30718
+ let headers = { "Content-Type": "application/json" };
30719
+ if (this.cdpConfig.useMainnet) {
30720
+ const authHeaders = await getCDPAuthHeaders(
30721
+ "POST",
30722
+ "/platform/v2/x402/settle",
30723
+ requestBody
30724
+ );
30725
+ headers = { ...headers, ...authHeaders };
30726
+ }
30727
+ const response = await fetch(`${this.facilitatorUrl}/settle`, {
30728
+ method: "POST",
30729
+ headers,
30730
+ body: JSON.stringify(requestBody)
30731
+ });
30732
+ const result = await response.json();
30733
+ if (!response.ok || !result.success) {
30734
+ throw new Error(result.error || result.errorReason || "Settlement failed");
30735
+ }
30736
+ return {
30737
+ transaction: result.transaction,
30738
+ status: result.status || "settled"
30739
+ };
30712
30740
  }
30713
30741
  async readBody(req) {
30714
30742
  return new Promise((resolve, reject) => {
@@ -30724,8 +30752,12 @@ var MoltsPayServer = class {
30724
30752
  req.on("error", reject);
30725
30753
  });
30726
30754
  }
30727
- sendJson(res, status, data) {
30728
- res.writeHead(status, { "Content-Type": "application/json" });
30755
+ sendJson(res, status, data, extraHeaders) {
30756
+ const headers = { "Content-Type": "application/json" };
30757
+ if (extraHeaders) {
30758
+ Object.assign(headers, extraHeaders);
30759
+ }
30760
+ res.writeHead(status, headers);
30729
30761
  res.end(JSON.stringify(data, null, 2));
30730
30762
  }
30731
30763
  };
@@ -30735,7 +30767,85 @@ init_cjs_shims();
30735
30767
  var import_fs2 = require("fs");
30736
30768
  var import_os = require("os");
30737
30769
  var import_path = require("path");
30738
- var import_ethers2 = require("ethers");
30770
+ var import_ethers = require("ethers");
30771
+
30772
+ // src/chains/index.ts
30773
+ init_cjs_shims();
30774
+ var CHAINS = {
30775
+ // ============ Mainnet ============
30776
+ base: {
30777
+ name: "Base",
30778
+ chainId: 8453,
30779
+ rpc: "https://mainnet.base.org",
30780
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
30781
+ explorer: "https://basescan.org/address/",
30782
+ explorerTx: "https://basescan.org/tx/",
30783
+ avgBlockTime: 2
30784
+ },
30785
+ polygon: {
30786
+ name: "Polygon",
30787
+ chainId: 137,
30788
+ rpc: "https://polygon-rpc.com",
30789
+ usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
30790
+ explorer: "https://polygonscan.com/address/",
30791
+ explorerTx: "https://polygonscan.com/tx/",
30792
+ avgBlockTime: 2
30793
+ },
30794
+ ethereum: {
30795
+ name: "Ethereum",
30796
+ chainId: 1,
30797
+ rpc: "https://eth.llamarpc.com",
30798
+ usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
30799
+ explorer: "https://etherscan.io/address/",
30800
+ explorerTx: "https://etherscan.io/tx/",
30801
+ avgBlockTime: 12
30802
+ },
30803
+ // ============ Testnet ============
30804
+ base_sepolia: {
30805
+ name: "Base Sepolia",
30806
+ chainId: 84532,
30807
+ rpc: "https://sepolia.base.org",
30808
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
30809
+ explorer: "https://sepolia.basescan.org/address/",
30810
+ explorerTx: "https://sepolia.basescan.org/tx/",
30811
+ avgBlockTime: 2
30812
+ },
30813
+ sepolia: {
30814
+ name: "Sepolia",
30815
+ chainId: 11155111,
30816
+ rpc: "https://rpc.sepolia.org",
30817
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
30818
+ explorer: "https://sepolia.etherscan.io/address/",
30819
+ explorerTx: "https://sepolia.etherscan.io/tx/",
30820
+ avgBlockTime: 12
30821
+ }
30822
+ };
30823
+ function getChain(name) {
30824
+ const config = CHAINS[name];
30825
+ if (!config) {
30826
+ throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(", ")}`);
30827
+ }
30828
+ return config;
30829
+ }
30830
+ function listChains() {
30831
+ return Object.keys(CHAINS);
30832
+ }
30833
+ function getChainById(chainId) {
30834
+ return Object.values(CHAINS).find((c) => c.chainId === chainId);
30835
+ }
30836
+ var ERC20_ABI = [
30837
+ "function balanceOf(address owner) view returns (uint256)",
30838
+ "function transfer(address to, uint256 amount) returns (bool)",
30839
+ "function approve(address spender, uint256 amount) returns (bool)",
30840
+ "function allowance(address owner, address spender) view returns (uint256)",
30841
+ "function decimals() view returns (uint8)",
30842
+ "function symbol() view returns (string)",
30843
+ "function name() view returns (string)",
30844
+ "function nonces(address owner) view returns (uint256)",
30845
+ "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
30846
+ "event Transfer(address indexed from, address indexed to, uint256 value)",
30847
+ "event Approval(address indexed owner, address indexed spender, uint256 value)"
30848
+ ];
30739
30849
 
30740
30850
  // src/client/types.ts
30741
30851
  init_cjs_shims();
@@ -30763,7 +30873,7 @@ var MoltsPayClient = class {
30763
30873
  this.config = this.loadConfig();
30764
30874
  this.walletData = this.loadWallet();
30765
30875
  if (this.walletData) {
30766
- this.wallet = new import_ethers2.Wallet(this.walletData.privateKey);
30876
+ this.wallet = new import_ethers.Wallet(this.walletData.privateKey);
30767
30877
  }
30768
30878
  }
30769
30879
  /**
@@ -30884,7 +30994,7 @@ var MoltsPayClient = class {
30884
30994
  async signEIP3009(to, amount, chain2) {
30885
30995
  const validAfter = 0;
30886
30996
  const validBefore = Math.floor(Date.now() / 1e3) + 3600;
30887
- const nonce = import_ethers2.ethers.hexlify(import_ethers2.ethers.randomBytes(32));
30997
+ const nonce = import_ethers.ethers.hexlify(import_ethers.ethers.randomBytes(32));
30888
30998
  const value = BigInt(Math.floor(amount * 1e6)).toString();
30889
30999
  const authorization = {
30890
31000
  from: this.wallet.address,
@@ -30966,7 +31076,7 @@ var MoltsPayClient = class {
30966
31076
  */
30967
31077
  static init(configDir, options) {
30968
31078
  (0, import_fs2.mkdirSync)(configDir, { recursive: true });
30969
- const wallet = import_ethers2.Wallet.createRandom();
31079
+ const wallet = import_ethers.Wallet.createRandom();
30970
31080
  const walletData = {
30971
31081
  address: wallet.address,
30972
31082
  privateKey: wallet.privateKey,
@@ -30998,14 +31108,14 @@ var MoltsPayClient = class {
30998
31108
  } catch {
30999
31109
  throw new Error(`Unknown chain: ${this.config.chain}`);
31000
31110
  }
31001
- const provider = new import_ethers2.ethers.JsonRpcProvider(chain2.rpc);
31111
+ const provider = new import_ethers.ethers.JsonRpcProvider(chain2.rpc);
31002
31112
  const nativeBalance = await provider.getBalance(this.wallet.address);
31003
31113
  const usdcAbi = ["function balanceOf(address) view returns (uint256)"];
31004
- const usdc = new import_ethers2.ethers.Contract(chain2.usdc, usdcAbi, provider);
31114
+ const usdc = new import_ethers.ethers.Contract(chain2.usdc, usdcAbi, provider);
31005
31115
  const usdcBalance = await usdc.balanceOf(this.wallet.address);
31006
31116
  return {
31007
- usdc: parseFloat(import_ethers2.ethers.formatUnits(usdcBalance, 6)),
31008
- native: parseFloat(import_ethers2.ethers.formatEther(nativeBalance))
31117
+ usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, 6)),
31118
+ native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
31009
31119
  };
31010
31120
  }
31011
31121
  };
@@ -31015,11 +31125,11 @@ init_cjs_shims();
31015
31125
 
31016
31126
  // src/wallet/Wallet.ts
31017
31127
  init_cjs_shims();
31018
- var import_ethers3 = require("ethers");
31128
+ var import_ethers2 = require("ethers");
31019
31129
 
31020
31130
  // src/wallet/createWallet.ts
31021
31131
  init_cjs_shims();
31022
- var import_ethers4 = require("ethers");
31132
+ var import_ethers3 = require("ethers");
31023
31133
  var import_fs3 = require("fs");
31024
31134
  var import_path2 = require("path");
31025
31135
  var import_crypto = require("crypto");
@@ -31064,7 +31174,7 @@ function createWallet(options = {}) {
31064
31174
  }
31065
31175
  }
31066
31176
  try {
31067
- const wallet = import_ethers4.ethers.Wallet.createRandom();
31177
+ const wallet = import_ethers3.ethers.Wallet.createRandom();
31068
31178
  const walletData = {
31069
31179
  address: wallet.address,
31070
31180
  label: options.label,
@@ -31119,26 +31229,26 @@ function loadWallet(options = {}) {
31119
31229
  }
31120
31230
  }
31121
31231
  function getWalletAddress(storagePath) {
31122
- const path2 = storagePath || (0, import_path2.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
31123
- if (!(0, import_fs3.existsSync)(path2)) {
31232
+ const path3 = storagePath || (0, import_path2.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
31233
+ if (!(0, import_fs3.existsSync)(path3)) {
31124
31234
  return null;
31125
31235
  }
31126
31236
  try {
31127
- const data = JSON.parse((0, import_fs3.readFileSync)(path2, "utf8"));
31237
+ const data = JSON.parse((0, import_fs3.readFileSync)(path3, "utf8"));
31128
31238
  return data.address;
31129
31239
  } catch {
31130
31240
  return null;
31131
31241
  }
31132
31242
  }
31133
31243
  function walletExists(storagePath) {
31134
- const path2 = storagePath || (0, import_path2.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
31135
- return (0, import_fs3.existsSync)(path2);
31244
+ const path3 = storagePath || (0, import_path2.join)(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
31245
+ return (0, import_fs3.existsSync)(path3);
31136
31246
  }
31137
31247
 
31138
31248
  // src/verify/index.ts
31139
31249
  init_cjs_shims();
31140
- var import_ethers5 = require("ethers");
31141
- var TRANSFER_EVENT_TOPIC = import_ethers5.ethers.id("Transfer(address,address,uint256)");
31250
+ var import_ethers4 = require("ethers");
31251
+ var TRANSFER_EVENT_TOPIC = import_ethers4.ethers.id("Transfer(address,address,uint256)");
31142
31252
  async function verifyPayment(params) {
31143
31253
  const { txHash, expectedAmount, expectedTo } = params;
31144
31254
  let chain2;
@@ -31155,7 +31265,7 @@ async function verifyPayment(params) {
31155
31265
  return { verified: false, error: `Unsupported chain: ${params.chain}` };
31156
31266
  }
31157
31267
  try {
31158
- const provider = new import_ethers5.ethers.JsonRpcProvider(chain2.rpc);
31268
+ const provider = new import_ethers4.ethers.JsonRpcProvider(chain2.rpc);
31159
31269
  const receipt = await provider.getTransactionReceipt(txHash);
31160
31270
  if (!receipt) {
31161
31271
  return { verified: false, error: "Transaction not found or not confirmed" };
@@ -31215,7 +31325,7 @@ async function getTransactionStatus(txHash, chain2 = "base") {
31215
31325
  return { status: "not_found" };
31216
31326
  }
31217
31327
  try {
31218
- const provider = new import_ethers5.ethers.JsonRpcProvider(chainConfig.rpc);
31328
+ const provider = new import_ethers4.ethers.JsonRpcProvider(chainConfig.rpc);
31219
31329
  const receipt = await provider.getTransactionReceipt(txHash);
31220
31330
  if (!receipt) {
31221
31331
  const tx = await provider.getTransaction(txHash);
@@ -31252,7 +31362,7 @@ async function waitForTransaction(txHash, chain2 = "base", confirmations = 1, ti
31252
31362
  } catch (e) {
31253
31363
  return { verified: false, confirmed: false, error: `Unsupported chain: ${chain2}` };
31254
31364
  }
31255
- const provider = new import_ethers5.ethers.JsonRpcProvider(chainConfig.rpc);
31365
+ const provider = new import_ethers4.ethers.JsonRpcProvider(chainConfig.rpc);
31256
31366
  try {
31257
31367
  const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
31258
31368
  if (!receipt) {
@@ -31275,8 +31385,8 @@ async function waitForTransaction(txHash, chain2 = "base", confirmations = 1, ti
31275
31385
  // src/cdp/index.ts
31276
31386
  init_cjs_shims();
31277
31387
  var fs = __toESM(require("fs"));
31278
- var path = __toESM(require("path"));
31279
- var DEFAULT_STORAGE_DIR2 = path.join(process.env.HOME || ".", ".moltspay");
31388
+ var path2 = __toESM(require("path"));
31389
+ var DEFAULT_STORAGE_DIR2 = path2.join(process.env.HOME || ".", ".moltspay");
31280
31390
  var CDP_CONFIG_FILE = "cdp-wallet.json";
31281
31391
  function isCDPAvailable() {
31282
31392
  try {
@@ -31298,7 +31408,7 @@ function getCDPCredentials(config) {
31298
31408
  async function initCDPWallet(config = {}) {
31299
31409
  const storageDir = config.storageDir || DEFAULT_STORAGE_DIR2;
31300
31410
  const chain2 = config.chain || "base";
31301
- const storagePath = path.join(storageDir, CDP_CONFIG_FILE);
31411
+ const storagePath = path2.join(storageDir, CDP_CONFIG_FILE);
31302
31412
  if (fs.existsSync(storagePath)) {
31303
31413
  try {
31304
31414
  const data = JSON.parse(fs.readFileSync(storagePath, "utf-8"));
@@ -31360,7 +31470,7 @@ async function initCDPWallet(config = {}) {
31360
31470
  }
31361
31471
  function loadCDPWallet(config = {}) {
31362
31472
  const storageDir = config.storageDir || DEFAULT_STORAGE_DIR2;
31363
- const storagePath = path.join(storageDir, CDP_CONFIG_FILE);
31473
+ const storagePath = path2.join(storageDir, CDP_CONFIG_FILE);
31364
31474
  if (!fs.existsSync(storagePath)) {
31365
31475
  return null;
31366
31476
  }
@@ -31393,9 +31503,9 @@ var CDPWallet = class {
31393
31503
  * Get USDC balance
31394
31504
  */
31395
31505
  async getBalance() {
31396
- const { ethers: ethers6 } = await import("ethers");
31397
- const provider = new ethers6.JsonRpcProvider(this.chainConfig.rpc);
31398
- const usdcContract = new ethers6.Contract(
31506
+ const { ethers: ethers5 } = await import("ethers");
31507
+ const provider = new ethers5.JsonRpcProvider(this.chainConfig.rpc);
31508
+ const usdcContract = new ethers5.Contract(
31399
31509
  this.chainConfig.usdc,
31400
31510
  ["function balanceOf(address) view returns (uint256)"],
31401
31511
  provider
@@ -31406,7 +31516,7 @@ var CDPWallet = class {
31406
31516
  ]);
31407
31517
  return {
31408
31518
  usdc: (Number(usdcBalance) / 1e6).toFixed(2),
31409
- eth: ethers6.formatEther(ethBalance)
31519
+ eth: ethers5.formatEther(ethBalance)
31410
31520
  };
31411
31521
  }
31412
31522
  /**
@@ -31424,7 +31534,7 @@ var CDPWallet = class {
31424
31534
  }
31425
31535
  try {
31426
31536
  const { CdpClient } = await import("@coinbase/cdp-sdk");
31427
- const { ethers: ethers6 } = await import("ethers");
31537
+ const { ethers: ethers5 } = await import("ethers");
31428
31538
  const cdp = new CdpClient({
31429
31539
  apiKeyId: creds.apiKeyId,
31430
31540
  apiKeySecret: creds.apiKeySecret,
@@ -31432,7 +31542,7 @@ var CDPWallet = class {
31432
31542
  });
31433
31543
  const account = await cdp.evm.getAccount({ address: this.address });
31434
31544
  const amountWei = BigInt(Math.floor(params.amount * 1e6));
31435
- const iface = new ethers6.Interface([
31545
+ const iface = new ethers5.Interface([
31436
31546
  "function transfer(address to, uint256 amount) returns (bool)"
31437
31547
  ]);
31438
31548
  const callData = iface.encodeFunctionData("transfer", [params.to, amountWei]);