moltspay 1.5.0 → 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/cli/index.js CHANGED
@@ -41,14 +41,17 @@ var import_os3 = require("os");
41
41
  var import_path3 = require("path");
42
42
  var import_fs5 = require("fs");
43
43
  var import_child_process = require("child_process");
44
- var import_ethers2 = require("ethers");
44
+ var import_ethers4 = require("ethers");
45
45
 
46
46
  // src/client/index.ts
47
47
  init_cjs_shims();
48
+
49
+ // src/client/node/index.ts
50
+ init_cjs_shims();
48
51
  var import_fs2 = require("fs");
49
52
  var import_os2 = require("os");
50
53
  var import_path2 = require("path");
51
- var import_ethers = require("ethers");
54
+ var import_ethers2 = require("ethers");
52
55
 
53
56
  // src/chains/index.ts
54
57
  init_cjs_shims();
@@ -532,16 +535,16 @@ var SolanaFacilitator = class extends BaseFacilitator {
532
535
  return this.supportedNetworks.includes(network);
533
536
  }
534
537
  };
535
- async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey) {
538
+ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey, connection) {
536
539
  const chainConfig = SOLANA_CHAINS[chain];
537
- const connection = new import_web33.Connection(chainConfig.rpc, "confirmed");
540
+ const conn = connection ?? new import_web33.Connection(chainConfig.rpc, "confirmed");
538
541
  const mint = new import_web33.PublicKey(chainConfig.tokens.USDC.mint);
539
542
  const actualFeePayer = feePayerPubkey || senderPubkey;
540
543
  const senderATA = await (0, import_spl_token2.getAssociatedTokenAddress)(mint, senderPubkey);
541
544
  const recipientATA = await (0, import_spl_token2.getAssociatedTokenAddress)(mint, recipientPubkey);
542
545
  const transaction = new import_web33.Transaction();
543
546
  try {
544
- await (0, import_spl_token2.getAccount)(connection, recipientATA);
547
+ await (0, import_spl_token2.getAccount)(conn, recipientATA);
545
548
  } catch {
546
549
  transaction.add(
547
550
  (0, import_spl_token2.createAssociatedTokenAccountInstruction)(
@@ -572,22 +575,202 @@ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amo
572
575
  // decimals
573
576
  )
574
577
  );
575
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
578
+ const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();
576
579
  transaction.recentBlockhash = blockhash;
577
580
  transaction.feePayer = actualFeePayer;
578
581
  return transaction;
579
582
  }
580
583
 
581
- // src/client/index.ts
582
- var import_web34 = require("@solana/web3.js");
584
+ // src/client/node/index.ts
585
+ var import_web35 = require("@solana/web3.js");
583
586
 
584
- // src/client/types.ts
587
+ // src/client/core/index.ts
585
588
  init_cjs_shims();
586
589
 
587
- // src/client/index.ts
590
+ // src/client/core/types.ts
591
+ init_cjs_shims();
588
592
  var X402_VERSION = 2;
589
593
  var PAYMENT_REQUIRED_HEADER = "x-payment-required";
590
594
  var PAYMENT_HEADER = "x-payment";
595
+
596
+ // src/client/core/chain-map.ts
597
+ init_cjs_shims();
598
+ var NETWORK_TO_CHAIN = {
599
+ "eip155:8453": "base",
600
+ "eip155:137": "polygon",
601
+ "eip155:84532": "base_sepolia",
602
+ "eip155:42431": "tempo_moderato",
603
+ "eip155:56": "bnb",
604
+ "eip155:97": "bnb_testnet",
605
+ "solana:mainnet": "solana",
606
+ "solana:devnet": "solana_devnet"
607
+ };
608
+ var CHAIN_TO_NETWORK = Object.fromEntries(
609
+ Object.entries(NETWORK_TO_CHAIN).map(([network, chain]) => [chain, network])
610
+ );
611
+ function networkToChainName(network) {
612
+ return NETWORK_TO_CHAIN[network] ?? null;
613
+ }
614
+
615
+ // src/client/core/base64.ts
616
+ init_cjs_shims();
617
+ var BufferCtor = globalThis.Buffer;
618
+
619
+ // src/client/core/errors.ts
620
+ init_cjs_shims();
621
+
622
+ // src/client/core/eip3009.ts
623
+ init_cjs_shims();
624
+ var EIP3009_TYPES = {
625
+ TransferWithAuthorization: [
626
+ { name: "from", type: "address" },
627
+ { name: "to", type: "address" },
628
+ { name: "value", type: "uint256" },
629
+ { name: "validAfter", type: "uint256" },
630
+ { name: "validBefore", type: "uint256" },
631
+ { name: "nonce", type: "bytes32" }
632
+ ]
633
+ };
634
+ function buildEIP3009TypedData(args) {
635
+ const validAfter = args.validAfter ?? "0";
636
+ const validBefore = args.validBefore ?? (Math.floor(Date.now() / 1e3) + 3600).toString();
637
+ const authorization = {
638
+ from: args.from,
639
+ to: args.to,
640
+ value: args.value,
641
+ validAfter,
642
+ validBefore,
643
+ nonce: args.nonce
644
+ };
645
+ return {
646
+ domain: {
647
+ name: args.tokenName,
648
+ version: args.tokenVersion,
649
+ chainId: args.chainId,
650
+ verifyingContract: args.tokenAddress
651
+ },
652
+ types: EIP3009_TYPES,
653
+ primaryType: "TransferWithAuthorization",
654
+ message: authorization
655
+ };
656
+ }
657
+
658
+ // src/client/core/eip2612.ts
659
+ init_cjs_shims();
660
+
661
+ // src/client/core/bnb-intent.ts
662
+ init_cjs_shims();
663
+ var BNB_INTENT_TYPES = {
664
+ PaymentIntent: [
665
+ { name: "from", type: "address" },
666
+ { name: "to", type: "address" },
667
+ { name: "amount", type: "uint256" },
668
+ { name: "token", type: "address" },
669
+ { name: "service", type: "string" },
670
+ { name: "nonce", type: "uint256" },
671
+ { name: "deadline", type: "uint256" }
672
+ ]
673
+ };
674
+ var BNB_DOMAIN_NAME = "MoltsPay";
675
+ var BNB_DOMAIN_VERSION = "1";
676
+ function buildBnbIntentTypedData(args) {
677
+ const intent = {
678
+ from: args.from,
679
+ to: args.to,
680
+ amount: args.amount,
681
+ token: args.tokenAddress,
682
+ service: args.service,
683
+ nonce: args.nonce,
684
+ deadline: args.deadline
685
+ };
686
+ return {
687
+ domain: {
688
+ name: BNB_DOMAIN_NAME,
689
+ version: BNB_DOMAIN_VERSION,
690
+ chainId: args.chainId
691
+ },
692
+ types: BNB_INTENT_TYPES,
693
+ primaryType: "PaymentIntent",
694
+ message: intent
695
+ };
696
+ }
697
+
698
+ // src/client/core/solana-tx.ts
699
+ init_cjs_shims();
700
+
701
+ // src/client/core/x402.ts
702
+ init_cjs_shims();
703
+
704
+ // src/client/node/signer.ts
705
+ init_cjs_shims();
706
+ var import_ethers = require("ethers");
707
+ var import_web34 = require("@solana/web3.js");
708
+ var NodeSigner = class {
709
+ evmWallet;
710
+ getSolanaKeypair;
711
+ constructor(evmWallet, options = {}) {
712
+ this.evmWallet = evmWallet;
713
+ this.getSolanaKeypair = options.getSolanaKeypair ?? (() => null);
714
+ }
715
+ async getEvmAddress() {
716
+ return this.evmWallet.address;
717
+ }
718
+ async getSolanaAddress() {
719
+ const kp = this.getSolanaKeypair();
720
+ return kp ? kp.publicKey.toBase58() : null;
721
+ }
722
+ async signTypedData(envelope) {
723
+ const mutableTypes = {};
724
+ for (const [key, fields] of Object.entries(envelope.types)) {
725
+ mutableTypes[key] = [...fields];
726
+ }
727
+ return this.evmWallet.signTypedData(
728
+ envelope.domain,
729
+ mutableTypes,
730
+ envelope.message
731
+ );
732
+ }
733
+ async sendEvmTransaction(args) {
734
+ const chain = findChainByChainId(args.chainId);
735
+ if (!chain) {
736
+ throw new Error(`sendEvmTransaction: unknown chainId ${args.chainId}`);
737
+ }
738
+ const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
739
+ const connected = this.evmWallet.connect(provider);
740
+ const tx = await connected.sendTransaction({
741
+ to: args.to,
742
+ data: args.data,
743
+ value: args.value ? BigInt(args.value) : 0n
744
+ });
745
+ return tx.hash;
746
+ }
747
+ async signSolanaTransaction(args) {
748
+ const kp = this.getSolanaKeypair();
749
+ if (!kp) {
750
+ throw new Error("signSolanaTransaction: no Solana wallet configured");
751
+ }
752
+ const tx = import_web34.Transaction.from(Buffer.from(args.transactionBase64, "base64"));
753
+ if (args.partialSign) {
754
+ tx.partialSign(kp);
755
+ } else {
756
+ tx.sign(kp);
757
+ }
758
+ return tx.serialize({ requireAllSignatures: false }).toString("base64");
759
+ }
760
+ };
761
+ function findChainByChainId(chainId) {
762
+ for (const cfg of Object.values(CHAINS)) {
763
+ if (cfg.chainId === chainId) {
764
+ return cfg;
765
+ }
766
+ }
767
+ return void 0;
768
+ }
769
+
770
+ // src/client/types.ts
771
+ init_cjs_shims();
772
+
773
+ // src/client/node/index.ts
591
774
  var DEFAULT_CONFIG = {
592
775
  chain: "base",
593
776
  limits: {
@@ -600,6 +783,7 @@ var MoltsPayClient = class {
600
783
  config;
601
784
  walletData = null;
602
785
  wallet = null;
786
+ signer = null;
603
787
  todaySpending = 0;
604
788
  lastSpendingReset = 0;
605
789
  constructor(options = {}) {
@@ -608,7 +792,11 @@ var MoltsPayClient = class {
608
792
  this.walletData = this.loadWallet();
609
793
  this.loadSpending();
610
794
  if (this.walletData) {
611
- this.wallet = new import_ethers.Wallet(this.walletData.privateKey);
795
+ this.wallet = new import_ethers2.Wallet(this.walletData.privateKey);
796
+ const configDir = this.configDir;
797
+ this.signer = new NodeSigner(this.wallet, {
798
+ getSolanaKeypair: () => loadSolanaWallet(configDir)
799
+ });
612
800
  }
613
801
  }
614
802
  /**
@@ -736,20 +924,6 @@ var MoltsPayClient = class {
736
924
  } catch {
737
925
  throw new Error("Invalid x-payment-required header");
738
926
  }
739
- const networkToChainName = (network2) => {
740
- if (network2 === "solana:mainnet") return "solana";
741
- if (network2 === "solana:devnet") return "solana_devnet";
742
- const match = network2.match(/^eip155:(\d+)$/);
743
- if (!match) return null;
744
- const chainId = parseInt(match[1]);
745
- if (chainId === 8453) return "base";
746
- if (chainId === 137) return "polygon";
747
- if (chainId === 84532) return "base_sepolia";
748
- if (chainId === 42431) return "tempo_moderato";
749
- if (chainId === 56) return "bnb";
750
- if (chainId === 97) return "bnb_testnet";
751
- return null;
752
- };
753
927
  const serverChains = requirements.map((r) => networkToChainName(r.network)).filter((c) => c !== null);
754
928
  const userSpecifiedChain = options.chain;
755
929
  let selectedChain;
@@ -978,14 +1152,14 @@ Please specify: --chain <chain_name>`
978
1152
  async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
979
1153
  const { to, amount, token, chainName, chain, spender } = paymentDetails;
980
1154
  const tokenConfig = chain.tokens[token];
981
- const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
1155
+ const provider = new import_ethers2.ethers.JsonRpcProvider(chain.rpc);
982
1156
  const allowance = await this.checkAllowance(tokenConfig.address, spender, provider);
983
1157
  const amountWeiCheck = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals));
984
1158
  if (allowance < amountWeiCheck) {
985
1159
  const nativeBalance = await provider.getBalance(this.wallet.address);
986
- const minGasBalance = import_ethers.ethers.parseEther("0.0005");
1160
+ const minGasBalance = import_ethers2.ethers.parseEther("0.0005");
987
1161
  if (nativeBalance < minGasBalance) {
988
- const nativeBNB = parseFloat(import_ethers.ethers.formatEther(nativeBalance)).toFixed(4);
1162
+ const nativeBNB = parseFloat(import_ethers2.ethers.formatEther(nativeBalance)).toFixed(4);
989
1163
  const isTestnet = chainName === "bnb_testnet";
990
1164
  if (isTestnet) {
991
1165
  throw new Error(
@@ -1019,35 +1193,21 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1019
1193
  );
1020
1194
  }
1021
1195
  const amountWei = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
1022
- const intent = {
1196
+ const intentNonce = Date.now();
1197
+ const intentDeadline = Date.now() + 36e5;
1198
+ const envelope = buildBnbIntentTypedData({
1023
1199
  from: this.wallet.address,
1024
1200
  to,
1025
1201
  amount: amountWei,
1026
- token: tokenConfig.address,
1202
+ tokenAddress: tokenConfig.address,
1027
1203
  service,
1028
- nonce: Date.now(),
1029
- // Use timestamp as nonce for simplicity
1030
- deadline: Date.now() + 36e5
1031
- // 1 hour
1032
- };
1033
- const domain = {
1034
- name: "MoltsPay",
1035
- version: "1",
1204
+ nonce: intentNonce,
1205
+ deadline: intentDeadline,
1036
1206
  chainId: chain.chainId
1037
- };
1038
- const types = {
1039
- PaymentIntent: [
1040
- { name: "from", type: "address" },
1041
- { name: "to", type: "address" },
1042
- { name: "amount", type: "uint256" },
1043
- { name: "token", type: "address" },
1044
- { name: "service", type: "string" },
1045
- { name: "nonce", type: "uint256" },
1046
- { name: "deadline", type: "uint256" }
1047
- ]
1048
- };
1207
+ });
1049
1208
  console.log(`[MoltsPay] Signing BNB payment intent...`);
1050
- const signature = await this.wallet.signTypedData(domain, types, intent);
1209
+ const signature = await this.signer.signTypedData(envelope);
1210
+ const intent = envelope.message;
1051
1211
  const network = `eip155:${chain.chainId}`;
1052
1212
  const payload = {
1053
1213
  x402Version: 2,
@@ -1108,11 +1268,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1108
1268
  throw new Error("Missing payTo address in payment requirements");
1109
1269
  }
1110
1270
  const solanaFeePayer = requirements.extra?.solanaFeePayer;
1111
- const feePayerPubkey = solanaFeePayer ? new import_web34.PublicKey(solanaFeePayer) : void 0;
1271
+ const feePayerPubkey = solanaFeePayer ? new import_web35.PublicKey(solanaFeePayer) : void 0;
1112
1272
  if (feePayerPubkey) {
1113
1273
  console.log(`[MoltsPay] Gasless mode: server pays fees`);
1114
1274
  }
1115
- const recipientPubkey = new import_web34.PublicKey(requirements.payTo);
1275
+ const recipientPubkey = new import_web35.PublicKey(requirements.payTo);
1116
1276
  const transaction = await createSolanaPaymentTransaction(
1117
1277
  solanaWallet.publicKey,
1118
1278
  recipientPubkey,
@@ -1121,12 +1281,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1121
1281
  feePayerPubkey
1122
1282
  // Optional fee payer for gasless mode
1123
1283
  );
1124
- if (feePayerPubkey) {
1125
- transaction.partialSign(solanaWallet);
1126
- } else {
1127
- transaction.sign(solanaWallet);
1128
- }
1129
- const signedTx = transaction.serialize({ requireAllSignatures: false }).toString("base64");
1284
+ const unsignedBase64 = transaction.serialize({ requireAllSignatures: false, verifySignatures: false }).toString("base64");
1285
+ const signedTx = await this.signer.signSolanaTransaction({
1286
+ transactionBase64: unsignedBase64,
1287
+ partialSign: !!feePayerPubkey
1288
+ });
1130
1289
  console.log(`[MoltsPay] Transaction signed, sending to server...`);
1131
1290
  const network = chain === "solana" ? "solana:mainnet" : "solana:devnet";
1132
1291
  const payload = {
@@ -1173,7 +1332,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1173
1332
  * Check ERC20 allowance for a spender
1174
1333
  */
1175
1334
  async checkAllowance(tokenAddress, spender, provider) {
1176
- const contract = new import_ethers.ethers.Contract(
1335
+ const contract = new import_ethers2.ethers.Contract(
1177
1336
  tokenAddress,
1178
1337
  ["function allowance(address owner, address spender) view returns (uint256)"],
1179
1338
  provider
@@ -1184,41 +1343,29 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1184
1343
  * Sign EIP-3009 transferWithAuthorization (GASLESS)
1185
1344
  * This only signs - no on-chain transaction, no gas needed.
1186
1345
  * Supports both USDC and USDT.
1346
+ *
1347
+ * Delegates typed-data construction to `core/eip3009.ts` and the signature
1348
+ * itself to `this.signer`. That way Web Client (Phase 4) can reuse the same
1349
+ * flow with an EIP-1193 signer without duplicating typed-data layout.
1187
1350
  */
1188
1351
  async signEIP3009(to, amount, chain, token = "USDC", domainOverride) {
1189
- const validAfter = 0;
1190
- const validBefore = Math.floor(Date.now() / 1e3) + 3600;
1191
- const nonce = import_ethers.ethers.hexlify(import_ethers.ethers.randomBytes(32));
1192
1352
  const tokenConfig = chain.tokens[token];
1193
1353
  const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
1194
- const authorization = {
1354
+ const nonce = import_ethers2.ethers.hexlify(import_ethers2.ethers.randomBytes(32));
1355
+ const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
1356
+ const tokenVersion = domainOverride?.version || "2";
1357
+ const envelope = buildEIP3009TypedData({
1195
1358
  from: this.wallet.address,
1196
1359
  to,
1197
1360
  value,
1198
- validAfter: validAfter.toString(),
1199
- validBefore: validBefore.toString(),
1200
- nonce
1201
- };
1202
- const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
1203
- const tokenVersion = domainOverride?.version || "2";
1204
- const domain = {
1205
- name: tokenName,
1206
- version: tokenVersion,
1361
+ nonce,
1207
1362
  chainId: chain.chainId,
1208
- verifyingContract: tokenConfig.address
1209
- };
1210
- const types = {
1211
- TransferWithAuthorization: [
1212
- { name: "from", type: "address" },
1213
- { name: "to", type: "address" },
1214
- { name: "value", type: "uint256" },
1215
- { name: "validAfter", type: "uint256" },
1216
- { name: "validBefore", type: "uint256" },
1217
- { name: "nonce", type: "bytes32" }
1218
- ]
1219
- };
1220
- const signature = await this.wallet.signTypedData(domain, types, authorization);
1221
- return { authorization, signature };
1363
+ tokenAddress: tokenConfig.address,
1364
+ tokenName,
1365
+ tokenVersion
1366
+ });
1367
+ const signature = await this.signer.signTypedData(envelope);
1368
+ return { authorization: envelope.message, signature };
1222
1369
  }
1223
1370
  /**
1224
1371
  * Check spending limits
@@ -1322,7 +1469,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1322
1469
  */
1323
1470
  static init(configDir, options) {
1324
1471
  (0, import_fs2.mkdirSync)(configDir, { recursive: true });
1325
- const wallet = import_ethers.Wallet.createRandom();
1472
+ const wallet = import_ethers2.Wallet.createRandom();
1326
1473
  const walletData = {
1327
1474
  address: wallet.address,
1328
1475
  privateKey: wallet.privateKey,
@@ -1354,17 +1501,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1354
1501
  } catch {
1355
1502
  throw new Error(`Unknown chain: ${this.config.chain}`);
1356
1503
  }
1357
- const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
1504
+ const provider = new import_ethers2.ethers.JsonRpcProvider(chain.rpc);
1358
1505
  const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
1359
1506
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
1360
1507
  provider.getBalance(this.wallet.address),
1361
- new import_ethers.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1362
- new import_ethers.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1508
+ new import_ethers2.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1509
+ new import_ethers2.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1363
1510
  ]);
1364
1511
  return {
1365
- usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1366
- usdt: parseFloat(import_ethers.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1367
- native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
1512
+ usdc: parseFloat(import_ethers2.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1513
+ usdt: parseFloat(import_ethers2.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1514
+ native: parseFloat(import_ethers2.ethers.formatEther(nativeBalance))
1368
1515
  };
1369
1516
  }
1370
1517
  /**
@@ -1387,38 +1534,38 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1387
1534
  supportedChains.map(async (chainName) => {
1388
1535
  try {
1389
1536
  const chain = getChain(chainName);
1390
- const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
1537
+ const provider = new import_ethers2.ethers.JsonRpcProvider(chain.rpc);
1391
1538
  if (chainName === "tempo_moderato") {
1392
1539
  const [nativeBalance, pathUSD, alphaUSD, betaUSD, thetaUSD] = await Promise.all([
1393
1540
  provider.getBalance(this.wallet.address),
1394
- new import_ethers.ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1395
- new import_ethers.ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1396
- new import_ethers.ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1397
- new import_ethers.ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
1541
+ new import_ethers2.ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1542
+ new import_ethers2.ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1543
+ new import_ethers2.ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1544
+ new import_ethers2.ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
1398
1545
  ]);
1399
1546
  results[chainName] = {
1400
- usdc: parseFloat(import_ethers.ethers.formatUnits(pathUSD, 6)),
1547
+ usdc: parseFloat(import_ethers2.ethers.formatUnits(pathUSD, 6)),
1401
1548
  // pathUSD as default USDC
1402
- usdt: parseFloat(import_ethers.ethers.formatUnits(alphaUSD, 6)),
1549
+ usdt: parseFloat(import_ethers2.ethers.formatUnits(alphaUSD, 6)),
1403
1550
  // alphaUSD as default USDT
1404
- native: parseFloat(import_ethers.ethers.formatEther(nativeBalance)),
1551
+ native: parseFloat(import_ethers2.ethers.formatEther(nativeBalance)),
1405
1552
  tempo: {
1406
- pathUSD: parseFloat(import_ethers.ethers.formatUnits(pathUSD, 6)),
1407
- alphaUSD: parseFloat(import_ethers.ethers.formatUnits(alphaUSD, 6)),
1408
- betaUSD: parseFloat(import_ethers.ethers.formatUnits(betaUSD, 6)),
1409
- thetaUSD: parseFloat(import_ethers.ethers.formatUnits(thetaUSD, 6))
1553
+ pathUSD: parseFloat(import_ethers2.ethers.formatUnits(pathUSD, 6)),
1554
+ alphaUSD: parseFloat(import_ethers2.ethers.formatUnits(alphaUSD, 6)),
1555
+ betaUSD: parseFloat(import_ethers2.ethers.formatUnits(betaUSD, 6)),
1556
+ thetaUSD: parseFloat(import_ethers2.ethers.formatUnits(thetaUSD, 6))
1410
1557
  }
1411
1558
  };
1412
1559
  } else {
1413
1560
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
1414
1561
  provider.getBalance(this.wallet.address),
1415
- new import_ethers.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1416
- new import_ethers.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1562
+ new import_ethers2.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1563
+ new import_ethers2.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1417
1564
  ]);
1418
1565
  results[chainName] = {
1419
- usdc: parseFloat(import_ethers.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1420
- usdt: parseFloat(import_ethers.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1421
- native: parseFloat(import_ethers.ethers.formatEther(nativeBalance))
1566
+ usdc: parseFloat(import_ethers2.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1567
+ usdt: parseFloat(import_ethers2.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1568
+ native: parseFloat(import_ethers2.ethers.formatEther(nativeBalance))
1422
1569
  };
1423
1570
  }
1424
1571
  } catch (err) {
@@ -1776,16 +1923,40 @@ var CDPFacilitator = class extends BaseFacilitator {
1776
1923
 
1777
1924
  // src/facilitators/tempo.ts
1778
1925
  init_cjs_shims();
1926
+ var import_ethers3 = require("ethers");
1779
1927
  var TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
1928
+ var TIP20_PERMIT_ABI = [
1929
+ "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
1930
+ "function transferFrom(address from, address to, uint256 value) returns (bool)"
1931
+ ];
1780
1932
  var TempoFacilitator = class extends BaseFacilitator {
1781
1933
  name = "tempo";
1782
1934
  displayName = "Tempo Testnet";
1783
1935
  supportedNetworks = ["eip155:42431"];
1784
1936
  // Tempo Moderato
1785
1937
  rpcUrl;
1938
+ settlerWallet = null;
1786
1939
  constructor() {
1787
1940
  super();
1788
1941
  this.rpcUrl = CHAINS.tempo_moderato.rpc;
1942
+ const settlerKey = process.env.TEMPO_SETTLER_KEY;
1943
+ if (settlerKey) {
1944
+ try {
1945
+ const provider = new import_ethers3.ethers.JsonRpcProvider(this.rpcUrl);
1946
+ this.settlerWallet = new import_ethers3.ethers.Wallet(settlerKey, provider);
1947
+ } catch (err) {
1948
+ console.warn("[TempoFacilitator] Invalid TEMPO_SETTLER_KEY, permit settlement disabled:", err);
1949
+ this.settlerWallet = null;
1950
+ }
1951
+ }
1952
+ }
1953
+ /**
1954
+ * Settler EOA address advertised to clients via `X-Payment-Required.extra.tempoSpender`.
1955
+ * Web Client uses this as the `spender` field in the signed EIP-2612 Permit.
1956
+ * Returns null if no TEMPO_SETTLER_KEY is configured — permit settlement unavailable.
1957
+ */
1958
+ getSpenderAddress() {
1959
+ return this.settlerWallet?.address ?? null;
1789
1960
  }
1790
1961
  async healthCheck() {
1791
1962
  const start = Date.now();
@@ -1811,6 +1982,44 @@ var TempoFacilitator = class extends BaseFacilitator {
1811
1982
  }
1812
1983
  }
1813
1984
  async verify(paymentPayload, requirements) {
1985
+ const inner = paymentPayload.payload;
1986
+ if (inner && "permit" in inner && inner.permit) {
1987
+ return this.verifyPermit(inner, requirements);
1988
+ }
1989
+ return this.verifyTxHash(paymentPayload, requirements);
1990
+ }
1991
+ /**
1992
+ * Structural validation of an EIP-2612 permit payload. Does NOT submit
1993
+ * anything on-chain — actual submission happens in settlePermit().
1994
+ */
1995
+ async verifyPermit(payload, requirements) {
1996
+ if (!this.settlerWallet) {
1997
+ return { valid: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
1998
+ }
1999
+ const p = payload.permit;
2000
+ if (!p || !p.owner || !p.spender || !p.value || !p.deadline) {
2001
+ return { valid: false, error: "Invalid permit payload: missing fields" };
2002
+ }
2003
+ if (p.spender.toLowerCase() !== this.settlerWallet.address.toLowerCase()) {
2004
+ return {
2005
+ valid: false,
2006
+ error: `Permit spender ${p.spender} does not match configured settler ${this.settlerWallet.address}`
2007
+ };
2008
+ }
2009
+ const deadline = BigInt(p.deadline);
2010
+ const now = BigInt(Math.floor(Date.now() / 1e3));
2011
+ if (deadline <= now) {
2012
+ return { valid: false, error: "Permit deadline has expired" };
2013
+ }
2014
+ if (BigInt(p.value) < BigInt(requirements.amount || "0")) {
2015
+ return {
2016
+ valid: false,
2017
+ error: `Permit value ${p.value} is less than required ${requirements.amount}`
2018
+ };
2019
+ }
2020
+ return { valid: true, details: { scheme: "permit", owner: p.owner } };
2021
+ }
2022
+ async verifyTxHash(paymentPayload, requirements) {
1814
2023
  try {
1815
2024
  const tempoPayload = paymentPayload.payload;
1816
2025
  if (!tempoPayload?.txHash) {
@@ -1868,7 +2077,11 @@ var TempoFacilitator = class extends BaseFacilitator {
1868
2077
  }
1869
2078
  }
1870
2079
  async settle(paymentPayload, requirements) {
1871
- const verifyResult = await this.verify(paymentPayload, requirements);
2080
+ const inner = paymentPayload.payload;
2081
+ if (inner && "permit" in inner && inner.permit) {
2082
+ return this.settlePermit(inner, requirements);
2083
+ }
2084
+ const verifyResult = await this.verifyTxHash(paymentPayload, requirements);
1872
2085
  if (!verifyResult.valid) {
1873
2086
  return { success: false, error: verifyResult.error };
1874
2087
  }
@@ -1879,6 +2092,52 @@ var TempoFacilitator = class extends BaseFacilitator {
1879
2092
  status: "settled"
1880
2093
  };
1881
2094
  }
2095
+ /**
2096
+ * EIP-2612 permit settlement path. Submits two transactions on Tempo:
2097
+ * 1. pathUSD.permit(owner, spender=settler, value, deadline, v, r, s)
2098
+ * 2. pathUSD.transferFrom(owner, payTo, value)
2099
+ *
2100
+ * The settler EOA pays Tempo gas (via the TIP-20 `feeToken` mechanism — no
2101
+ * native tTEMPO required; any held TIP-20 token balance covers fees).
2102
+ */
2103
+ async settlePermit(payload, requirements) {
2104
+ if (!this.settlerWallet) {
2105
+ return { success: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
2106
+ }
2107
+ if (!requirements.asset || !requirements.payTo) {
2108
+ return { success: false, error: "Missing asset or payTo in requirements" };
2109
+ }
2110
+ const verifyResult = await this.verifyPermit(payload, requirements);
2111
+ if (!verifyResult.valid) {
2112
+ return { success: false, error: verifyResult.error };
2113
+ }
2114
+ const token = new import_ethers3.ethers.Contract(requirements.asset, TIP20_PERMIT_ABI, this.settlerWallet);
2115
+ const p = payload.permit;
2116
+ try {
2117
+ const permitTx = await token.permit(
2118
+ p.owner,
2119
+ p.spender,
2120
+ p.value,
2121
+ p.deadline,
2122
+ p.v,
2123
+ p.r,
2124
+ p.s
2125
+ );
2126
+ await permitTx.wait();
2127
+ const transferTx = await token.transferFrom(p.owner, requirements.payTo, p.value);
2128
+ await transferTx.wait();
2129
+ return {
2130
+ success: true,
2131
+ transaction: transferTx.hash,
2132
+ status: "settled"
2133
+ };
2134
+ } catch (err) {
2135
+ return {
2136
+ success: false,
2137
+ error: `Tempo permit settlement failed: ${err.message}`
2138
+ };
2139
+ }
2140
+ }
1882
2141
  async getTransactionReceipt(txHash) {
1883
2142
  const response = await fetch(this.rpcUrl, {
1884
2143
  method: "POST",
@@ -2122,12 +2381,12 @@ var BNBFacilitator = class extends BaseFacilitator {
2122
2381
  return this.spenderAddress;
2123
2382
  }
2124
2383
  async getServerAddress() {
2125
- const { ethers: ethers3 } = await import("ethers");
2126
- const wallet = new ethers3.Wallet(this.serverPrivateKey);
2384
+ const { ethers: ethers5 } = await import("ethers");
2385
+ const wallet = new ethers5.Wallet(this.serverPrivateKey);
2127
2386
  return wallet.address;
2128
2387
  }
2129
2388
  async recoverIntentSigner(intent, chainId) {
2130
- const { ethers: ethers3 } = await import("ethers");
2389
+ const { ethers: ethers5 } = await import("ethers");
2131
2390
  const domain = {
2132
2391
  ...EIP712_DOMAIN,
2133
2392
  chainId
@@ -2141,7 +2400,7 @@ var BNBFacilitator = class extends BaseFacilitator {
2141
2400
  nonce: intent.nonce,
2142
2401
  deadline: intent.deadline
2143
2402
  };
2144
- const recoveredAddress = ethers3.verifyTypedData(
2403
+ const recoveredAddress = ethers5.verifyTypedData(
2145
2404
  domain,
2146
2405
  INTENT_TYPES,
2147
2406
  message,
@@ -2185,10 +2444,10 @@ var BNBFacilitator = class extends BaseFacilitator {
2185
2444
  return result.result || "0x0";
2186
2445
  }
2187
2446
  async executeTransferFrom(from, to, amount, token, rpcUrl) {
2188
- const { ethers: ethers3 } = await import("ethers");
2189
- const provider = new ethers3.JsonRpcProvider(rpcUrl);
2190
- const wallet = new ethers3.Wallet(this.serverPrivateKey, provider);
2191
- const tokenContract = new ethers3.Contract(token, [
2447
+ const { ethers: ethers5 } = await import("ethers");
2448
+ const provider = new ethers5.JsonRpcProvider(rpcUrl);
2449
+ const wallet = new ethers5.Wallet(this.serverPrivateKey, provider);
2450
+ const tokenContract = new ethers5.Contract(token, [
2192
2451
  "function transferFrom(address from, address to, uint256 amount) returns (bool)"
2193
2452
  ], wallet);
2194
2453
  const tx = await tokenContract.transferFrom(from, to, amount);
@@ -2213,7 +2472,7 @@ var BNBFacilitator = class extends BaseFacilitator {
2213
2472
 
2214
2473
  // src/facilitators/registry.ts
2215
2474
  init_cjs_shims();
2216
- var import_web35 = require("@solana/web3.js");
2475
+ var import_web36 = require("@solana/web3.js");
2217
2476
  var import_bs582 = __toESM(require("bs58"));
2218
2477
  var FacilitatorRegistry = class {
2219
2478
  factories = /* @__PURE__ */ new Map();
@@ -2229,7 +2488,7 @@ var FacilitatorRegistry = class {
2229
2488
  const feePayerKey = config?.feePayerPrivateKey || process.env.SOLANA_FEE_PAYER_KEY;
2230
2489
  if (feePayerKey) {
2231
2490
  try {
2232
- feePayerKeypair = import_web35.Keypair.fromSecretKey(import_bs582.default.decode(feePayerKey));
2491
+ feePayerKeypair = import_web36.Keypair.fromSecretKey(import_bs582.default.decode(feePayerKey));
2233
2492
  } catch (e) {
2234
2493
  console.warn(`[SolanaFacilitator] Invalid fee payer key: ${e.message}`);
2235
2494
  }
@@ -2504,7 +2763,7 @@ var TOKEN_ADDRESSES = {
2504
2763
  // Devnet USDC
2505
2764
  }
2506
2765
  };
2507
- var CHAIN_TO_NETWORK = {
2766
+ var CHAIN_TO_NETWORK2 = {
2508
2767
  "base": "eip155:8453",
2509
2768
  "base_sepolia": "eip155:84532",
2510
2769
  "polygon": "eip155:137",
@@ -2535,9 +2794,13 @@ var TOKEN_DOMAINS = {
2535
2794
  USDT: { name: "(PoS) Tether USD", version: "2" }
2536
2795
  },
2537
2796
  // Tempo Moderato testnet - TIP-20 stablecoins
2797
+ // Domain names verified against on-chain DOMAIN_SEPARATOR values on 2026-04-21.
2798
+ // See docs/TEMPO-WEB-SUPPORT.md Section 2 and test/server/tempo-domain.test.ts.
2799
+ // All 4 Tempo TIP-20 tokens (pathUSD / AlphaUSD / BetaUSD / ThetaUSD) use
2800
+ // the token symbol with first letter capitalized + version "1".
2538
2801
  "eip155:42431": {
2539
- USDC: { name: "pathUSD", version: "1" },
2540
- USDT: { name: "alphaUSD", version: "1" }
2802
+ USDC: { name: "PathUSD", version: "1" },
2803
+ USDT: { name: "AlphaUSD", version: "1" }
2541
2804
  },
2542
2805
  // BNB Smart Chain mainnet
2543
2806
  "eip155:56": {
@@ -2660,14 +2923,14 @@ var MoltsPayServer = class {
2660
2923
  const chainName = typeof c === "string" ? c : c.chain;
2661
2924
  const explicitWallet = typeof c === "object" ? c.wallet : null;
2662
2925
  return {
2663
- network: CHAIN_TO_NETWORK[chainName] || "eip155:8453",
2926
+ network: CHAIN_TO_NETWORK2[chainName] || "eip155:8453",
2664
2927
  wallet: getWalletForChain(chainName, explicitWallet || void 0),
2665
2928
  tokens: (typeof c === "object" ? c.tokens : null) || ["USDC"]
2666
2929
  };
2667
2930
  });
2668
2931
  }
2669
2932
  const chain = provider.chain || "base";
2670
- const network = CHAIN_TO_NETWORK[chain] || this.networkId;
2933
+ const network = CHAIN_TO_NETWORK2[chain] || this.networkId;
2671
2934
  return [{
2672
2935
  network,
2673
2936
  wallet: getWalletForChain(chain),
@@ -2706,13 +2969,62 @@ var MoltsPayServer = class {
2706
2969
  });
2707
2970
  }
2708
2971
  /**
2709
- * Handle incoming request
2972
+ * Apply CORS response headers according to the `cors` option.
2973
+ *
2974
+ * Default (`cors` unset or `true`): `Access-Control-Allow-Origin: *`. Matches 1.5.x behavior
2975
+ * and works for every browser client whose origin does not need to send cookies.
2976
+ *
2977
+ * `cors: false`: emit no CORS headers. Same-origin only.
2978
+ * `cors: string[]`: origin allowlist — echo the origin back iff it matches.
2979
+ * `cors: CorsOptions`: full control (allowlist + credentials + maxAge).
2980
+ *
2981
+ * The required-for-Web response headers are always exposed when CORS is active:
2982
+ * `X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt`.
2710
2983
  */
2711
- async handleRequest(req, res) {
2712
- res.setHeader("Access-Control-Allow-Origin", "*");
2984
+ applyCorsHeaders(req, res) {
2985
+ const cors = this.options.cors;
2986
+ if (cors === false) {
2987
+ return;
2988
+ }
2989
+ const requestOrigin = req.headers.origin ?? "*";
2990
+ if (cors === void 0 || cors === true) {
2991
+ this.writeCorsHeaders(res, "*");
2992
+ return;
2993
+ }
2994
+ if (Array.isArray(cors)) {
2995
+ if (cors.includes(requestOrigin)) {
2996
+ this.writeCorsHeaders(res, requestOrigin);
2997
+ res.setHeader("Vary", "Origin");
2998
+ }
2999
+ return;
3000
+ }
3001
+ const opt = cors;
3002
+ const isAllowed = typeof opt.origins === "function" ? opt.origins(requestOrigin) : opt.origins.includes(requestOrigin);
3003
+ if (!isAllowed) {
3004
+ return;
3005
+ }
3006
+ this.writeCorsHeaders(res, requestOrigin);
3007
+ res.setHeader("Vary", "Origin");
3008
+ if (opt.credentials) {
3009
+ res.setHeader("Access-Control-Allow-Credentials", "true");
3010
+ }
3011
+ const maxAge = opt.maxAge ?? 600;
3012
+ res.setHeader("Access-Control-Max-Age", String(maxAge));
3013
+ }
3014
+ writeCorsHeaders(res, origin) {
3015
+ res.setHeader("Access-Control-Allow-Origin", origin);
2713
3016
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
2714
3017
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
2715
- res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt");
3018
+ res.setHeader(
3019
+ "Access-Control-Expose-Headers",
3020
+ "X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt"
3021
+ );
3022
+ }
3023
+ /**
3024
+ * Handle incoming request
3025
+ */
3026
+ async handleRequest(req, res) {
3027
+ this.applyCorsHeaders(req, res);
2716
3028
  if (req.method === "OPTIONS") {
2717
3029
  res.writeHead(204);
2718
3030
  res.end();
@@ -2935,6 +3247,14 @@ var MoltsPayServer = class {
2935
3247
  console.log(`[MoltsPay] Payment settled by ${settlement.facilitator}: ${settlement.transaction || "pending"}`);
2936
3248
  } catch (err) {
2937
3249
  console.error("[MoltsPay] Settlement failed:", err.message);
3250
+ settlement = { success: false, error: err.message, facilitator: "none" };
3251
+ }
3252
+ if (!settlement?.success) {
3253
+ return this.sendJson(res, 402, {
3254
+ error: "Payment settlement failed",
3255
+ message: settlement?.error || "Settlement returned no success state",
3256
+ facilitator: settlement?.facilitator
3257
+ });
2938
3258
  }
2939
3259
  }
2940
3260
  const responseHeaders = {};
@@ -3189,7 +3509,7 @@ var MoltsPayServer = class {
3189
3509
  }
3190
3510
  const scheme = payment.accepted?.scheme || payment.scheme;
3191
3511
  const network = payment.accepted?.network || payment.network || this.networkId;
3192
- if (scheme !== "exact") {
3512
+ if (scheme !== "exact" && scheme !== "permit") {
3193
3513
  return { valid: false, error: `Unsupported scheme: ${scheme}` };
3194
3514
  }
3195
3515
  if (!this.isNetworkAccepted(network)) {
@@ -3211,8 +3531,10 @@ var MoltsPayServer = class {
3211
3531
  const tokenAddresses = TOKEN_ADDRESSES[selectedNetwork] || {};
3212
3532
  const tokenAddress = tokenAddresses[selectedToken];
3213
3533
  const tokenDomain = getTokenDomain(selectedNetwork, selectedToken);
3534
+ const isTempo = selectedNetwork === "eip155:42431";
3535
+ const scheme = isTempo ? "permit" : "exact";
3214
3536
  const requirements = {
3215
- scheme: "exact",
3537
+ scheme,
3216
3538
  network: selectedNetwork,
3217
3539
  asset: tokenAddress,
3218
3540
  amount: amountInUnits,
@@ -3240,6 +3562,16 @@ var MoltsPayServer = class {
3240
3562
  };
3241
3563
  }
3242
3564
  }
3565
+ if (isTempo) {
3566
+ const tempoFacilitator = this.registry.get("tempo");
3567
+ const tempoSpender = tempoFacilitator?.getSpenderAddress?.();
3568
+ if (tempoSpender) {
3569
+ requirements.extra = {
3570
+ ...requirements.extra || {},
3571
+ tempoSpender
3572
+ };
3573
+ }
3574
+ }
3243
3575
  return requirements;
3244
3576
  }
3245
3577
  /**
@@ -3374,10 +3706,10 @@ var MoltsPayServer = class {
3374
3706
  }
3375
3707
  const scheme = payment.accepted?.scheme || payment.scheme;
3376
3708
  const network = payment.accepted?.network || payment.network;
3377
- if (scheme !== "exact") {
3709
+ if (scheme !== "exact" && scheme !== "permit") {
3378
3710
  return this.sendJson(res, 402, { error: `Unsupported scheme: ${scheme}` });
3379
3711
  }
3380
- const expectedNetwork = chain ? CHAIN_TO_NETWORK[chain] || this.networkId : this.networkId;
3712
+ const expectedNetwork = chain ? CHAIN_TO_NETWORK2[chain] || this.networkId : this.networkId;
3381
3713
  if (network !== expectedNetwork) {
3382
3714
  return this.sendJson(res, 402, { error: `Network mismatch: expected ${expectedNetwork}, got ${network}` });
3383
3715
  }
@@ -3639,7 +3971,7 @@ var MoltsPayServer = class {
3639
3971
  buildProxyPaymentRequirements(config, wallet, token, chain) {
3640
3972
  const amountInUnits = Math.floor(config.price * 1e6).toString();
3641
3973
  const acceptedTokens = getAcceptedCurrencies(config);
3642
- const networkId = chain ? CHAIN_TO_NETWORK[chain] || this.networkId : this.networkId;
3974
+ const networkId = chain ? CHAIN_TO_NETWORK2[chain] || this.networkId : this.networkId;
3643
3975
  const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
3644
3976
  const tokenAddresses = TOKEN_ADDRESSES[networkId] || TOKEN_ADDRESSES[this.networkId] || {};
3645
3977
  const tokenAddress = tokenAddresses[selectedToken];
@@ -3737,7 +4069,7 @@ var ERC20_APPROVE_ABI = [
3737
4069
  ];
3738
4070
  async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = false) {
3739
4071
  const chainConfig = CHAINS[chain];
3740
- const provider = new import_ethers2.ethers.JsonRpcProvider(chainConfig.rpc);
4072
+ const provider = new import_ethers4.ethers.JsonRpcProvider(chainConfig.rpc);
3741
4073
  const wallet = client.getWallet();
3742
4074
  if (!wallet) {
3743
4075
  console.log(" \u274C No wallet found");
@@ -3746,15 +4078,15 @@ async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = fal
3746
4078
  const signer = wallet.connect(provider);
3747
4079
  console.log(` Spender: ${spenderAddress}`);
3748
4080
  let bnbBalance = await provider.getBalance(wallet.address);
3749
- const minGasRequired = import_ethers2.ethers.parseEther("0.0005");
4081
+ const minGasRequired = import_ethers4.ethers.parseEther("0.0005");
3750
4082
  if (bnbBalance < minGasRequired) {
3751
4083
  if (sponsorGas && BNB_SPONSOR_KEY) {
3752
4084
  console.log(" \u23F3 Sponsoring BNB gas for approvals...");
3753
4085
  try {
3754
- const sponsorWallet = new import_ethers2.ethers.Wallet(BNB_SPONSOR_KEY, provider);
4086
+ const sponsorWallet = new import_ethers4.ethers.Wallet(BNB_SPONSOR_KEY, provider);
3755
4087
  const tx = await sponsorWallet.sendTransaction({
3756
4088
  to: wallet.address,
3757
- value: import_ethers2.ethers.parseEther("0.001")
4089
+ value: import_ethers4.ethers.parseEther("0.001")
3758
4090
  });
3759
4091
  await tx.wait();
3760
4092
  console.log(` \u2705 Sponsored 0.001 BNB (tx: ${tx.hash.slice(0, 10)}...)`);
@@ -3773,7 +4105,7 @@ async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = fal
3773
4105
  }
3774
4106
  for (const tokenSymbol of ["USDT", "USDC"]) {
3775
4107
  const tokenConfig = chainConfig.tokens[tokenSymbol];
3776
- const tokenContract = new import_ethers2.ethers.Contract(tokenConfig.address, ERC20_APPROVE_ABI, signer);
4108
+ const tokenContract = new import_ethers4.ethers.Contract(tokenConfig.address, ERC20_APPROVE_ABI, signer);
3777
4109
  const allowance = await tokenContract.allowance(wallet.address, spenderAddress);
3778
4110
  if (allowance > 0n) {
3779
4111
  console.log(` \u2705 ${tokenSymbol}: already approved for ${spenderAddress.slice(0, 10)}...`);
@@ -3781,7 +4113,7 @@ async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = fal
3781
4113
  }
3782
4114
  console.log(` \u23F3 Approving ${tokenSymbol}...`);
3783
4115
  try {
3784
- const tx = await tokenContract.approve(spenderAddress, import_ethers2.ethers.MaxUint256);
4116
+ const tx = await tokenContract.approve(spenderAddress, import_ethers4.ethers.MaxUint256);
3785
4117
  await tx.wait();
3786
4118
  console.log(` \u2705 ${tokenSymbol}: approved (tx: ${tx.hash.slice(0, 10)}...)`);
3787
4119
  } catch (err) {
@@ -3792,7 +4124,7 @@ async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = fal
3792
4124
  }
3793
4125
  async function checkBNBApprovals(address, chain, configDir = DEFAULT_CONFIG_DIR2) {
3794
4126
  const chainConfig = CHAINS[chain];
3795
- const provider = new import_ethers2.ethers.JsonRpcProvider(chainConfig.rpc);
4127
+ const provider = new import_ethers4.ethers.JsonRpcProvider(chainConfig.rpc);
3796
4128
  let spenderAddress = null;
3797
4129
  try {
3798
4130
  const walletPath = (0, import_path3.join)(configDir, "wallet.json");
@@ -3806,7 +4138,7 @@ async function checkBNBApprovals(address, chain, configDir = DEFAULT_CONFIG_DIR2
3806
4138
  }
3807
4139
  for (const tokenSymbol of ["USDT", "USDC"]) {
3808
4140
  const tokenConfig = chainConfig.tokens[tokenSymbol];
3809
- const tokenContract = new import_ethers2.ethers.Contract(tokenConfig.address, ERC20_APPROVE_ABI, provider);
4141
+ const tokenContract = new import_ethers4.ethers.Contract(tokenConfig.address, ERC20_APPROVE_ABI, provider);
3810
4142
  const allowance = await tokenContract.allowance(address, spenderAddress);
3811
4143
  result[tokenSymbol.toLowerCase()] = allowance > 0n;
3812
4144
  }