moltspay 1.4.1 → 1.6.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/dist/index.js CHANGED
@@ -288,6 +288,9 @@ var CDPFacilitator = class extends BaseFacilitator {
288
288
  }
289
289
  };
290
290
 
291
+ // src/facilitators/tempo.ts
292
+ var import_ethers = require("ethers");
293
+
291
294
  // src/chains/index.ts
292
295
  var CHAINS = {
293
296
  // ============ Mainnet ============
@@ -483,15 +486,38 @@ var ERC20_ABI = [
483
486
 
484
487
  // src/facilitators/tempo.ts
485
488
  var TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
489
+ var TIP20_PERMIT_ABI = [
490
+ "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
491
+ "function transferFrom(address from, address to, uint256 value) returns (bool)"
492
+ ];
486
493
  var TempoFacilitator = class extends BaseFacilitator {
487
494
  name = "tempo";
488
495
  displayName = "Tempo Testnet";
489
496
  supportedNetworks = ["eip155:42431"];
490
497
  // Tempo Moderato
491
498
  rpcUrl;
499
+ settlerWallet = null;
492
500
  constructor() {
493
501
  super();
494
502
  this.rpcUrl = CHAINS.tempo_moderato.rpc;
503
+ const settlerKey = process.env.TEMPO_SETTLER_KEY;
504
+ if (settlerKey) {
505
+ try {
506
+ const provider = new import_ethers.ethers.JsonRpcProvider(this.rpcUrl);
507
+ this.settlerWallet = new import_ethers.ethers.Wallet(settlerKey, provider);
508
+ } catch (err) {
509
+ console.warn("[TempoFacilitator] Invalid TEMPO_SETTLER_KEY, permit settlement disabled:", err);
510
+ this.settlerWallet = null;
511
+ }
512
+ }
513
+ }
514
+ /**
515
+ * Settler EOA address advertised to clients via `X-Payment-Required.extra.tempoSpender`.
516
+ * Web Client uses this as the `spender` field in the signed EIP-2612 Permit.
517
+ * Returns null if no TEMPO_SETTLER_KEY is configured — permit settlement unavailable.
518
+ */
519
+ getSpenderAddress() {
520
+ return this.settlerWallet?.address ?? null;
495
521
  }
496
522
  async healthCheck() {
497
523
  const start = Date.now();
@@ -517,6 +543,44 @@ var TempoFacilitator = class extends BaseFacilitator {
517
543
  }
518
544
  }
519
545
  async verify(paymentPayload, requirements) {
546
+ const inner = paymentPayload.payload;
547
+ if (inner && "permit" in inner && inner.permit) {
548
+ return this.verifyPermit(inner, requirements);
549
+ }
550
+ return this.verifyTxHash(paymentPayload, requirements);
551
+ }
552
+ /**
553
+ * Structural validation of an EIP-2612 permit payload. Does NOT submit
554
+ * anything on-chain — actual submission happens in settlePermit().
555
+ */
556
+ async verifyPermit(payload, requirements) {
557
+ if (!this.settlerWallet) {
558
+ return { valid: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
559
+ }
560
+ const p = payload.permit;
561
+ if (!p || !p.owner || !p.spender || !p.value || !p.deadline) {
562
+ return { valid: false, error: "Invalid permit payload: missing fields" };
563
+ }
564
+ if (p.spender.toLowerCase() !== this.settlerWallet.address.toLowerCase()) {
565
+ return {
566
+ valid: false,
567
+ error: `Permit spender ${p.spender} does not match configured settler ${this.settlerWallet.address}`
568
+ };
569
+ }
570
+ const deadline = BigInt(p.deadline);
571
+ const now = BigInt(Math.floor(Date.now() / 1e3));
572
+ if (deadline <= now) {
573
+ return { valid: false, error: "Permit deadline has expired" };
574
+ }
575
+ if (BigInt(p.value) < BigInt(requirements.amount || "0")) {
576
+ return {
577
+ valid: false,
578
+ error: `Permit value ${p.value} is less than required ${requirements.amount}`
579
+ };
580
+ }
581
+ return { valid: true, details: { scheme: "permit", owner: p.owner } };
582
+ }
583
+ async verifyTxHash(paymentPayload, requirements) {
520
584
  try {
521
585
  const tempoPayload = paymentPayload.payload;
522
586
  if (!tempoPayload?.txHash) {
@@ -574,7 +638,11 @@ var TempoFacilitator = class extends BaseFacilitator {
574
638
  }
575
639
  }
576
640
  async settle(paymentPayload, requirements) {
577
- const verifyResult = await this.verify(paymentPayload, requirements);
641
+ const inner = paymentPayload.payload;
642
+ if (inner && "permit" in inner && inner.permit) {
643
+ return this.settlePermit(inner, requirements);
644
+ }
645
+ const verifyResult = await this.verifyTxHash(paymentPayload, requirements);
578
646
  if (!verifyResult.valid) {
579
647
  return { success: false, error: verifyResult.error };
580
648
  }
@@ -585,6 +653,52 @@ var TempoFacilitator = class extends BaseFacilitator {
585
653
  status: "settled"
586
654
  };
587
655
  }
656
+ /**
657
+ * EIP-2612 permit settlement path. Submits two transactions on Tempo:
658
+ * 1. pathUSD.permit(owner, spender=settler, value, deadline, v, r, s)
659
+ * 2. pathUSD.transferFrom(owner, payTo, value)
660
+ *
661
+ * The settler EOA pays Tempo gas (via the TIP-20 `feeToken` mechanism — no
662
+ * native tTEMPO required; any held TIP-20 token balance covers fees).
663
+ */
664
+ async settlePermit(payload, requirements) {
665
+ if (!this.settlerWallet) {
666
+ return { success: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
667
+ }
668
+ if (!requirements.asset || !requirements.payTo) {
669
+ return { success: false, error: "Missing asset or payTo in requirements" };
670
+ }
671
+ const verifyResult = await this.verifyPermit(payload, requirements);
672
+ if (!verifyResult.valid) {
673
+ return { success: false, error: verifyResult.error };
674
+ }
675
+ const token = new import_ethers.ethers.Contract(requirements.asset, TIP20_PERMIT_ABI, this.settlerWallet);
676
+ const p = payload.permit;
677
+ try {
678
+ const permitTx = await token.permit(
679
+ p.owner,
680
+ p.spender,
681
+ p.value,
682
+ p.deadline,
683
+ p.v,
684
+ p.r,
685
+ p.s
686
+ );
687
+ await permitTx.wait();
688
+ const transferTx = await token.transferFrom(p.owner, requirements.payTo, p.value);
689
+ await transferTx.wait();
690
+ return {
691
+ success: true,
692
+ transaction: transferTx.hash,
693
+ status: "settled"
694
+ };
695
+ } catch (err) {
696
+ return {
697
+ success: false,
698
+ error: `Tempo permit settlement failed: ${err.message}`
699
+ };
700
+ }
701
+ }
588
702
  async getTransactionReceipt(txHash) {
589
703
  const response = await fetch(this.rpcUrl, {
590
704
  method: "POST",
@@ -827,12 +941,12 @@ var BNBFacilitator = class extends BaseFacilitator {
827
941
  return this.spenderAddress;
828
942
  }
829
943
  async getServerAddress() {
830
- const { ethers: ethers5 } = await import("ethers");
831
- const wallet = new ethers5.Wallet(this.serverPrivateKey);
944
+ const { ethers: ethers7 } = await import("ethers");
945
+ const wallet = new ethers7.Wallet(this.serverPrivateKey);
832
946
  return wallet.address;
833
947
  }
834
948
  async recoverIntentSigner(intent, chainId) {
835
- const { ethers: ethers5 } = await import("ethers");
949
+ const { ethers: ethers7 } = await import("ethers");
836
950
  const domain = {
837
951
  ...EIP712_DOMAIN,
838
952
  chainId
@@ -846,7 +960,7 @@ var BNBFacilitator = class extends BaseFacilitator {
846
960
  nonce: intent.nonce,
847
961
  deadline: intent.deadline
848
962
  };
849
- const recoveredAddress = ethers5.verifyTypedData(
963
+ const recoveredAddress = ethers7.verifyTypedData(
850
964
  domain,
851
965
  INTENT_TYPES,
852
966
  message,
@@ -890,10 +1004,10 @@ var BNBFacilitator = class extends BaseFacilitator {
890
1004
  return result.result || "0x0";
891
1005
  }
892
1006
  async executeTransferFrom(from, to, amount, token, rpcUrl) {
893
- const { ethers: ethers5 } = await import("ethers");
894
- const provider = new ethers5.JsonRpcProvider(rpcUrl);
895
- const wallet = new ethers5.Wallet(this.serverPrivateKey, provider);
896
- const tokenContract = new ethers5.Contract(token, [
1007
+ const { ethers: ethers7 } = await import("ethers");
1008
+ const provider = new ethers7.JsonRpcProvider(rpcUrl);
1009
+ const wallet = new ethers7.Wallet(this.serverPrivateKey, provider);
1010
+ const tokenContract = new ethers7.Contract(token, [
897
1011
  "function transferFrom(address from, address to, uint256 amount) returns (bool)"
898
1012
  ], wallet);
899
1013
  const tx = await tokenContract.transferFrom(from, to, amount);
@@ -1119,16 +1233,16 @@ var SolanaFacilitator = class extends BaseFacilitator {
1119
1233
  return this.supportedNetworks.includes(network);
1120
1234
  }
1121
1235
  };
1122
- async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey) {
1236
+ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey, connection) {
1123
1237
  const chainConfig = SOLANA_CHAINS[chain];
1124
- const connection = new import_web32.Connection(chainConfig.rpc, "confirmed");
1238
+ const conn = connection ?? new import_web32.Connection(chainConfig.rpc, "confirmed");
1125
1239
  const mint = new import_web32.PublicKey(chainConfig.tokens.USDC.mint);
1126
1240
  const actualFeePayer = feePayerPubkey || senderPubkey;
1127
1241
  const senderATA = await (0, import_spl_token.getAssociatedTokenAddress)(mint, senderPubkey);
1128
1242
  const recipientATA = await (0, import_spl_token.getAssociatedTokenAddress)(mint, recipientPubkey);
1129
1243
  const transaction = new import_web32.Transaction();
1130
1244
  try {
1131
- await (0, import_spl_token.getAccount)(connection, recipientATA);
1245
+ await (0, import_spl_token.getAccount)(conn, recipientATA);
1132
1246
  } catch {
1133
1247
  transaction.add(
1134
1248
  (0, import_spl_token.createAssociatedTokenAccountInstruction)(
@@ -1159,7 +1273,7 @@ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amo
1159
1273
  // decimals
1160
1274
  )
1161
1275
  );
1162
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1276
+ const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();
1163
1277
  transaction.recentBlockhash = blockhash;
1164
1278
  transaction.feePayer = actualFeePayer;
1165
1279
  return transaction;
@@ -1495,9 +1609,13 @@ var TOKEN_DOMAINS = {
1495
1609
  USDT: { name: "(PoS) Tether USD", version: "2" }
1496
1610
  },
1497
1611
  // Tempo Moderato testnet - TIP-20 stablecoins
1612
+ // Domain names verified against on-chain DOMAIN_SEPARATOR values on 2026-04-21.
1613
+ // See docs/TEMPO-WEB-SUPPORT.md Section 2 and test/server/tempo-domain.test.ts.
1614
+ // All 4 Tempo TIP-20 tokens (pathUSD / AlphaUSD / BetaUSD / ThetaUSD) use
1615
+ // the token symbol with first letter capitalized + version "1".
1498
1616
  "eip155:42431": {
1499
- USDC: { name: "pathUSD", version: "1" },
1500
- USDT: { name: "alphaUSD", version: "1" }
1617
+ USDC: { name: "PathUSD", version: "1" },
1618
+ USDT: { name: "AlphaUSD", version: "1" }
1501
1619
  },
1502
1620
  // BNB Smart Chain mainnet
1503
1621
  "eip155:56": {
@@ -1666,13 +1784,62 @@ var MoltsPayServer = class {
1666
1784
  });
1667
1785
  }
1668
1786
  /**
1669
- * Handle incoming request
1787
+ * Apply CORS response headers according to the `cors` option.
1788
+ *
1789
+ * Default (`cors` unset or `true`): `Access-Control-Allow-Origin: *`. Matches 1.5.x behavior
1790
+ * and works for every browser client whose origin does not need to send cookies.
1791
+ *
1792
+ * `cors: false`: emit no CORS headers. Same-origin only.
1793
+ * `cors: string[]`: origin allowlist — echo the origin back iff it matches.
1794
+ * `cors: CorsOptions`: full control (allowlist + credentials + maxAge).
1795
+ *
1796
+ * The required-for-Web response headers are always exposed when CORS is active:
1797
+ * `X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt`.
1670
1798
  */
1671
- async handleRequest(req, res) {
1672
- res.setHeader("Access-Control-Allow-Origin", "*");
1799
+ applyCorsHeaders(req, res) {
1800
+ const cors = this.options.cors;
1801
+ if (cors === false) {
1802
+ return;
1803
+ }
1804
+ const requestOrigin = req.headers.origin ?? "*";
1805
+ if (cors === void 0 || cors === true) {
1806
+ this.writeCorsHeaders(res, "*");
1807
+ return;
1808
+ }
1809
+ if (Array.isArray(cors)) {
1810
+ if (cors.includes(requestOrigin)) {
1811
+ this.writeCorsHeaders(res, requestOrigin);
1812
+ res.setHeader("Vary", "Origin");
1813
+ }
1814
+ return;
1815
+ }
1816
+ const opt = cors;
1817
+ const isAllowed = typeof opt.origins === "function" ? opt.origins(requestOrigin) : opt.origins.includes(requestOrigin);
1818
+ if (!isAllowed) {
1819
+ return;
1820
+ }
1821
+ this.writeCorsHeaders(res, requestOrigin);
1822
+ res.setHeader("Vary", "Origin");
1823
+ if (opt.credentials) {
1824
+ res.setHeader("Access-Control-Allow-Credentials", "true");
1825
+ }
1826
+ const maxAge = opt.maxAge ?? 600;
1827
+ res.setHeader("Access-Control-Max-Age", String(maxAge));
1828
+ }
1829
+ writeCorsHeaders(res, origin) {
1830
+ res.setHeader("Access-Control-Allow-Origin", origin);
1673
1831
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
1674
1832
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
1675
- res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt");
1833
+ res.setHeader(
1834
+ "Access-Control-Expose-Headers",
1835
+ "X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt"
1836
+ );
1837
+ }
1838
+ /**
1839
+ * Handle incoming request
1840
+ */
1841
+ async handleRequest(req, res) {
1842
+ this.applyCorsHeaders(req, res);
1676
1843
  if (req.method === "OPTIONS") {
1677
1844
  res.writeHead(204);
1678
1845
  res.end();
@@ -1895,6 +2062,14 @@ var MoltsPayServer = class {
1895
2062
  console.log(`[MoltsPay] Payment settled by ${settlement.facilitator}: ${settlement.transaction || "pending"}`);
1896
2063
  } catch (err) {
1897
2064
  console.error("[MoltsPay] Settlement failed:", err.message);
2065
+ settlement = { success: false, error: err.message, facilitator: "none" };
2066
+ }
2067
+ if (!settlement?.success) {
2068
+ return this.sendJson(res, 402, {
2069
+ error: "Payment settlement failed",
2070
+ message: settlement?.error || "Settlement returned no success state",
2071
+ facilitator: settlement?.facilitator
2072
+ });
1898
2073
  }
1899
2074
  }
1900
2075
  const responseHeaders = {};
@@ -2149,7 +2324,7 @@ var MoltsPayServer = class {
2149
2324
  }
2150
2325
  const scheme = payment.accepted?.scheme || payment.scheme;
2151
2326
  const network = payment.accepted?.network || payment.network || this.networkId;
2152
- if (scheme !== "exact") {
2327
+ if (scheme !== "exact" && scheme !== "permit") {
2153
2328
  return { valid: false, error: `Unsupported scheme: ${scheme}` };
2154
2329
  }
2155
2330
  if (!this.isNetworkAccepted(network)) {
@@ -2171,8 +2346,10 @@ var MoltsPayServer = class {
2171
2346
  const tokenAddresses = TOKEN_ADDRESSES[selectedNetwork] || {};
2172
2347
  const tokenAddress = tokenAddresses[selectedToken];
2173
2348
  const tokenDomain = getTokenDomain(selectedNetwork, selectedToken);
2349
+ const isTempo = selectedNetwork === "eip155:42431";
2350
+ const scheme = isTempo ? "permit" : "exact";
2174
2351
  const requirements = {
2175
- scheme: "exact",
2352
+ scheme,
2176
2353
  network: selectedNetwork,
2177
2354
  asset: tokenAddress,
2178
2355
  amount: amountInUnits,
@@ -2200,6 +2377,16 @@ var MoltsPayServer = class {
2200
2377
  };
2201
2378
  }
2202
2379
  }
2380
+ if (isTempo) {
2381
+ const tempoFacilitator = this.registry.get("tempo");
2382
+ const tempoSpender = tempoFacilitator?.getSpenderAddress?.();
2383
+ if (tempoSpender) {
2384
+ requirements.extra = {
2385
+ ...requirements.extra || {},
2386
+ tempoSpender
2387
+ };
2388
+ }
2389
+ }
2203
2390
  return requirements;
2204
2391
  }
2205
2392
  /**
@@ -2334,7 +2521,7 @@ var MoltsPayServer = class {
2334
2521
  }
2335
2522
  const scheme = payment.accepted?.scheme || payment.scheme;
2336
2523
  const network = payment.accepted?.network || payment.network;
2337
- if (scheme !== "exact") {
2524
+ if (scheme !== "exact" && scheme !== "permit") {
2338
2525
  return this.sendJson(res, 402, { error: `Unsupported scheme: ${scheme}` });
2339
2526
  }
2340
2527
  const expectedNetwork = chain ? CHAIN_TO_NETWORK[chain] || this.networkId : this.networkId;
@@ -2654,11 +2841,11 @@ var MoltsPayServer = class {
2654
2841
  }
2655
2842
  };
2656
2843
 
2657
- // src/client/index.ts
2844
+ // src/client/node/index.ts
2658
2845
  var import_fs4 = require("fs");
2659
2846
  var import_os2 = require("os");
2660
2847
  var import_path2 = require("path");
2661
- var import_ethers = require("ethers");
2848
+ var import_ethers3 = require("ethers");
2662
2849
 
2663
2850
  // src/wallet/solana.ts
2664
2851
  var import_web34 = require("@solana/web3.js");
@@ -2687,11 +2874,172 @@ function loadSolanaWallet(configDir = DEFAULT_CONFIG_DIR) {
2687
2874
  }
2688
2875
  }
2689
2876
 
2690
- // src/client/index.ts
2691
- var import_web35 = require("@solana/web3.js");
2877
+ // src/client/node/index.ts
2878
+ var import_web36 = require("@solana/web3.js");
2879
+
2880
+ // src/client/core/types.ts
2692
2881
  var X402_VERSION3 = 2;
2693
2882
  var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
2694
2883
  var PAYMENT_HEADER2 = "x-payment";
2884
+
2885
+ // src/client/core/chain-map.ts
2886
+ var NETWORK_TO_CHAIN = {
2887
+ "eip155:8453": "base",
2888
+ "eip155:137": "polygon",
2889
+ "eip155:84532": "base_sepolia",
2890
+ "eip155:42431": "tempo_moderato",
2891
+ "eip155:56": "bnb",
2892
+ "eip155:97": "bnb_testnet",
2893
+ "solana:mainnet": "solana",
2894
+ "solana:devnet": "solana_devnet"
2895
+ };
2896
+ var CHAIN_TO_NETWORK2 = Object.fromEntries(
2897
+ Object.entries(NETWORK_TO_CHAIN).map(([network, chain]) => [chain, network])
2898
+ );
2899
+ function networkToChainName(network) {
2900
+ return NETWORK_TO_CHAIN[network] ?? null;
2901
+ }
2902
+
2903
+ // src/client/core/base64.ts
2904
+ var BufferCtor = globalThis.Buffer;
2905
+
2906
+ // src/client/core/eip3009.ts
2907
+ var EIP3009_TYPES = {
2908
+ TransferWithAuthorization: [
2909
+ { name: "from", type: "address" },
2910
+ { name: "to", type: "address" },
2911
+ { name: "value", type: "uint256" },
2912
+ { name: "validAfter", type: "uint256" },
2913
+ { name: "validBefore", type: "uint256" },
2914
+ { name: "nonce", type: "bytes32" }
2915
+ ]
2916
+ };
2917
+ function buildEIP3009TypedData(args) {
2918
+ const validAfter = args.validAfter ?? "0";
2919
+ const validBefore = args.validBefore ?? (Math.floor(Date.now() / 1e3) + 3600).toString();
2920
+ const authorization = {
2921
+ from: args.from,
2922
+ to: args.to,
2923
+ value: args.value,
2924
+ validAfter,
2925
+ validBefore,
2926
+ nonce: args.nonce
2927
+ };
2928
+ return {
2929
+ domain: {
2930
+ name: args.tokenName,
2931
+ version: args.tokenVersion,
2932
+ chainId: args.chainId,
2933
+ verifyingContract: args.tokenAddress
2934
+ },
2935
+ types: EIP3009_TYPES,
2936
+ primaryType: "TransferWithAuthorization",
2937
+ message: authorization
2938
+ };
2939
+ }
2940
+
2941
+ // src/client/core/bnb-intent.ts
2942
+ var BNB_INTENT_TYPES = {
2943
+ PaymentIntent: [
2944
+ { name: "from", type: "address" },
2945
+ { name: "to", type: "address" },
2946
+ { name: "amount", type: "uint256" },
2947
+ { name: "token", type: "address" },
2948
+ { name: "service", type: "string" },
2949
+ { name: "nonce", type: "uint256" },
2950
+ { name: "deadline", type: "uint256" }
2951
+ ]
2952
+ };
2953
+ var BNB_DOMAIN_NAME = "MoltsPay";
2954
+ var BNB_DOMAIN_VERSION = "1";
2955
+ function buildBnbIntentTypedData(args) {
2956
+ const intent = {
2957
+ from: args.from,
2958
+ to: args.to,
2959
+ amount: args.amount,
2960
+ token: args.tokenAddress,
2961
+ service: args.service,
2962
+ nonce: args.nonce,
2963
+ deadline: args.deadline
2964
+ };
2965
+ return {
2966
+ domain: {
2967
+ name: BNB_DOMAIN_NAME,
2968
+ version: BNB_DOMAIN_VERSION,
2969
+ chainId: args.chainId
2970
+ },
2971
+ types: BNB_INTENT_TYPES,
2972
+ primaryType: "PaymentIntent",
2973
+ message: intent
2974
+ };
2975
+ }
2976
+
2977
+ // src/client/node/signer.ts
2978
+ var import_ethers2 = require("ethers");
2979
+ var import_web35 = require("@solana/web3.js");
2980
+ var NodeSigner = class {
2981
+ evmWallet;
2982
+ getSolanaKeypair;
2983
+ constructor(evmWallet, options = {}) {
2984
+ this.evmWallet = evmWallet;
2985
+ this.getSolanaKeypair = options.getSolanaKeypair ?? (() => null);
2986
+ }
2987
+ async getEvmAddress() {
2988
+ return this.evmWallet.address;
2989
+ }
2990
+ async getSolanaAddress() {
2991
+ const kp = this.getSolanaKeypair();
2992
+ return kp ? kp.publicKey.toBase58() : null;
2993
+ }
2994
+ async signTypedData(envelope) {
2995
+ const mutableTypes = {};
2996
+ for (const [key, fields] of Object.entries(envelope.types)) {
2997
+ mutableTypes[key] = [...fields];
2998
+ }
2999
+ return this.evmWallet.signTypedData(
3000
+ envelope.domain,
3001
+ mutableTypes,
3002
+ envelope.message
3003
+ );
3004
+ }
3005
+ async sendEvmTransaction(args) {
3006
+ const chain = findChainByChainId(args.chainId);
3007
+ if (!chain) {
3008
+ throw new Error(`sendEvmTransaction: unknown chainId ${args.chainId}`);
3009
+ }
3010
+ const provider = new import_ethers2.ethers.JsonRpcProvider(chain.rpc);
3011
+ const connected = this.evmWallet.connect(provider);
3012
+ const tx = await connected.sendTransaction({
3013
+ to: args.to,
3014
+ data: args.data,
3015
+ value: args.value ? BigInt(args.value) : 0n
3016
+ });
3017
+ return tx.hash;
3018
+ }
3019
+ async signSolanaTransaction(args) {
3020
+ const kp = this.getSolanaKeypair();
3021
+ if (!kp) {
3022
+ throw new Error("signSolanaTransaction: no Solana wallet configured");
3023
+ }
3024
+ const tx = import_web35.Transaction.from(Buffer.from(args.transactionBase64, "base64"));
3025
+ if (args.partialSign) {
3026
+ tx.partialSign(kp);
3027
+ } else {
3028
+ tx.sign(kp);
3029
+ }
3030
+ return tx.serialize({ requireAllSignatures: false }).toString("base64");
3031
+ }
3032
+ };
3033
+ function findChainByChainId(chainId) {
3034
+ for (const cfg of Object.values(CHAINS)) {
3035
+ if (cfg.chainId === chainId) {
3036
+ return cfg;
3037
+ }
3038
+ }
3039
+ return void 0;
3040
+ }
3041
+
3042
+ // src/client/node/index.ts
2695
3043
  var DEFAULT_CONFIG = {
2696
3044
  chain: "base",
2697
3045
  limits: {
@@ -2704,6 +3052,7 @@ var MoltsPayClient = class {
2704
3052
  config;
2705
3053
  walletData = null;
2706
3054
  wallet = null;
3055
+ signer = null;
2707
3056
  todaySpending = 0;
2708
3057
  lastSpendingReset = 0;
2709
3058
  constructor(options = {}) {
@@ -2712,7 +3061,11 @@ var MoltsPayClient = class {
2712
3061
  this.walletData = this.loadWallet();
2713
3062
  this.loadSpending();
2714
3063
  if (this.walletData) {
2715
- this.wallet = new import_ethers.Wallet(this.walletData.privateKey);
3064
+ this.wallet = new import_ethers3.Wallet(this.walletData.privateKey);
3065
+ const configDir = this.configDir;
3066
+ this.signer = new NodeSigner(this.wallet, {
3067
+ getSolanaKeypair: () => loadSolanaWallet(configDir)
3068
+ });
2716
3069
  }
2717
3070
  }
2718
3071
  /**
@@ -2840,20 +3193,6 @@ var MoltsPayClient = class {
2840
3193
  } catch {
2841
3194
  throw new Error("Invalid x-payment-required header");
2842
3195
  }
2843
- const networkToChainName = (network2) => {
2844
- if (network2 === "solana:mainnet") return "solana";
2845
- if (network2 === "solana:devnet") return "solana_devnet";
2846
- const match = network2.match(/^eip155:(\d+)$/);
2847
- if (!match) return null;
2848
- const chainId = parseInt(match[1]);
2849
- if (chainId === 8453) return "base";
2850
- if (chainId === 137) return "polygon";
2851
- if (chainId === 84532) return "base_sepolia";
2852
- if (chainId === 42431) return "tempo_moderato";
2853
- if (chainId === 56) return "bnb";
2854
- if (chainId === 97) return "bnb_testnet";
2855
- return null;
2856
- };
2857
3196
  const serverChains = requirements.map((r) => networkToChainName(r.network)).filter((c) => c !== null);
2858
3197
  const userSpecifiedChain = options.chain;
2859
3198
  let selectedChain;
@@ -3082,14 +3421,14 @@ Please specify: --chain <chain_name>`
3082
3421
  async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
3083
3422
  const { to, amount, token, chainName, chain, spender } = paymentDetails;
3084
3423
  const tokenConfig = chain.tokens[token];
3085
- const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
3424
+ const provider = new import_ethers3.ethers.JsonRpcProvider(chain.rpc);
3086
3425
  const allowance = await this.checkAllowance(tokenConfig.address, spender, provider);
3087
3426
  const amountWeiCheck = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals));
3088
3427
  if (allowance < amountWeiCheck) {
3089
3428
  const nativeBalance = await provider.getBalance(this.wallet.address);
3090
- const minGasBalance = import_ethers.ethers.parseEther("0.0005");
3429
+ const minGasBalance = import_ethers3.ethers.parseEther("0.0005");
3091
3430
  if (nativeBalance < minGasBalance) {
3092
- const nativeBNB = parseFloat(import_ethers.ethers.formatEther(nativeBalance)).toFixed(4);
3431
+ const nativeBNB = parseFloat(import_ethers3.ethers.formatEther(nativeBalance)).toFixed(4);
3093
3432
  const isTestnet = chainName === "bnb_testnet";
3094
3433
  if (isTestnet) {
3095
3434
  throw new Error(
@@ -3123,35 +3462,21 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3123
3462
  );
3124
3463
  }
3125
3464
  const amountWei = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
3126
- const intent = {
3465
+ const intentNonce = Date.now();
3466
+ const intentDeadline = Date.now() + 36e5;
3467
+ const envelope = buildBnbIntentTypedData({
3127
3468
  from: this.wallet.address,
3128
3469
  to,
3129
3470
  amount: amountWei,
3130
- token: tokenConfig.address,
3471
+ tokenAddress: tokenConfig.address,
3131
3472
  service,
3132
- nonce: Date.now(),
3133
- // Use timestamp as nonce for simplicity
3134
- deadline: Date.now() + 36e5
3135
- // 1 hour
3136
- };
3137
- const domain = {
3138
- name: "MoltsPay",
3139
- version: "1",
3473
+ nonce: intentNonce,
3474
+ deadline: intentDeadline,
3140
3475
  chainId: chain.chainId
3141
- };
3142
- const types = {
3143
- PaymentIntent: [
3144
- { name: "from", type: "address" },
3145
- { name: "to", type: "address" },
3146
- { name: "amount", type: "uint256" },
3147
- { name: "token", type: "address" },
3148
- { name: "service", type: "string" },
3149
- { name: "nonce", type: "uint256" },
3150
- { name: "deadline", type: "uint256" }
3151
- ]
3152
- };
3476
+ });
3153
3477
  console.log(`[MoltsPay] Signing BNB payment intent...`);
3154
- const signature = await this.wallet.signTypedData(domain, types, intent);
3478
+ const signature = await this.signer.signTypedData(envelope);
3479
+ const intent = envelope.message;
3155
3480
  const network = `eip155:${chain.chainId}`;
3156
3481
  const payload = {
3157
3482
  x402Version: 2,
@@ -3212,11 +3537,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3212
3537
  throw new Error("Missing payTo address in payment requirements");
3213
3538
  }
3214
3539
  const solanaFeePayer = requirements.extra?.solanaFeePayer;
3215
- const feePayerPubkey = solanaFeePayer ? new import_web35.PublicKey(solanaFeePayer) : void 0;
3540
+ const feePayerPubkey = solanaFeePayer ? new import_web36.PublicKey(solanaFeePayer) : void 0;
3216
3541
  if (feePayerPubkey) {
3217
3542
  console.log(`[MoltsPay] Gasless mode: server pays fees`);
3218
3543
  }
3219
- const recipientPubkey = new import_web35.PublicKey(requirements.payTo);
3544
+ const recipientPubkey = new import_web36.PublicKey(requirements.payTo);
3220
3545
  const transaction = await createSolanaPaymentTransaction(
3221
3546
  solanaWallet.publicKey,
3222
3547
  recipientPubkey,
@@ -3225,12 +3550,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3225
3550
  feePayerPubkey
3226
3551
  // Optional fee payer for gasless mode
3227
3552
  );
3228
- if (feePayerPubkey) {
3229
- transaction.partialSign(solanaWallet);
3230
- } else {
3231
- transaction.sign(solanaWallet);
3232
- }
3233
- const signedTx = transaction.serialize({ requireAllSignatures: false }).toString("base64");
3553
+ const unsignedBase64 = transaction.serialize({ requireAllSignatures: false, verifySignatures: false }).toString("base64");
3554
+ const signedTx = await this.signer.signSolanaTransaction({
3555
+ transactionBase64: unsignedBase64,
3556
+ partialSign: !!feePayerPubkey
3557
+ });
3234
3558
  console.log(`[MoltsPay] Transaction signed, sending to server...`);
3235
3559
  const network = chain === "solana" ? "solana:mainnet" : "solana:devnet";
3236
3560
  const payload = {
@@ -3277,7 +3601,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3277
3601
  * Check ERC20 allowance for a spender
3278
3602
  */
3279
3603
  async checkAllowance(tokenAddress, spender, provider) {
3280
- const contract = new import_ethers.ethers.Contract(
3604
+ const contract = new import_ethers3.ethers.Contract(
3281
3605
  tokenAddress,
3282
3606
  ["function allowance(address owner, address spender) view returns (uint256)"],
3283
3607
  provider
@@ -3288,41 +3612,29 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3288
3612
  * Sign EIP-3009 transferWithAuthorization (GASLESS)
3289
3613
  * This only signs - no on-chain transaction, no gas needed.
3290
3614
  * Supports both USDC and USDT.
3615
+ *
3616
+ * Delegates typed-data construction to `core/eip3009.ts` and the signature
3617
+ * itself to `this.signer`. That way Web Client (Phase 4) can reuse the same
3618
+ * flow with an EIP-1193 signer without duplicating typed-data layout.
3291
3619
  */
3292
3620
  async signEIP3009(to, amount, chain, token = "USDC", domainOverride) {
3293
- const validAfter = 0;
3294
- const validBefore = Math.floor(Date.now() / 1e3) + 3600;
3295
- const nonce = import_ethers.ethers.hexlify(import_ethers.ethers.randomBytes(32));
3296
3621
  const tokenConfig = chain.tokens[token];
3297
3622
  const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
3298
- const authorization = {
3623
+ const nonce = import_ethers3.ethers.hexlify(import_ethers3.ethers.randomBytes(32));
3624
+ const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
3625
+ const tokenVersion = domainOverride?.version || "2";
3626
+ const envelope = buildEIP3009TypedData({
3299
3627
  from: this.wallet.address,
3300
3628
  to,
3301
3629
  value,
3302
- validAfter: validAfter.toString(),
3303
- validBefore: validBefore.toString(),
3304
- nonce
3305
- };
3306
- const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
3307
- const tokenVersion = domainOverride?.version || "2";
3308
- const domain = {
3309
- name: tokenName,
3310
- version: tokenVersion,
3630
+ nonce,
3311
3631
  chainId: chain.chainId,
3312
- verifyingContract: tokenConfig.address
3313
- };
3314
- const types = {
3315
- TransferWithAuthorization: [
3316
- { name: "from", type: "address" },
3317
- { name: "to", type: "address" },
3318
- { name: "value", type: "uint256" },
3319
- { name: "validAfter", type: "uint256" },
3320
- { name: "validBefore", type: "uint256" },
3321
- { name: "nonce", type: "bytes32" }
3322
- ]
3323
- };
3324
- const signature = await this.wallet.signTypedData(domain, types, authorization);
3325
- return { authorization, signature };
3632
+ tokenAddress: tokenConfig.address,
3633
+ tokenName,
3634
+ tokenVersion
3635
+ });
3636
+ const signature = await this.signer.signTypedData(envelope);
3637
+ return { authorization: envelope.message, signature };
3326
3638
  }
3327
3639
  /**
3328
3640
  * Check spending limits
@@ -3404,15 +3716,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3404
3716
  loadWallet() {
3405
3717
  const walletPath = (0, import_path2.join)(this.configDir, "wallet.json");
3406
3718
  if ((0, import_fs4.existsSync)(walletPath)) {
3407
- try {
3408
- const stats = (0, import_fs4.statSync)(walletPath);
3409
- const mode = stats.mode & 511;
3410
- if (mode !== 384) {
3411
- console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
3412
- console.warn(`[MoltsPay] Fixing permissions to 0600...`);
3413
- (0, import_fs4.chmodSync)(walletPath, 384);
3719
+ if (process.platform !== "win32") {
3720
+ try {
3721
+ const stats = (0, import_fs4.statSync)(walletPath);
3722
+ const mode = stats.mode & 511;
3723
+ if (mode !== 384) {
3724
+ console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
3725
+ console.warn(`[MoltsPay] Fixing permissions to 0600...`);
3726
+ (0, import_fs4.chmodSync)(walletPath, 384);
3727
+ }
3728
+ } catch {
3414
3729
  }
3415
- } catch (err) {
3416
3730
  }
3417
3731
  const content = (0, import_fs4.readFileSync)(walletPath, "utf-8");
3418
3732
  return JSON.parse(content);
@@ -3424,7 +3738,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3424
3738
  */
3425
3739
  static init(configDir, options) {
3426
3740
  (0, import_fs4.mkdirSync)(configDir, { recursive: true });
3427
- const wallet = import_ethers.Wallet.createRandom();
3741
+ const wallet = import_ethers3.Wallet.createRandom();
3428
3742
  const walletData = {
3429
3743
  address: wallet.address,
3430
3744
  privateKey: wallet.privateKey,
@@ -3456,17 +3770,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3456
3770
  } catch {
3457
3771
  throw new Error(`Unknown chain: ${this.config.chain}`);
3458
3772
  }
3459
- const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
3773
+ const provider = new import_ethers3.ethers.JsonRpcProvider(chain.rpc);
3460
3774
  const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
3461
3775
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
3462
3776
  provider.getBalance(this.wallet.address),
3463
- new import_ethers.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
3464
- new import_ethers.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
3777
+ new import_ethers3.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
3778
+ new import_ethers3.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
3465
3779
  ]);
3466
3780
  return {
3467
- usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
3468
- usdt: parseFloat(import_ethers.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
3469
- native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
3781
+ usdc: parseFloat(import_ethers3.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
3782
+ usdt: parseFloat(import_ethers3.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
3783
+ native: parseFloat(import_ethers3.ethers.formatEther(nativeBalance))
3470
3784
  };
3471
3785
  }
3472
3786
  /**
@@ -3489,38 +3803,38 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3489
3803
  supportedChains.map(async (chainName) => {
3490
3804
  try {
3491
3805
  const chain = getChain(chainName);
3492
- const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
3806
+ const provider = new import_ethers3.ethers.JsonRpcProvider(chain.rpc);
3493
3807
  if (chainName === "tempo_moderato") {
3494
3808
  const [nativeBalance, pathUSD, alphaUSD, betaUSD, thetaUSD] = await Promise.all([
3495
3809
  provider.getBalance(this.wallet.address),
3496
- new import_ethers.ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3497
- new import_ethers.ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3498
- new import_ethers.ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3499
- new import_ethers.ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
3810
+ new import_ethers3.ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3811
+ new import_ethers3.ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3812
+ new import_ethers3.ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3813
+ new import_ethers3.ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
3500
3814
  ]);
3501
3815
  results[chainName] = {
3502
- usdc: parseFloat(import_ethers.ethers.formatUnits(pathUSD, 6)),
3816
+ usdc: parseFloat(import_ethers3.ethers.formatUnits(pathUSD, 6)),
3503
3817
  // pathUSD as default USDC
3504
- usdt: parseFloat(import_ethers.ethers.formatUnits(alphaUSD, 6)),
3818
+ usdt: parseFloat(import_ethers3.ethers.formatUnits(alphaUSD, 6)),
3505
3819
  // alphaUSD as default USDT
3506
- native: parseFloat(import_ethers.ethers.formatEther(nativeBalance)),
3820
+ native: parseFloat(import_ethers3.ethers.formatEther(nativeBalance)),
3507
3821
  tempo: {
3508
- pathUSD: parseFloat(import_ethers.ethers.formatUnits(pathUSD, 6)),
3509
- alphaUSD: parseFloat(import_ethers.ethers.formatUnits(alphaUSD, 6)),
3510
- betaUSD: parseFloat(import_ethers.ethers.formatUnits(betaUSD, 6)),
3511
- thetaUSD: parseFloat(import_ethers.ethers.formatUnits(thetaUSD, 6))
3822
+ pathUSD: parseFloat(import_ethers3.ethers.formatUnits(pathUSD, 6)),
3823
+ alphaUSD: parseFloat(import_ethers3.ethers.formatUnits(alphaUSD, 6)),
3824
+ betaUSD: parseFloat(import_ethers3.ethers.formatUnits(betaUSD, 6)),
3825
+ thetaUSD: parseFloat(import_ethers3.ethers.formatUnits(thetaUSD, 6))
3512
3826
  }
3513
3827
  };
3514
3828
  } else {
3515
3829
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
3516
3830
  provider.getBalance(this.wallet.address),
3517
- new import_ethers.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
3518
- new import_ethers.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
3831
+ new import_ethers3.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
3832
+ new import_ethers3.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
3519
3833
  ]);
3520
3834
  results[chainName] = {
3521
- usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
3522
- usdt: parseFloat(import_ethers.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
3523
- native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
3835
+ usdc: parseFloat(import_ethers3.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
3836
+ usdt: parseFloat(import_ethers3.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
3837
+ native: parseFloat(import_ethers3.ethers.formatEther(nativeBalance))
3524
3838
  };
3525
3839
  }
3526
3840
  } catch (err) {
@@ -3648,10 +3962,10 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3648
3962
  };
3649
3963
 
3650
3964
  // src/wallet/Wallet.ts
3651
- var import_ethers2 = require("ethers");
3965
+ var import_ethers4 = require("ethers");
3652
3966
 
3653
3967
  // src/wallet/createWallet.ts
3654
- var import_ethers3 = require("ethers");
3968
+ var import_ethers5 = require("ethers");
3655
3969
  var import_fs5 = require("fs");
3656
3970
  var import_path3 = require("path");
3657
3971
  var import_crypto = require("crypto");
@@ -3696,7 +4010,7 @@ function createWallet(options = {}) {
3696
4010
  }
3697
4011
  }
3698
4012
  try {
3699
- const wallet = import_ethers3.ethers.Wallet.createRandom();
4013
+ const wallet = import_ethers5.ethers.Wallet.createRandom();
3700
4014
  const walletData = {
3701
4015
  address: wallet.address,
3702
4016
  label: options.label,
@@ -3768,8 +4082,8 @@ function walletExists(storagePath) {
3768
4082
  }
3769
4083
 
3770
4084
  // src/verify/index.ts
3771
- var import_ethers4 = require("ethers");
3772
- var TRANSFER_EVENT_TOPIC3 = import_ethers4.ethers.id("Transfer(address,address,uint256)");
4085
+ var import_ethers6 = require("ethers");
4086
+ var TRANSFER_EVENT_TOPIC3 = import_ethers6.ethers.id("Transfer(address,address,uint256)");
3773
4087
  async function verifyPayment(params) {
3774
4088
  const { txHash, expectedAmount, expectedTo, expectedToken } = params;
3775
4089
  let chain;
@@ -3786,7 +4100,7 @@ async function verifyPayment(params) {
3786
4100
  return { verified: false, error: `Unsupported chain: ${params.chain}` };
3787
4101
  }
3788
4102
  try {
3789
- const provider = new import_ethers4.ethers.JsonRpcProvider(chain.rpc);
4103
+ const provider = new import_ethers6.ethers.JsonRpcProvider(chain.rpc);
3790
4104
  const receipt = await provider.getTransactionReceipt(txHash);
3791
4105
  if (!receipt) {
3792
4106
  return { verified: false, error: "Transaction not found or not confirmed" };
@@ -3858,7 +4172,7 @@ async function getTransactionStatus(txHash, chain = "base") {
3858
4172
  return { status: "not_found" };
3859
4173
  }
3860
4174
  try {
3861
- const provider = new import_ethers4.ethers.JsonRpcProvider(chainConfig.rpc);
4175
+ const provider = new import_ethers6.ethers.JsonRpcProvider(chainConfig.rpc);
3862
4176
  const receipt = await provider.getTransactionReceipt(txHash);
3863
4177
  if (!receipt) {
3864
4178
  const tx = await provider.getTransaction(txHash);
@@ -3895,7 +4209,7 @@ async function waitForTransaction(txHash, chain = "base", confirmations = 1, tim
3895
4209
  } catch (e) {
3896
4210
  return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };
3897
4211
  }
3898
- const provider = new import_ethers4.ethers.JsonRpcProvider(chainConfig.rpc);
4212
+ const provider = new import_ethers6.ethers.JsonRpcProvider(chainConfig.rpc);
3899
4213
  try {
3900
4214
  const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
3901
4215
  if (!receipt) {
@@ -4035,9 +4349,9 @@ var CDPWallet = class {
4035
4349
  * Get USDC balance
4036
4350
  */
4037
4351
  async getBalance() {
4038
- const { ethers: ethers5 } = await import("ethers");
4039
- const provider = new ethers5.JsonRpcProvider(this.chainConfig.rpc);
4040
- const usdcContract = new ethers5.Contract(
4352
+ const { ethers: ethers7 } = await import("ethers");
4353
+ const provider = new ethers7.JsonRpcProvider(this.chainConfig.rpc);
4354
+ const usdcContract = new ethers7.Contract(
4041
4355
  this.chainConfig.usdc,
4042
4356
  ["function balanceOf(address) view returns (uint256)"],
4043
4357
  provider
@@ -4048,7 +4362,7 @@ var CDPWallet = class {
4048
4362
  ]);
4049
4363
  return {
4050
4364
  usdc: (Number(usdcBalance) / 1e6).toFixed(2),
4051
- eth: ethers5.formatEther(ethBalance)
4365
+ eth: ethers7.formatEther(ethBalance)
4052
4366
  };
4053
4367
  }
4054
4368
  /**
@@ -4066,7 +4380,7 @@ var CDPWallet = class {
4066
4380
  }
4067
4381
  try {
4068
4382
  const { CdpClient } = await import("@coinbase/cdp-sdk");
4069
- const { ethers: ethers5 } = await import("ethers");
4383
+ const { ethers: ethers7 } = await import("ethers");
4070
4384
  const cdp = new CdpClient({
4071
4385
  apiKeyId: creds.apiKeyId,
4072
4386
  apiKeySecret: creds.apiKeySecret,
@@ -4074,7 +4388,7 @@ var CDPWallet = class {
4074
4388
  });
4075
4389
  const account = await cdp.evm.getAccount({ address: this.address });
4076
4390
  const amountWei = BigInt(Math.floor(params.amount * 1e6));
4077
- const iface = new ethers5.Interface([
4391
+ const iface = new ethers7.Interface([
4078
4392
  "function transfer(address to, uint256 amount) returns (bool)"
4079
4393
  ]);
4080
4394
  const callData = iface.encodeFunctionData("transfer", [params.to, amountWei]);