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.mjs CHANGED
@@ -236,6 +236,9 @@ var CDPFacilitator = class extends BaseFacilitator {
236
236
  }
237
237
  };
238
238
 
239
+ // src/facilitators/tempo.ts
240
+ import { ethers } from "ethers";
241
+
239
242
  // src/chains/index.ts
240
243
  var CHAINS = {
241
244
  // ============ Mainnet ============
@@ -431,15 +434,38 @@ var ERC20_ABI = [
431
434
 
432
435
  // src/facilitators/tempo.ts
433
436
  var TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
437
+ var TIP20_PERMIT_ABI = [
438
+ "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
439
+ "function transferFrom(address from, address to, uint256 value) returns (bool)"
440
+ ];
434
441
  var TempoFacilitator = class extends BaseFacilitator {
435
442
  name = "tempo";
436
443
  displayName = "Tempo Testnet";
437
444
  supportedNetworks = ["eip155:42431"];
438
445
  // Tempo Moderato
439
446
  rpcUrl;
447
+ settlerWallet = null;
440
448
  constructor() {
441
449
  super();
442
450
  this.rpcUrl = CHAINS.tempo_moderato.rpc;
451
+ const settlerKey = process.env.TEMPO_SETTLER_KEY;
452
+ if (settlerKey) {
453
+ try {
454
+ const provider = new ethers.JsonRpcProvider(this.rpcUrl);
455
+ this.settlerWallet = new ethers.Wallet(settlerKey, provider);
456
+ } catch (err) {
457
+ console.warn("[TempoFacilitator] Invalid TEMPO_SETTLER_KEY, permit settlement disabled:", err);
458
+ this.settlerWallet = null;
459
+ }
460
+ }
461
+ }
462
+ /**
463
+ * Settler EOA address advertised to clients via `X-Payment-Required.extra.tempoSpender`.
464
+ * Web Client uses this as the `spender` field in the signed EIP-2612 Permit.
465
+ * Returns null if no TEMPO_SETTLER_KEY is configured — permit settlement unavailable.
466
+ */
467
+ getSpenderAddress() {
468
+ return this.settlerWallet?.address ?? null;
443
469
  }
444
470
  async healthCheck() {
445
471
  const start = Date.now();
@@ -465,6 +491,44 @@ var TempoFacilitator = class extends BaseFacilitator {
465
491
  }
466
492
  }
467
493
  async verify(paymentPayload, requirements) {
494
+ const inner = paymentPayload.payload;
495
+ if (inner && "permit" in inner && inner.permit) {
496
+ return this.verifyPermit(inner, requirements);
497
+ }
498
+ return this.verifyTxHash(paymentPayload, requirements);
499
+ }
500
+ /**
501
+ * Structural validation of an EIP-2612 permit payload. Does NOT submit
502
+ * anything on-chain — actual submission happens in settlePermit().
503
+ */
504
+ async verifyPermit(payload, requirements) {
505
+ if (!this.settlerWallet) {
506
+ return { valid: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
507
+ }
508
+ const p = payload.permit;
509
+ if (!p || !p.owner || !p.spender || !p.value || !p.deadline) {
510
+ return { valid: false, error: "Invalid permit payload: missing fields" };
511
+ }
512
+ if (p.spender.toLowerCase() !== this.settlerWallet.address.toLowerCase()) {
513
+ return {
514
+ valid: false,
515
+ error: `Permit spender ${p.spender} does not match configured settler ${this.settlerWallet.address}`
516
+ };
517
+ }
518
+ const deadline = BigInt(p.deadline);
519
+ const now = BigInt(Math.floor(Date.now() / 1e3));
520
+ if (deadline <= now) {
521
+ return { valid: false, error: "Permit deadline has expired" };
522
+ }
523
+ if (BigInt(p.value) < BigInt(requirements.amount || "0")) {
524
+ return {
525
+ valid: false,
526
+ error: `Permit value ${p.value} is less than required ${requirements.amount}`
527
+ };
528
+ }
529
+ return { valid: true, details: { scheme: "permit", owner: p.owner } };
530
+ }
531
+ async verifyTxHash(paymentPayload, requirements) {
468
532
  try {
469
533
  const tempoPayload = paymentPayload.payload;
470
534
  if (!tempoPayload?.txHash) {
@@ -522,7 +586,11 @@ var TempoFacilitator = class extends BaseFacilitator {
522
586
  }
523
587
  }
524
588
  async settle(paymentPayload, requirements) {
525
- const verifyResult = await this.verify(paymentPayload, requirements);
589
+ const inner = paymentPayload.payload;
590
+ if (inner && "permit" in inner && inner.permit) {
591
+ return this.settlePermit(inner, requirements);
592
+ }
593
+ const verifyResult = await this.verifyTxHash(paymentPayload, requirements);
526
594
  if (!verifyResult.valid) {
527
595
  return { success: false, error: verifyResult.error };
528
596
  }
@@ -533,6 +601,52 @@ var TempoFacilitator = class extends BaseFacilitator {
533
601
  status: "settled"
534
602
  };
535
603
  }
604
+ /**
605
+ * EIP-2612 permit settlement path. Submits two transactions on Tempo:
606
+ * 1. pathUSD.permit(owner, spender=settler, value, deadline, v, r, s)
607
+ * 2. pathUSD.transferFrom(owner, payTo, value)
608
+ *
609
+ * The settler EOA pays Tempo gas (via the TIP-20 `feeToken` mechanism — no
610
+ * native tTEMPO required; any held TIP-20 token balance covers fees).
611
+ */
612
+ async settlePermit(payload, requirements) {
613
+ if (!this.settlerWallet) {
614
+ return { success: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
615
+ }
616
+ if (!requirements.asset || !requirements.payTo) {
617
+ return { success: false, error: "Missing asset or payTo in requirements" };
618
+ }
619
+ const verifyResult = await this.verifyPermit(payload, requirements);
620
+ if (!verifyResult.valid) {
621
+ return { success: false, error: verifyResult.error };
622
+ }
623
+ const token = new ethers.Contract(requirements.asset, TIP20_PERMIT_ABI, this.settlerWallet);
624
+ const p = payload.permit;
625
+ try {
626
+ const permitTx = await token.permit(
627
+ p.owner,
628
+ p.spender,
629
+ p.value,
630
+ p.deadline,
631
+ p.v,
632
+ p.r,
633
+ p.s
634
+ );
635
+ await permitTx.wait();
636
+ const transferTx = await token.transferFrom(p.owner, requirements.payTo, p.value);
637
+ await transferTx.wait();
638
+ return {
639
+ success: true,
640
+ transaction: transferTx.hash,
641
+ status: "settled"
642
+ };
643
+ } catch (err) {
644
+ return {
645
+ success: false,
646
+ error: `Tempo permit settlement failed: ${err.message}`
647
+ };
648
+ }
649
+ }
536
650
  async getTransactionReceipt(txHash) {
537
651
  const response = await fetch(this.rpcUrl, {
538
652
  method: "POST",
@@ -775,12 +889,12 @@ var BNBFacilitator = class extends BaseFacilitator {
775
889
  return this.spenderAddress;
776
890
  }
777
891
  async getServerAddress() {
778
- const { ethers: ethers5 } = await import("ethers");
779
- const wallet = new ethers5.Wallet(this.serverPrivateKey);
892
+ const { ethers: ethers7 } = await import("ethers");
893
+ const wallet = new ethers7.Wallet(this.serverPrivateKey);
780
894
  return wallet.address;
781
895
  }
782
896
  async recoverIntentSigner(intent, chainId) {
783
- const { ethers: ethers5 } = await import("ethers");
897
+ const { ethers: ethers7 } = await import("ethers");
784
898
  const domain = {
785
899
  ...EIP712_DOMAIN,
786
900
  chainId
@@ -794,7 +908,7 @@ var BNBFacilitator = class extends BaseFacilitator {
794
908
  nonce: intent.nonce,
795
909
  deadline: intent.deadline
796
910
  };
797
- const recoveredAddress = ethers5.verifyTypedData(
911
+ const recoveredAddress = ethers7.verifyTypedData(
798
912
  domain,
799
913
  INTENT_TYPES,
800
914
  message,
@@ -838,10 +952,10 @@ var BNBFacilitator = class extends BaseFacilitator {
838
952
  return result.result || "0x0";
839
953
  }
840
954
  async executeTransferFrom(from, to, amount, token, rpcUrl) {
841
- const { ethers: ethers5 } = await import("ethers");
842
- const provider = new ethers5.JsonRpcProvider(rpcUrl);
843
- const wallet = new ethers5.Wallet(this.serverPrivateKey, provider);
844
- const tokenContract = new ethers5.Contract(token, [
955
+ const { ethers: ethers7 } = await import("ethers");
956
+ const provider = new ethers7.JsonRpcProvider(rpcUrl);
957
+ const wallet = new ethers7.Wallet(this.serverPrivateKey, provider);
958
+ const tokenContract = new ethers7.Contract(token, [
845
959
  "function transferFrom(address from, address to, uint256 amount) returns (bool)"
846
960
  ], wallet);
847
961
  const tx = await tokenContract.transferFrom(from, to, amount);
@@ -1077,16 +1191,16 @@ var SolanaFacilitator = class extends BaseFacilitator {
1077
1191
  return this.supportedNetworks.includes(network);
1078
1192
  }
1079
1193
  };
1080
- async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey) {
1194
+ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey, connection) {
1081
1195
  const chainConfig = SOLANA_CHAINS[chain];
1082
- const connection = new Connection2(chainConfig.rpc, "confirmed");
1196
+ const conn = connection ?? new Connection2(chainConfig.rpc, "confirmed");
1083
1197
  const mint = new PublicKey2(chainConfig.tokens.USDC.mint);
1084
1198
  const actualFeePayer = feePayerPubkey || senderPubkey;
1085
1199
  const senderATA = await getAssociatedTokenAddress(mint, senderPubkey);
1086
1200
  const recipientATA = await getAssociatedTokenAddress(mint, recipientPubkey);
1087
1201
  const transaction = new Transaction();
1088
1202
  try {
1089
- await getAccount(connection, recipientATA);
1203
+ await getAccount(conn, recipientATA);
1090
1204
  } catch {
1091
1205
  transaction.add(
1092
1206
  createAssociatedTokenAccountInstruction(
@@ -1117,7 +1231,7 @@ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amo
1117
1231
  // decimals
1118
1232
  )
1119
1233
  );
1120
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1234
+ const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();
1121
1235
  transaction.recentBlockhash = blockhash;
1122
1236
  transaction.feePayer = actualFeePayer;
1123
1237
  return transaction;
@@ -1453,9 +1567,13 @@ var TOKEN_DOMAINS = {
1453
1567
  USDT: { name: "(PoS) Tether USD", version: "2" }
1454
1568
  },
1455
1569
  // Tempo Moderato testnet - TIP-20 stablecoins
1570
+ // Domain names verified against on-chain DOMAIN_SEPARATOR values on 2026-04-21.
1571
+ // See docs/TEMPO-WEB-SUPPORT.md Section 2 and test/server/tempo-domain.test.ts.
1572
+ // All 4 Tempo TIP-20 tokens (pathUSD / AlphaUSD / BetaUSD / ThetaUSD) use
1573
+ // the token symbol with first letter capitalized + version "1".
1456
1574
  "eip155:42431": {
1457
- USDC: { name: "pathUSD", version: "1" },
1458
- USDT: { name: "alphaUSD", version: "1" }
1575
+ USDC: { name: "PathUSD", version: "1" },
1576
+ USDT: { name: "AlphaUSD", version: "1" }
1459
1577
  },
1460
1578
  // BNB Smart Chain mainnet
1461
1579
  "eip155:56": {
@@ -1624,13 +1742,62 @@ var MoltsPayServer = class {
1624
1742
  });
1625
1743
  }
1626
1744
  /**
1627
- * Handle incoming request
1745
+ * Apply CORS response headers according to the `cors` option.
1746
+ *
1747
+ * Default (`cors` unset or `true`): `Access-Control-Allow-Origin: *`. Matches 1.5.x behavior
1748
+ * and works for every browser client whose origin does not need to send cookies.
1749
+ *
1750
+ * `cors: false`: emit no CORS headers. Same-origin only.
1751
+ * `cors: string[]`: origin allowlist — echo the origin back iff it matches.
1752
+ * `cors: CorsOptions`: full control (allowlist + credentials + maxAge).
1753
+ *
1754
+ * The required-for-Web response headers are always exposed when CORS is active:
1755
+ * `X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt`.
1628
1756
  */
1629
- async handleRequest(req, res) {
1630
- res.setHeader("Access-Control-Allow-Origin", "*");
1757
+ applyCorsHeaders(req, res) {
1758
+ const cors = this.options.cors;
1759
+ if (cors === false) {
1760
+ return;
1761
+ }
1762
+ const requestOrigin = req.headers.origin ?? "*";
1763
+ if (cors === void 0 || cors === true) {
1764
+ this.writeCorsHeaders(res, "*");
1765
+ return;
1766
+ }
1767
+ if (Array.isArray(cors)) {
1768
+ if (cors.includes(requestOrigin)) {
1769
+ this.writeCorsHeaders(res, requestOrigin);
1770
+ res.setHeader("Vary", "Origin");
1771
+ }
1772
+ return;
1773
+ }
1774
+ const opt = cors;
1775
+ const isAllowed = typeof opt.origins === "function" ? opt.origins(requestOrigin) : opt.origins.includes(requestOrigin);
1776
+ if (!isAllowed) {
1777
+ return;
1778
+ }
1779
+ this.writeCorsHeaders(res, requestOrigin);
1780
+ res.setHeader("Vary", "Origin");
1781
+ if (opt.credentials) {
1782
+ res.setHeader("Access-Control-Allow-Credentials", "true");
1783
+ }
1784
+ const maxAge = opt.maxAge ?? 600;
1785
+ res.setHeader("Access-Control-Max-Age", String(maxAge));
1786
+ }
1787
+ writeCorsHeaders(res, origin) {
1788
+ res.setHeader("Access-Control-Allow-Origin", origin);
1631
1789
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
1632
1790
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
1633
- res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt");
1791
+ res.setHeader(
1792
+ "Access-Control-Expose-Headers",
1793
+ "X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt"
1794
+ );
1795
+ }
1796
+ /**
1797
+ * Handle incoming request
1798
+ */
1799
+ async handleRequest(req, res) {
1800
+ this.applyCorsHeaders(req, res);
1634
1801
  if (req.method === "OPTIONS") {
1635
1802
  res.writeHead(204);
1636
1803
  res.end();
@@ -1853,6 +2020,14 @@ var MoltsPayServer = class {
1853
2020
  console.log(`[MoltsPay] Payment settled by ${settlement.facilitator}: ${settlement.transaction || "pending"}`);
1854
2021
  } catch (err) {
1855
2022
  console.error("[MoltsPay] Settlement failed:", err.message);
2023
+ settlement = { success: false, error: err.message, facilitator: "none" };
2024
+ }
2025
+ if (!settlement?.success) {
2026
+ return this.sendJson(res, 402, {
2027
+ error: "Payment settlement failed",
2028
+ message: settlement?.error || "Settlement returned no success state",
2029
+ facilitator: settlement?.facilitator
2030
+ });
1856
2031
  }
1857
2032
  }
1858
2033
  const responseHeaders = {};
@@ -2107,7 +2282,7 @@ var MoltsPayServer = class {
2107
2282
  }
2108
2283
  const scheme = payment.accepted?.scheme || payment.scheme;
2109
2284
  const network = payment.accepted?.network || payment.network || this.networkId;
2110
- if (scheme !== "exact") {
2285
+ if (scheme !== "exact" && scheme !== "permit") {
2111
2286
  return { valid: false, error: `Unsupported scheme: ${scheme}` };
2112
2287
  }
2113
2288
  if (!this.isNetworkAccepted(network)) {
@@ -2129,8 +2304,10 @@ var MoltsPayServer = class {
2129
2304
  const tokenAddresses = TOKEN_ADDRESSES[selectedNetwork] || {};
2130
2305
  const tokenAddress = tokenAddresses[selectedToken];
2131
2306
  const tokenDomain = getTokenDomain(selectedNetwork, selectedToken);
2307
+ const isTempo = selectedNetwork === "eip155:42431";
2308
+ const scheme = isTempo ? "permit" : "exact";
2132
2309
  const requirements = {
2133
- scheme: "exact",
2310
+ scheme,
2134
2311
  network: selectedNetwork,
2135
2312
  asset: tokenAddress,
2136
2313
  amount: amountInUnits,
@@ -2158,6 +2335,16 @@ var MoltsPayServer = class {
2158
2335
  };
2159
2336
  }
2160
2337
  }
2338
+ if (isTempo) {
2339
+ const tempoFacilitator = this.registry.get("tempo");
2340
+ const tempoSpender = tempoFacilitator?.getSpenderAddress?.();
2341
+ if (tempoSpender) {
2342
+ requirements.extra = {
2343
+ ...requirements.extra || {},
2344
+ tempoSpender
2345
+ };
2346
+ }
2347
+ }
2161
2348
  return requirements;
2162
2349
  }
2163
2350
  /**
@@ -2292,7 +2479,7 @@ var MoltsPayServer = class {
2292
2479
  }
2293
2480
  const scheme = payment.accepted?.scheme || payment.scheme;
2294
2481
  const network = payment.accepted?.network || payment.network;
2295
- if (scheme !== "exact") {
2482
+ if (scheme !== "exact" && scheme !== "permit") {
2296
2483
  return this.sendJson(res, 402, { error: `Unsupported scheme: ${scheme}` });
2297
2484
  }
2298
2485
  const expectedNetwork = chain ? CHAIN_TO_NETWORK[chain] || this.networkId : this.networkId;
@@ -2612,11 +2799,11 @@ var MoltsPayServer = class {
2612
2799
  }
2613
2800
  };
2614
2801
 
2615
- // src/client/index.ts
2802
+ // src/client/node/index.ts
2616
2803
  import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, statSync, chmodSync } from "fs";
2617
2804
  import { homedir as homedir2 } from "os";
2618
2805
  import { join as join4 } from "path";
2619
- import { Wallet, ethers } from "ethers";
2806
+ import { Wallet as Wallet2, ethers as ethers3 } from "ethers";
2620
2807
 
2621
2808
  // src/wallet/solana.ts
2622
2809
  import { Keypair as Keypair3, PublicKey as PublicKey3, LAMPORTS_PER_SOL } from "@solana/web3.js";
@@ -2645,11 +2832,172 @@ function loadSolanaWallet(configDir = DEFAULT_CONFIG_DIR) {
2645
2832
  }
2646
2833
  }
2647
2834
 
2648
- // src/client/index.ts
2835
+ // src/client/node/index.ts
2649
2836
  import { PublicKey as PublicKey4 } from "@solana/web3.js";
2837
+
2838
+ // src/client/core/types.ts
2650
2839
  var X402_VERSION3 = 2;
2651
2840
  var PAYMENT_REQUIRED_HEADER2 = "x-payment-required";
2652
2841
  var PAYMENT_HEADER2 = "x-payment";
2842
+
2843
+ // src/client/core/chain-map.ts
2844
+ var NETWORK_TO_CHAIN = {
2845
+ "eip155:8453": "base",
2846
+ "eip155:137": "polygon",
2847
+ "eip155:84532": "base_sepolia",
2848
+ "eip155:42431": "tempo_moderato",
2849
+ "eip155:56": "bnb",
2850
+ "eip155:97": "bnb_testnet",
2851
+ "solana:mainnet": "solana",
2852
+ "solana:devnet": "solana_devnet"
2853
+ };
2854
+ var CHAIN_TO_NETWORK2 = Object.fromEntries(
2855
+ Object.entries(NETWORK_TO_CHAIN).map(([network, chain]) => [chain, network])
2856
+ );
2857
+ function networkToChainName(network) {
2858
+ return NETWORK_TO_CHAIN[network] ?? null;
2859
+ }
2860
+
2861
+ // src/client/core/base64.ts
2862
+ var BufferCtor = globalThis.Buffer;
2863
+
2864
+ // src/client/core/eip3009.ts
2865
+ var EIP3009_TYPES = {
2866
+ TransferWithAuthorization: [
2867
+ { name: "from", type: "address" },
2868
+ { name: "to", type: "address" },
2869
+ { name: "value", type: "uint256" },
2870
+ { name: "validAfter", type: "uint256" },
2871
+ { name: "validBefore", type: "uint256" },
2872
+ { name: "nonce", type: "bytes32" }
2873
+ ]
2874
+ };
2875
+ function buildEIP3009TypedData(args) {
2876
+ const validAfter = args.validAfter ?? "0";
2877
+ const validBefore = args.validBefore ?? (Math.floor(Date.now() / 1e3) + 3600).toString();
2878
+ const authorization = {
2879
+ from: args.from,
2880
+ to: args.to,
2881
+ value: args.value,
2882
+ validAfter,
2883
+ validBefore,
2884
+ nonce: args.nonce
2885
+ };
2886
+ return {
2887
+ domain: {
2888
+ name: args.tokenName,
2889
+ version: args.tokenVersion,
2890
+ chainId: args.chainId,
2891
+ verifyingContract: args.tokenAddress
2892
+ },
2893
+ types: EIP3009_TYPES,
2894
+ primaryType: "TransferWithAuthorization",
2895
+ message: authorization
2896
+ };
2897
+ }
2898
+
2899
+ // src/client/core/bnb-intent.ts
2900
+ var BNB_INTENT_TYPES = {
2901
+ PaymentIntent: [
2902
+ { name: "from", type: "address" },
2903
+ { name: "to", type: "address" },
2904
+ { name: "amount", type: "uint256" },
2905
+ { name: "token", type: "address" },
2906
+ { name: "service", type: "string" },
2907
+ { name: "nonce", type: "uint256" },
2908
+ { name: "deadline", type: "uint256" }
2909
+ ]
2910
+ };
2911
+ var BNB_DOMAIN_NAME = "MoltsPay";
2912
+ var BNB_DOMAIN_VERSION = "1";
2913
+ function buildBnbIntentTypedData(args) {
2914
+ const intent = {
2915
+ from: args.from,
2916
+ to: args.to,
2917
+ amount: args.amount,
2918
+ token: args.tokenAddress,
2919
+ service: args.service,
2920
+ nonce: args.nonce,
2921
+ deadline: args.deadline
2922
+ };
2923
+ return {
2924
+ domain: {
2925
+ name: BNB_DOMAIN_NAME,
2926
+ version: BNB_DOMAIN_VERSION,
2927
+ chainId: args.chainId
2928
+ },
2929
+ types: BNB_INTENT_TYPES,
2930
+ primaryType: "PaymentIntent",
2931
+ message: intent
2932
+ };
2933
+ }
2934
+
2935
+ // src/client/node/signer.ts
2936
+ import { ethers as ethers2 } from "ethers";
2937
+ import { Transaction as Transaction2 } from "@solana/web3.js";
2938
+ var NodeSigner = class {
2939
+ evmWallet;
2940
+ getSolanaKeypair;
2941
+ constructor(evmWallet, options = {}) {
2942
+ this.evmWallet = evmWallet;
2943
+ this.getSolanaKeypair = options.getSolanaKeypair ?? (() => null);
2944
+ }
2945
+ async getEvmAddress() {
2946
+ return this.evmWallet.address;
2947
+ }
2948
+ async getSolanaAddress() {
2949
+ const kp = this.getSolanaKeypair();
2950
+ return kp ? kp.publicKey.toBase58() : null;
2951
+ }
2952
+ async signTypedData(envelope) {
2953
+ const mutableTypes = {};
2954
+ for (const [key, fields] of Object.entries(envelope.types)) {
2955
+ mutableTypes[key] = [...fields];
2956
+ }
2957
+ return this.evmWallet.signTypedData(
2958
+ envelope.domain,
2959
+ mutableTypes,
2960
+ envelope.message
2961
+ );
2962
+ }
2963
+ async sendEvmTransaction(args) {
2964
+ const chain = findChainByChainId(args.chainId);
2965
+ if (!chain) {
2966
+ throw new Error(`sendEvmTransaction: unknown chainId ${args.chainId}`);
2967
+ }
2968
+ const provider = new ethers2.JsonRpcProvider(chain.rpc);
2969
+ const connected = this.evmWallet.connect(provider);
2970
+ const tx = await connected.sendTransaction({
2971
+ to: args.to,
2972
+ data: args.data,
2973
+ value: args.value ? BigInt(args.value) : 0n
2974
+ });
2975
+ return tx.hash;
2976
+ }
2977
+ async signSolanaTransaction(args) {
2978
+ const kp = this.getSolanaKeypair();
2979
+ if (!kp) {
2980
+ throw new Error("signSolanaTransaction: no Solana wallet configured");
2981
+ }
2982
+ const tx = Transaction2.from(Buffer.from(args.transactionBase64, "base64"));
2983
+ if (args.partialSign) {
2984
+ tx.partialSign(kp);
2985
+ } else {
2986
+ tx.sign(kp);
2987
+ }
2988
+ return tx.serialize({ requireAllSignatures: false }).toString("base64");
2989
+ }
2990
+ };
2991
+ function findChainByChainId(chainId) {
2992
+ for (const cfg of Object.values(CHAINS)) {
2993
+ if (cfg.chainId === chainId) {
2994
+ return cfg;
2995
+ }
2996
+ }
2997
+ return void 0;
2998
+ }
2999
+
3000
+ // src/client/node/index.ts
2653
3001
  var DEFAULT_CONFIG = {
2654
3002
  chain: "base",
2655
3003
  limits: {
@@ -2662,6 +3010,7 @@ var MoltsPayClient = class {
2662
3010
  config;
2663
3011
  walletData = null;
2664
3012
  wallet = null;
3013
+ signer = null;
2665
3014
  todaySpending = 0;
2666
3015
  lastSpendingReset = 0;
2667
3016
  constructor(options = {}) {
@@ -2670,7 +3019,11 @@ var MoltsPayClient = class {
2670
3019
  this.walletData = this.loadWallet();
2671
3020
  this.loadSpending();
2672
3021
  if (this.walletData) {
2673
- this.wallet = new Wallet(this.walletData.privateKey);
3022
+ this.wallet = new Wallet2(this.walletData.privateKey);
3023
+ const configDir = this.configDir;
3024
+ this.signer = new NodeSigner(this.wallet, {
3025
+ getSolanaKeypair: () => loadSolanaWallet(configDir)
3026
+ });
2674
3027
  }
2675
3028
  }
2676
3029
  /**
@@ -2798,20 +3151,6 @@ var MoltsPayClient = class {
2798
3151
  } catch {
2799
3152
  throw new Error("Invalid x-payment-required header");
2800
3153
  }
2801
- const networkToChainName = (network2) => {
2802
- if (network2 === "solana:mainnet") return "solana";
2803
- if (network2 === "solana:devnet") return "solana_devnet";
2804
- const match = network2.match(/^eip155:(\d+)$/);
2805
- if (!match) return null;
2806
- const chainId = parseInt(match[1]);
2807
- if (chainId === 8453) return "base";
2808
- if (chainId === 137) return "polygon";
2809
- if (chainId === 84532) return "base_sepolia";
2810
- if (chainId === 42431) return "tempo_moderato";
2811
- if (chainId === 56) return "bnb";
2812
- if (chainId === 97) return "bnb_testnet";
2813
- return null;
2814
- };
2815
3154
  const serverChains = requirements.map((r) => networkToChainName(r.network)).filter((c) => c !== null);
2816
3155
  const userSpecifiedChain = options.chain;
2817
3156
  let selectedChain;
@@ -3040,14 +3379,14 @@ Please specify: --chain <chain_name>`
3040
3379
  async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
3041
3380
  const { to, amount, token, chainName, chain, spender } = paymentDetails;
3042
3381
  const tokenConfig = chain.tokens[token];
3043
- const provider = new ethers.JsonRpcProvider(chain.rpc);
3382
+ const provider = new ethers3.JsonRpcProvider(chain.rpc);
3044
3383
  const allowance = await this.checkAllowance(tokenConfig.address, spender, provider);
3045
3384
  const amountWeiCheck = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals));
3046
3385
  if (allowance < amountWeiCheck) {
3047
3386
  const nativeBalance = await provider.getBalance(this.wallet.address);
3048
- const minGasBalance = ethers.parseEther("0.0005");
3387
+ const minGasBalance = ethers3.parseEther("0.0005");
3049
3388
  if (nativeBalance < minGasBalance) {
3050
- const nativeBNB = parseFloat(ethers.formatEther(nativeBalance)).toFixed(4);
3389
+ const nativeBNB = parseFloat(ethers3.formatEther(nativeBalance)).toFixed(4);
3051
3390
  const isTestnet = chainName === "bnb_testnet";
3052
3391
  if (isTestnet) {
3053
3392
  throw new Error(
@@ -3081,35 +3420,21 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3081
3420
  );
3082
3421
  }
3083
3422
  const amountWei = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
3084
- const intent = {
3423
+ const intentNonce = Date.now();
3424
+ const intentDeadline = Date.now() + 36e5;
3425
+ const envelope = buildBnbIntentTypedData({
3085
3426
  from: this.wallet.address,
3086
3427
  to,
3087
3428
  amount: amountWei,
3088
- token: tokenConfig.address,
3429
+ tokenAddress: tokenConfig.address,
3089
3430
  service,
3090
- nonce: Date.now(),
3091
- // Use timestamp as nonce for simplicity
3092
- deadline: Date.now() + 36e5
3093
- // 1 hour
3094
- };
3095
- const domain = {
3096
- name: "MoltsPay",
3097
- version: "1",
3431
+ nonce: intentNonce,
3432
+ deadline: intentDeadline,
3098
3433
  chainId: chain.chainId
3099
- };
3100
- const types = {
3101
- PaymentIntent: [
3102
- { name: "from", type: "address" },
3103
- { name: "to", type: "address" },
3104
- { name: "amount", type: "uint256" },
3105
- { name: "token", type: "address" },
3106
- { name: "service", type: "string" },
3107
- { name: "nonce", type: "uint256" },
3108
- { name: "deadline", type: "uint256" }
3109
- ]
3110
- };
3434
+ });
3111
3435
  console.log(`[MoltsPay] Signing BNB payment intent...`);
3112
- const signature = await this.wallet.signTypedData(domain, types, intent);
3436
+ const signature = await this.signer.signTypedData(envelope);
3437
+ const intent = envelope.message;
3113
3438
  const network = `eip155:${chain.chainId}`;
3114
3439
  const payload = {
3115
3440
  x402Version: 2,
@@ -3183,12 +3508,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3183
3508
  feePayerPubkey
3184
3509
  // Optional fee payer for gasless mode
3185
3510
  );
3186
- if (feePayerPubkey) {
3187
- transaction.partialSign(solanaWallet);
3188
- } else {
3189
- transaction.sign(solanaWallet);
3190
- }
3191
- const signedTx = transaction.serialize({ requireAllSignatures: false }).toString("base64");
3511
+ const unsignedBase64 = transaction.serialize({ requireAllSignatures: false, verifySignatures: false }).toString("base64");
3512
+ const signedTx = await this.signer.signSolanaTransaction({
3513
+ transactionBase64: unsignedBase64,
3514
+ partialSign: !!feePayerPubkey
3515
+ });
3192
3516
  console.log(`[MoltsPay] Transaction signed, sending to server...`);
3193
3517
  const network = chain === "solana" ? "solana:mainnet" : "solana:devnet";
3194
3518
  const payload = {
@@ -3235,7 +3559,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3235
3559
  * Check ERC20 allowance for a spender
3236
3560
  */
3237
3561
  async checkAllowance(tokenAddress, spender, provider) {
3238
- const contract = new ethers.Contract(
3562
+ const contract = new ethers3.Contract(
3239
3563
  tokenAddress,
3240
3564
  ["function allowance(address owner, address spender) view returns (uint256)"],
3241
3565
  provider
@@ -3246,41 +3570,29 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3246
3570
  * Sign EIP-3009 transferWithAuthorization (GASLESS)
3247
3571
  * This only signs - no on-chain transaction, no gas needed.
3248
3572
  * Supports both USDC and USDT.
3573
+ *
3574
+ * Delegates typed-data construction to `core/eip3009.ts` and the signature
3575
+ * itself to `this.signer`. That way Web Client (Phase 4) can reuse the same
3576
+ * flow with an EIP-1193 signer without duplicating typed-data layout.
3249
3577
  */
3250
3578
  async signEIP3009(to, amount, chain, token = "USDC", domainOverride) {
3251
- const validAfter = 0;
3252
- const validBefore = Math.floor(Date.now() / 1e3) + 3600;
3253
- const nonce = ethers.hexlify(ethers.randomBytes(32));
3254
3579
  const tokenConfig = chain.tokens[token];
3255
3580
  const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
3256
- const authorization = {
3581
+ const nonce = ethers3.hexlify(ethers3.randomBytes(32));
3582
+ const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
3583
+ const tokenVersion = domainOverride?.version || "2";
3584
+ const envelope = buildEIP3009TypedData({
3257
3585
  from: this.wallet.address,
3258
3586
  to,
3259
3587
  value,
3260
- validAfter: validAfter.toString(),
3261
- validBefore: validBefore.toString(),
3262
- nonce
3263
- };
3264
- const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
3265
- const tokenVersion = domainOverride?.version || "2";
3266
- const domain = {
3267
- name: tokenName,
3268
- version: tokenVersion,
3588
+ nonce,
3269
3589
  chainId: chain.chainId,
3270
- verifyingContract: tokenConfig.address
3271
- };
3272
- const types = {
3273
- TransferWithAuthorization: [
3274
- { name: "from", type: "address" },
3275
- { name: "to", type: "address" },
3276
- { name: "value", type: "uint256" },
3277
- { name: "validAfter", type: "uint256" },
3278
- { name: "validBefore", type: "uint256" },
3279
- { name: "nonce", type: "bytes32" }
3280
- ]
3281
- };
3282
- const signature = await this.wallet.signTypedData(domain, types, authorization);
3283
- return { authorization, signature };
3590
+ tokenAddress: tokenConfig.address,
3591
+ tokenName,
3592
+ tokenVersion
3593
+ });
3594
+ const signature = await this.signer.signTypedData(envelope);
3595
+ return { authorization: envelope.message, signature };
3284
3596
  }
3285
3597
  /**
3286
3598
  * Check spending limits
@@ -3362,15 +3674,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3362
3674
  loadWallet() {
3363
3675
  const walletPath = join4(this.configDir, "wallet.json");
3364
3676
  if (existsSync4(walletPath)) {
3365
- try {
3366
- const stats = statSync(walletPath);
3367
- const mode = stats.mode & 511;
3368
- if (mode !== 384) {
3369
- console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
3370
- console.warn(`[MoltsPay] Fixing permissions to 0600...`);
3371
- chmodSync(walletPath, 384);
3677
+ if (process.platform !== "win32") {
3678
+ try {
3679
+ const stats = statSync(walletPath);
3680
+ const mode = stats.mode & 511;
3681
+ if (mode !== 384) {
3682
+ console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
3683
+ console.warn(`[MoltsPay] Fixing permissions to 0600...`);
3684
+ chmodSync(walletPath, 384);
3685
+ }
3686
+ } catch {
3372
3687
  }
3373
- } catch (err) {
3374
3688
  }
3375
3689
  const content = readFileSync4(walletPath, "utf-8");
3376
3690
  return JSON.parse(content);
@@ -3382,7 +3696,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3382
3696
  */
3383
3697
  static init(configDir, options) {
3384
3698
  mkdirSync2(configDir, { recursive: true });
3385
- const wallet = Wallet.createRandom();
3699
+ const wallet = Wallet2.createRandom();
3386
3700
  const walletData = {
3387
3701
  address: wallet.address,
3388
3702
  privateKey: wallet.privateKey,
@@ -3414,17 +3728,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3414
3728
  } catch {
3415
3729
  throw new Error(`Unknown chain: ${this.config.chain}`);
3416
3730
  }
3417
- const provider = new ethers.JsonRpcProvider(chain.rpc);
3731
+ const provider = new ethers3.JsonRpcProvider(chain.rpc);
3418
3732
  const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
3419
3733
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
3420
3734
  provider.getBalance(this.wallet.address),
3421
- new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
3422
- new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
3735
+ new ethers3.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
3736
+ new ethers3.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
3423
3737
  ]);
3424
3738
  return {
3425
- usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
3426
- usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
3427
- native: parseFloat(ethers.formatEther(nativeBalance))
3739
+ usdc: parseFloat(ethers3.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
3740
+ usdt: parseFloat(ethers3.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
3741
+ native: parseFloat(ethers3.formatEther(nativeBalance))
3428
3742
  };
3429
3743
  }
3430
3744
  /**
@@ -3447,38 +3761,38 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3447
3761
  supportedChains.map(async (chainName) => {
3448
3762
  try {
3449
3763
  const chain = getChain(chainName);
3450
- const provider = new ethers.JsonRpcProvider(chain.rpc);
3764
+ const provider = new ethers3.JsonRpcProvider(chain.rpc);
3451
3765
  if (chainName === "tempo_moderato") {
3452
3766
  const [nativeBalance, pathUSD, alphaUSD, betaUSD, thetaUSD] = await Promise.all([
3453
3767
  provider.getBalance(this.wallet.address),
3454
- new ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3455
- new ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3456
- new ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3457
- new ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
3768
+ new ethers3.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3769
+ new ethers3.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3770
+ new ethers3.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
3771
+ new ethers3.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
3458
3772
  ]);
3459
3773
  results[chainName] = {
3460
- usdc: parseFloat(ethers.formatUnits(pathUSD, 6)),
3774
+ usdc: parseFloat(ethers3.formatUnits(pathUSD, 6)),
3461
3775
  // pathUSD as default USDC
3462
- usdt: parseFloat(ethers.formatUnits(alphaUSD, 6)),
3776
+ usdt: parseFloat(ethers3.formatUnits(alphaUSD, 6)),
3463
3777
  // alphaUSD as default USDT
3464
- native: parseFloat(ethers.formatEther(nativeBalance)),
3778
+ native: parseFloat(ethers3.formatEther(nativeBalance)),
3465
3779
  tempo: {
3466
- pathUSD: parseFloat(ethers.formatUnits(pathUSD, 6)),
3467
- alphaUSD: parseFloat(ethers.formatUnits(alphaUSD, 6)),
3468
- betaUSD: parseFloat(ethers.formatUnits(betaUSD, 6)),
3469
- thetaUSD: parseFloat(ethers.formatUnits(thetaUSD, 6))
3780
+ pathUSD: parseFloat(ethers3.formatUnits(pathUSD, 6)),
3781
+ alphaUSD: parseFloat(ethers3.formatUnits(alphaUSD, 6)),
3782
+ betaUSD: parseFloat(ethers3.formatUnits(betaUSD, 6)),
3783
+ thetaUSD: parseFloat(ethers3.formatUnits(thetaUSD, 6))
3470
3784
  }
3471
3785
  };
3472
3786
  } else {
3473
3787
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
3474
3788
  provider.getBalance(this.wallet.address),
3475
- new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
3476
- new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
3789
+ new ethers3.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
3790
+ new ethers3.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
3477
3791
  ]);
3478
3792
  results[chainName] = {
3479
- usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
3480
- usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
3481
- native: parseFloat(ethers.formatEther(nativeBalance))
3793
+ usdc: parseFloat(ethers3.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
3794
+ usdt: parseFloat(ethers3.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
3795
+ native: parseFloat(ethers3.formatEther(nativeBalance))
3482
3796
  };
3483
3797
  }
3484
3798
  } catch (err) {
@@ -3606,10 +3920,10 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
3606
3920
  };
3607
3921
 
3608
3922
  // src/wallet/Wallet.ts
3609
- import { ethers as ethers2 } from "ethers";
3923
+ import { ethers as ethers4 } from "ethers";
3610
3924
 
3611
3925
  // src/wallet/createWallet.ts
3612
- import { ethers as ethers3 } from "ethers";
3926
+ import { ethers as ethers5 } from "ethers";
3613
3927
  import { writeFileSync as writeFileSync3, readFileSync as readFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
3614
3928
  import { join as join5, dirname } from "path";
3615
3929
  import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
@@ -3654,7 +3968,7 @@ function createWallet(options = {}) {
3654
3968
  }
3655
3969
  }
3656
3970
  try {
3657
- const wallet = ethers3.Wallet.createRandom();
3971
+ const wallet = ethers5.Wallet.createRandom();
3658
3972
  const walletData = {
3659
3973
  address: wallet.address,
3660
3974
  label: options.label,
@@ -3726,8 +4040,8 @@ function walletExists(storagePath) {
3726
4040
  }
3727
4041
 
3728
4042
  // src/verify/index.ts
3729
- import { ethers as ethers4 } from "ethers";
3730
- var TRANSFER_EVENT_TOPIC3 = ethers4.id("Transfer(address,address,uint256)");
4043
+ import { ethers as ethers6 } from "ethers";
4044
+ var TRANSFER_EVENT_TOPIC3 = ethers6.id("Transfer(address,address,uint256)");
3731
4045
  async function verifyPayment(params) {
3732
4046
  const { txHash, expectedAmount, expectedTo, expectedToken } = params;
3733
4047
  let chain;
@@ -3744,7 +4058,7 @@ async function verifyPayment(params) {
3744
4058
  return { verified: false, error: `Unsupported chain: ${params.chain}` };
3745
4059
  }
3746
4060
  try {
3747
- const provider = new ethers4.JsonRpcProvider(chain.rpc);
4061
+ const provider = new ethers6.JsonRpcProvider(chain.rpc);
3748
4062
  const receipt = await provider.getTransactionReceipt(txHash);
3749
4063
  if (!receipt) {
3750
4064
  return { verified: false, error: "Transaction not found or not confirmed" };
@@ -3816,7 +4130,7 @@ async function getTransactionStatus(txHash, chain = "base") {
3816
4130
  return { status: "not_found" };
3817
4131
  }
3818
4132
  try {
3819
- const provider = new ethers4.JsonRpcProvider(chainConfig.rpc);
4133
+ const provider = new ethers6.JsonRpcProvider(chainConfig.rpc);
3820
4134
  const receipt = await provider.getTransactionReceipt(txHash);
3821
4135
  if (!receipt) {
3822
4136
  const tx = await provider.getTransaction(txHash);
@@ -3853,7 +4167,7 @@ async function waitForTransaction(txHash, chain = "base", confirmations = 1, tim
3853
4167
  } catch (e) {
3854
4168
  return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };
3855
4169
  }
3856
- const provider = new ethers4.JsonRpcProvider(chainConfig.rpc);
4170
+ const provider = new ethers6.JsonRpcProvider(chainConfig.rpc);
3857
4171
  try {
3858
4172
  const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
3859
4173
  if (!receipt) {
@@ -3993,9 +4307,9 @@ var CDPWallet = class {
3993
4307
  * Get USDC balance
3994
4308
  */
3995
4309
  async getBalance() {
3996
- const { ethers: ethers5 } = await import("ethers");
3997
- const provider = new ethers5.JsonRpcProvider(this.chainConfig.rpc);
3998
- const usdcContract = new ethers5.Contract(
4310
+ const { ethers: ethers7 } = await import("ethers");
4311
+ const provider = new ethers7.JsonRpcProvider(this.chainConfig.rpc);
4312
+ const usdcContract = new ethers7.Contract(
3999
4313
  this.chainConfig.usdc,
4000
4314
  ["function balanceOf(address) view returns (uint256)"],
4001
4315
  provider
@@ -4006,7 +4320,7 @@ var CDPWallet = class {
4006
4320
  ]);
4007
4321
  return {
4008
4322
  usdc: (Number(usdcBalance) / 1e6).toFixed(2),
4009
- eth: ethers5.formatEther(ethBalance)
4323
+ eth: ethers7.formatEther(ethBalance)
4010
4324
  };
4011
4325
  }
4012
4326
  /**
@@ -4024,7 +4338,7 @@ var CDPWallet = class {
4024
4338
  }
4025
4339
  try {
4026
4340
  const { CdpClient } = await import("@coinbase/cdp-sdk");
4027
- const { ethers: ethers5 } = await import("ethers");
4341
+ const { ethers: ethers7 } = await import("ethers");
4028
4342
  const cdp = new CdpClient({
4029
4343
  apiKeyId: creds.apiKeyId,
4030
4344
  apiKeySecret: creds.apiKeySecret,
@@ -4032,7 +4346,7 @@ var CDPWallet = class {
4032
4346
  });
4033
4347
  const account = await cdp.evm.getAccount({ address: this.address });
4034
4348
  const amountWei = BigInt(Math.floor(params.amount * 1e6));
4035
- const iface = new ethers5.Interface([
4349
+ const iface = new ethers7.Interface([
4036
4350
  "function transfer(address to, uint256 amount) returns (bool)"
4037
4351
  ]);
4038
4352
  const callData = iface.encodeFunctionData("transfer", [params.to, amountWei]);