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.
@@ -25,14 +25,17 @@ import { homedir as homedir3 } from "os";
25
25
  import { join as join5, dirname, resolve } from "path";
26
26
  import { existsSync as existsSync5, writeFileSync as writeFileSync3, readFileSync as readFileSync5, unlinkSync, mkdirSync as mkdirSync3 } from "fs";
27
27
  import { spawn } from "child_process";
28
- import { ethers as ethers2 } from "ethers";
28
+ import { ethers as ethers4 } from "ethers";
29
29
 
30
30
  // src/client/index.ts
31
31
  init_esm_shims();
32
+
33
+ // src/client/node/index.ts
34
+ init_esm_shims();
32
35
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, statSync, chmodSync } from "fs";
33
36
  import { homedir as homedir2 } from "os";
34
37
  import { join as join2 } from "path";
35
- import { Wallet, ethers } from "ethers";
38
+ import { Wallet as Wallet2, ethers as ethers2 } from "ethers";
36
39
 
37
40
  // src/chains/index.ts
38
41
  init_esm_shims();
@@ -526,16 +529,16 @@ var SolanaFacilitator = class extends BaseFacilitator {
526
529
  return this.supportedNetworks.includes(network);
527
530
  }
528
531
  };
529
- async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey) {
532
+ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey, connection) {
530
533
  const chainConfig = SOLANA_CHAINS[chain];
531
- const connection = new Connection3(chainConfig.rpc, "confirmed");
534
+ const conn = connection ?? new Connection3(chainConfig.rpc, "confirmed");
532
535
  const mint = new PublicKey3(chainConfig.tokens.USDC.mint);
533
536
  const actualFeePayer = feePayerPubkey || senderPubkey;
534
537
  const senderATA = await getAssociatedTokenAddress2(mint, senderPubkey);
535
538
  const recipientATA = await getAssociatedTokenAddress2(mint, recipientPubkey);
536
539
  const transaction = new Transaction();
537
540
  try {
538
- await getAccount2(connection, recipientATA);
541
+ await getAccount2(conn, recipientATA);
539
542
  } catch {
540
543
  transaction.add(
541
544
  createAssociatedTokenAccountInstruction(
@@ -566,22 +569,202 @@ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amo
566
569
  // decimals
567
570
  )
568
571
  );
569
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
572
+ const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();
570
573
  transaction.recentBlockhash = blockhash;
571
574
  transaction.feePayer = actualFeePayer;
572
575
  return transaction;
573
576
  }
574
577
 
575
- // src/client/index.ts
578
+ // src/client/node/index.ts
576
579
  import { PublicKey as PublicKey4 } from "@solana/web3.js";
577
580
 
578
- // src/client/types.ts
581
+ // src/client/core/index.ts
579
582
  init_esm_shims();
580
583
 
581
- // src/client/index.ts
584
+ // src/client/core/types.ts
585
+ init_esm_shims();
582
586
  var X402_VERSION = 2;
583
587
  var PAYMENT_REQUIRED_HEADER = "x-payment-required";
584
588
  var PAYMENT_HEADER = "x-payment";
589
+
590
+ // src/client/core/chain-map.ts
591
+ init_esm_shims();
592
+ var NETWORK_TO_CHAIN = {
593
+ "eip155:8453": "base",
594
+ "eip155:137": "polygon",
595
+ "eip155:84532": "base_sepolia",
596
+ "eip155:42431": "tempo_moderato",
597
+ "eip155:56": "bnb",
598
+ "eip155:97": "bnb_testnet",
599
+ "solana:mainnet": "solana",
600
+ "solana:devnet": "solana_devnet"
601
+ };
602
+ var CHAIN_TO_NETWORK = Object.fromEntries(
603
+ Object.entries(NETWORK_TO_CHAIN).map(([network, chain]) => [chain, network])
604
+ );
605
+ function networkToChainName(network) {
606
+ return NETWORK_TO_CHAIN[network] ?? null;
607
+ }
608
+
609
+ // src/client/core/base64.ts
610
+ init_esm_shims();
611
+ var BufferCtor = globalThis.Buffer;
612
+
613
+ // src/client/core/errors.ts
614
+ init_esm_shims();
615
+
616
+ // src/client/core/eip3009.ts
617
+ init_esm_shims();
618
+ var EIP3009_TYPES = {
619
+ TransferWithAuthorization: [
620
+ { name: "from", type: "address" },
621
+ { name: "to", type: "address" },
622
+ { name: "value", type: "uint256" },
623
+ { name: "validAfter", type: "uint256" },
624
+ { name: "validBefore", type: "uint256" },
625
+ { name: "nonce", type: "bytes32" }
626
+ ]
627
+ };
628
+ function buildEIP3009TypedData(args) {
629
+ const validAfter = args.validAfter ?? "0";
630
+ const validBefore = args.validBefore ?? (Math.floor(Date.now() / 1e3) + 3600).toString();
631
+ const authorization = {
632
+ from: args.from,
633
+ to: args.to,
634
+ value: args.value,
635
+ validAfter,
636
+ validBefore,
637
+ nonce: args.nonce
638
+ };
639
+ return {
640
+ domain: {
641
+ name: args.tokenName,
642
+ version: args.tokenVersion,
643
+ chainId: args.chainId,
644
+ verifyingContract: args.tokenAddress
645
+ },
646
+ types: EIP3009_TYPES,
647
+ primaryType: "TransferWithAuthorization",
648
+ message: authorization
649
+ };
650
+ }
651
+
652
+ // src/client/core/eip2612.ts
653
+ init_esm_shims();
654
+
655
+ // src/client/core/bnb-intent.ts
656
+ init_esm_shims();
657
+ var BNB_INTENT_TYPES = {
658
+ PaymentIntent: [
659
+ { name: "from", type: "address" },
660
+ { name: "to", type: "address" },
661
+ { name: "amount", type: "uint256" },
662
+ { name: "token", type: "address" },
663
+ { name: "service", type: "string" },
664
+ { name: "nonce", type: "uint256" },
665
+ { name: "deadline", type: "uint256" }
666
+ ]
667
+ };
668
+ var BNB_DOMAIN_NAME = "MoltsPay";
669
+ var BNB_DOMAIN_VERSION = "1";
670
+ function buildBnbIntentTypedData(args) {
671
+ const intent = {
672
+ from: args.from,
673
+ to: args.to,
674
+ amount: args.amount,
675
+ token: args.tokenAddress,
676
+ service: args.service,
677
+ nonce: args.nonce,
678
+ deadline: args.deadline
679
+ };
680
+ return {
681
+ domain: {
682
+ name: BNB_DOMAIN_NAME,
683
+ version: BNB_DOMAIN_VERSION,
684
+ chainId: args.chainId
685
+ },
686
+ types: BNB_INTENT_TYPES,
687
+ primaryType: "PaymentIntent",
688
+ message: intent
689
+ };
690
+ }
691
+
692
+ // src/client/core/solana-tx.ts
693
+ init_esm_shims();
694
+
695
+ // src/client/core/x402.ts
696
+ init_esm_shims();
697
+
698
+ // src/client/node/signer.ts
699
+ init_esm_shims();
700
+ import { ethers } from "ethers";
701
+ import { Transaction as Transaction2 } from "@solana/web3.js";
702
+ var NodeSigner = class {
703
+ evmWallet;
704
+ getSolanaKeypair;
705
+ constructor(evmWallet, options = {}) {
706
+ this.evmWallet = evmWallet;
707
+ this.getSolanaKeypair = options.getSolanaKeypair ?? (() => null);
708
+ }
709
+ async getEvmAddress() {
710
+ return this.evmWallet.address;
711
+ }
712
+ async getSolanaAddress() {
713
+ const kp = this.getSolanaKeypair();
714
+ return kp ? kp.publicKey.toBase58() : null;
715
+ }
716
+ async signTypedData(envelope) {
717
+ const mutableTypes = {};
718
+ for (const [key, fields] of Object.entries(envelope.types)) {
719
+ mutableTypes[key] = [...fields];
720
+ }
721
+ return this.evmWallet.signTypedData(
722
+ envelope.domain,
723
+ mutableTypes,
724
+ envelope.message
725
+ );
726
+ }
727
+ async sendEvmTransaction(args) {
728
+ const chain = findChainByChainId(args.chainId);
729
+ if (!chain) {
730
+ throw new Error(`sendEvmTransaction: unknown chainId ${args.chainId}`);
731
+ }
732
+ const provider = new ethers.JsonRpcProvider(chain.rpc);
733
+ const connected = this.evmWallet.connect(provider);
734
+ const tx = await connected.sendTransaction({
735
+ to: args.to,
736
+ data: args.data,
737
+ value: args.value ? BigInt(args.value) : 0n
738
+ });
739
+ return tx.hash;
740
+ }
741
+ async signSolanaTransaction(args) {
742
+ const kp = this.getSolanaKeypair();
743
+ if (!kp) {
744
+ throw new Error("signSolanaTransaction: no Solana wallet configured");
745
+ }
746
+ const tx = Transaction2.from(Buffer.from(args.transactionBase64, "base64"));
747
+ if (args.partialSign) {
748
+ tx.partialSign(kp);
749
+ } else {
750
+ tx.sign(kp);
751
+ }
752
+ return tx.serialize({ requireAllSignatures: false }).toString("base64");
753
+ }
754
+ };
755
+ function findChainByChainId(chainId) {
756
+ for (const cfg of Object.values(CHAINS)) {
757
+ if (cfg.chainId === chainId) {
758
+ return cfg;
759
+ }
760
+ }
761
+ return void 0;
762
+ }
763
+
764
+ // src/client/types.ts
765
+ init_esm_shims();
766
+
767
+ // src/client/node/index.ts
585
768
  var DEFAULT_CONFIG = {
586
769
  chain: "base",
587
770
  limits: {
@@ -594,6 +777,7 @@ var MoltsPayClient = class {
594
777
  config;
595
778
  walletData = null;
596
779
  wallet = null;
780
+ signer = null;
597
781
  todaySpending = 0;
598
782
  lastSpendingReset = 0;
599
783
  constructor(options = {}) {
@@ -602,7 +786,11 @@ var MoltsPayClient = class {
602
786
  this.walletData = this.loadWallet();
603
787
  this.loadSpending();
604
788
  if (this.walletData) {
605
- this.wallet = new Wallet(this.walletData.privateKey);
789
+ this.wallet = new Wallet2(this.walletData.privateKey);
790
+ const configDir = this.configDir;
791
+ this.signer = new NodeSigner(this.wallet, {
792
+ getSolanaKeypair: () => loadSolanaWallet(configDir)
793
+ });
606
794
  }
607
795
  }
608
796
  /**
@@ -730,20 +918,6 @@ var MoltsPayClient = class {
730
918
  } catch {
731
919
  throw new Error("Invalid x-payment-required header");
732
920
  }
733
- const networkToChainName = (network2) => {
734
- if (network2 === "solana:mainnet") return "solana";
735
- if (network2 === "solana:devnet") return "solana_devnet";
736
- const match = network2.match(/^eip155:(\d+)$/);
737
- if (!match) return null;
738
- const chainId = parseInt(match[1]);
739
- if (chainId === 8453) return "base";
740
- if (chainId === 137) return "polygon";
741
- if (chainId === 84532) return "base_sepolia";
742
- if (chainId === 42431) return "tempo_moderato";
743
- if (chainId === 56) return "bnb";
744
- if (chainId === 97) return "bnb_testnet";
745
- return null;
746
- };
747
921
  const serverChains = requirements.map((r) => networkToChainName(r.network)).filter((c) => c !== null);
748
922
  const userSpecifiedChain = options.chain;
749
923
  let selectedChain;
@@ -972,14 +1146,14 @@ Please specify: --chain <chain_name>`
972
1146
  async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
973
1147
  const { to, amount, token, chainName, chain, spender } = paymentDetails;
974
1148
  const tokenConfig = chain.tokens[token];
975
- const provider = new ethers.JsonRpcProvider(chain.rpc);
1149
+ const provider = new ethers2.JsonRpcProvider(chain.rpc);
976
1150
  const allowance = await this.checkAllowance(tokenConfig.address, spender, provider);
977
1151
  const amountWeiCheck = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals));
978
1152
  if (allowance < amountWeiCheck) {
979
1153
  const nativeBalance = await provider.getBalance(this.wallet.address);
980
- const minGasBalance = ethers.parseEther("0.0005");
1154
+ const minGasBalance = ethers2.parseEther("0.0005");
981
1155
  if (nativeBalance < minGasBalance) {
982
- const nativeBNB = parseFloat(ethers.formatEther(nativeBalance)).toFixed(4);
1156
+ const nativeBNB = parseFloat(ethers2.formatEther(nativeBalance)).toFixed(4);
983
1157
  const isTestnet = chainName === "bnb_testnet";
984
1158
  if (isTestnet) {
985
1159
  throw new Error(
@@ -1013,35 +1187,21 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1013
1187
  );
1014
1188
  }
1015
1189
  const amountWei = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
1016
- const intent = {
1190
+ const intentNonce = Date.now();
1191
+ const intentDeadline = Date.now() + 36e5;
1192
+ const envelope = buildBnbIntentTypedData({
1017
1193
  from: this.wallet.address,
1018
1194
  to,
1019
1195
  amount: amountWei,
1020
- token: tokenConfig.address,
1196
+ tokenAddress: tokenConfig.address,
1021
1197
  service,
1022
- nonce: Date.now(),
1023
- // Use timestamp as nonce for simplicity
1024
- deadline: Date.now() + 36e5
1025
- // 1 hour
1026
- };
1027
- const domain = {
1028
- name: "MoltsPay",
1029
- version: "1",
1198
+ nonce: intentNonce,
1199
+ deadline: intentDeadline,
1030
1200
  chainId: chain.chainId
1031
- };
1032
- const types = {
1033
- PaymentIntent: [
1034
- { name: "from", type: "address" },
1035
- { name: "to", type: "address" },
1036
- { name: "amount", type: "uint256" },
1037
- { name: "token", type: "address" },
1038
- { name: "service", type: "string" },
1039
- { name: "nonce", type: "uint256" },
1040
- { name: "deadline", type: "uint256" }
1041
- ]
1042
- };
1201
+ });
1043
1202
  console.log(`[MoltsPay] Signing BNB payment intent...`);
1044
- const signature = await this.wallet.signTypedData(domain, types, intent);
1203
+ const signature = await this.signer.signTypedData(envelope);
1204
+ const intent = envelope.message;
1045
1205
  const network = `eip155:${chain.chainId}`;
1046
1206
  const payload = {
1047
1207
  x402Version: 2,
@@ -1115,12 +1275,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1115
1275
  feePayerPubkey
1116
1276
  // Optional fee payer for gasless mode
1117
1277
  );
1118
- if (feePayerPubkey) {
1119
- transaction.partialSign(solanaWallet);
1120
- } else {
1121
- transaction.sign(solanaWallet);
1122
- }
1123
- const signedTx = transaction.serialize({ requireAllSignatures: false }).toString("base64");
1278
+ const unsignedBase64 = transaction.serialize({ requireAllSignatures: false, verifySignatures: false }).toString("base64");
1279
+ const signedTx = await this.signer.signSolanaTransaction({
1280
+ transactionBase64: unsignedBase64,
1281
+ partialSign: !!feePayerPubkey
1282
+ });
1124
1283
  console.log(`[MoltsPay] Transaction signed, sending to server...`);
1125
1284
  const network = chain === "solana" ? "solana:mainnet" : "solana:devnet";
1126
1285
  const payload = {
@@ -1167,7 +1326,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1167
1326
  * Check ERC20 allowance for a spender
1168
1327
  */
1169
1328
  async checkAllowance(tokenAddress, spender, provider) {
1170
- const contract = new ethers.Contract(
1329
+ const contract = new ethers2.Contract(
1171
1330
  tokenAddress,
1172
1331
  ["function allowance(address owner, address spender) view returns (uint256)"],
1173
1332
  provider
@@ -1178,41 +1337,29 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1178
1337
  * Sign EIP-3009 transferWithAuthorization (GASLESS)
1179
1338
  * This only signs - no on-chain transaction, no gas needed.
1180
1339
  * Supports both USDC and USDT.
1340
+ *
1341
+ * Delegates typed-data construction to `core/eip3009.ts` and the signature
1342
+ * itself to `this.signer`. That way Web Client (Phase 4) can reuse the same
1343
+ * flow with an EIP-1193 signer without duplicating typed-data layout.
1181
1344
  */
1182
1345
  async signEIP3009(to, amount, chain, token = "USDC", domainOverride) {
1183
- const validAfter = 0;
1184
- const validBefore = Math.floor(Date.now() / 1e3) + 3600;
1185
- const nonce = ethers.hexlify(ethers.randomBytes(32));
1186
1346
  const tokenConfig = chain.tokens[token];
1187
1347
  const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
1188
- const authorization = {
1348
+ const nonce = ethers2.hexlify(ethers2.randomBytes(32));
1349
+ const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
1350
+ const tokenVersion = domainOverride?.version || "2";
1351
+ const envelope = buildEIP3009TypedData({
1189
1352
  from: this.wallet.address,
1190
1353
  to,
1191
1354
  value,
1192
- validAfter: validAfter.toString(),
1193
- validBefore: validBefore.toString(),
1194
- nonce
1195
- };
1196
- const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
1197
- const tokenVersion = domainOverride?.version || "2";
1198
- const domain = {
1199
- name: tokenName,
1200
- version: tokenVersion,
1355
+ nonce,
1201
1356
  chainId: chain.chainId,
1202
- verifyingContract: tokenConfig.address
1203
- };
1204
- const types = {
1205
- TransferWithAuthorization: [
1206
- { name: "from", type: "address" },
1207
- { name: "to", type: "address" },
1208
- { name: "value", type: "uint256" },
1209
- { name: "validAfter", type: "uint256" },
1210
- { name: "validBefore", type: "uint256" },
1211
- { name: "nonce", type: "bytes32" }
1212
- ]
1213
- };
1214
- const signature = await this.wallet.signTypedData(domain, types, authorization);
1215
- return { authorization, signature };
1357
+ tokenAddress: tokenConfig.address,
1358
+ tokenName,
1359
+ tokenVersion
1360
+ });
1361
+ const signature = await this.signer.signTypedData(envelope);
1362
+ return { authorization: envelope.message, signature };
1216
1363
  }
1217
1364
  /**
1218
1365
  * Check spending limits
@@ -1316,7 +1463,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1316
1463
  */
1317
1464
  static init(configDir, options) {
1318
1465
  mkdirSync2(configDir, { recursive: true });
1319
- const wallet = Wallet.createRandom();
1466
+ const wallet = Wallet2.createRandom();
1320
1467
  const walletData = {
1321
1468
  address: wallet.address,
1322
1469
  privateKey: wallet.privateKey,
@@ -1348,17 +1495,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1348
1495
  } catch {
1349
1496
  throw new Error(`Unknown chain: ${this.config.chain}`);
1350
1497
  }
1351
- const provider = new ethers.JsonRpcProvider(chain.rpc);
1498
+ const provider = new ethers2.JsonRpcProvider(chain.rpc);
1352
1499
  const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
1353
1500
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
1354
1501
  provider.getBalance(this.wallet.address),
1355
- new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1356
- new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1502
+ new ethers2.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1503
+ new ethers2.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1357
1504
  ]);
1358
1505
  return {
1359
- usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1360
- usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1361
- native: parseFloat(ethers.formatEther(nativeBalance))
1506
+ usdc: parseFloat(ethers2.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1507
+ usdt: parseFloat(ethers2.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1508
+ native: parseFloat(ethers2.formatEther(nativeBalance))
1362
1509
  };
1363
1510
  }
1364
1511
  /**
@@ -1381,38 +1528,38 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1381
1528
  supportedChains.map(async (chainName) => {
1382
1529
  try {
1383
1530
  const chain = getChain(chainName);
1384
- const provider = new ethers.JsonRpcProvider(chain.rpc);
1531
+ const provider = new ethers2.JsonRpcProvider(chain.rpc);
1385
1532
  if (chainName === "tempo_moderato") {
1386
1533
  const [nativeBalance, pathUSD, alphaUSD, betaUSD, thetaUSD] = await Promise.all([
1387
1534
  provider.getBalance(this.wallet.address),
1388
- new ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1389
- new ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1390
- new ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1391
- new ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
1535
+ new ethers2.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1536
+ new ethers2.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1537
+ new ethers2.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1538
+ new ethers2.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
1392
1539
  ]);
1393
1540
  results[chainName] = {
1394
- usdc: parseFloat(ethers.formatUnits(pathUSD, 6)),
1541
+ usdc: parseFloat(ethers2.formatUnits(pathUSD, 6)),
1395
1542
  // pathUSD as default USDC
1396
- usdt: parseFloat(ethers.formatUnits(alphaUSD, 6)),
1543
+ usdt: parseFloat(ethers2.formatUnits(alphaUSD, 6)),
1397
1544
  // alphaUSD as default USDT
1398
- native: parseFloat(ethers.formatEther(nativeBalance)),
1545
+ native: parseFloat(ethers2.formatEther(nativeBalance)),
1399
1546
  tempo: {
1400
- pathUSD: parseFloat(ethers.formatUnits(pathUSD, 6)),
1401
- alphaUSD: parseFloat(ethers.formatUnits(alphaUSD, 6)),
1402
- betaUSD: parseFloat(ethers.formatUnits(betaUSD, 6)),
1403
- thetaUSD: parseFloat(ethers.formatUnits(thetaUSD, 6))
1547
+ pathUSD: parseFloat(ethers2.formatUnits(pathUSD, 6)),
1548
+ alphaUSD: parseFloat(ethers2.formatUnits(alphaUSD, 6)),
1549
+ betaUSD: parseFloat(ethers2.formatUnits(betaUSD, 6)),
1550
+ thetaUSD: parseFloat(ethers2.formatUnits(thetaUSD, 6))
1404
1551
  }
1405
1552
  };
1406
1553
  } else {
1407
1554
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
1408
1555
  provider.getBalance(this.wallet.address),
1409
- new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1410
- new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1556
+ new ethers2.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1557
+ new ethers2.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1411
1558
  ]);
1412
1559
  results[chainName] = {
1413
- usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1414
- usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1415
- native: parseFloat(ethers.formatEther(nativeBalance))
1560
+ usdc: parseFloat(ethers2.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1561
+ usdt: parseFloat(ethers2.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1562
+ native: parseFloat(ethers2.formatEther(nativeBalance))
1416
1563
  };
1417
1564
  }
1418
1565
  } catch (err) {
@@ -1770,16 +1917,40 @@ var CDPFacilitator = class extends BaseFacilitator {
1770
1917
 
1771
1918
  // src/facilitators/tempo.ts
1772
1919
  init_esm_shims();
1920
+ import { ethers as ethers3 } from "ethers";
1773
1921
  var TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
1922
+ var TIP20_PERMIT_ABI = [
1923
+ "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
1924
+ "function transferFrom(address from, address to, uint256 value) returns (bool)"
1925
+ ];
1774
1926
  var TempoFacilitator = class extends BaseFacilitator {
1775
1927
  name = "tempo";
1776
1928
  displayName = "Tempo Testnet";
1777
1929
  supportedNetworks = ["eip155:42431"];
1778
1930
  // Tempo Moderato
1779
1931
  rpcUrl;
1932
+ settlerWallet = null;
1780
1933
  constructor() {
1781
1934
  super();
1782
1935
  this.rpcUrl = CHAINS.tempo_moderato.rpc;
1936
+ const settlerKey = process.env.TEMPO_SETTLER_KEY;
1937
+ if (settlerKey) {
1938
+ try {
1939
+ const provider = new ethers3.JsonRpcProvider(this.rpcUrl);
1940
+ this.settlerWallet = new ethers3.Wallet(settlerKey, provider);
1941
+ } catch (err) {
1942
+ console.warn("[TempoFacilitator] Invalid TEMPO_SETTLER_KEY, permit settlement disabled:", err);
1943
+ this.settlerWallet = null;
1944
+ }
1945
+ }
1946
+ }
1947
+ /**
1948
+ * Settler EOA address advertised to clients via `X-Payment-Required.extra.tempoSpender`.
1949
+ * Web Client uses this as the `spender` field in the signed EIP-2612 Permit.
1950
+ * Returns null if no TEMPO_SETTLER_KEY is configured — permit settlement unavailable.
1951
+ */
1952
+ getSpenderAddress() {
1953
+ return this.settlerWallet?.address ?? null;
1783
1954
  }
1784
1955
  async healthCheck() {
1785
1956
  const start = Date.now();
@@ -1805,6 +1976,44 @@ var TempoFacilitator = class extends BaseFacilitator {
1805
1976
  }
1806
1977
  }
1807
1978
  async verify(paymentPayload, requirements) {
1979
+ const inner = paymentPayload.payload;
1980
+ if (inner && "permit" in inner && inner.permit) {
1981
+ return this.verifyPermit(inner, requirements);
1982
+ }
1983
+ return this.verifyTxHash(paymentPayload, requirements);
1984
+ }
1985
+ /**
1986
+ * Structural validation of an EIP-2612 permit payload. Does NOT submit
1987
+ * anything on-chain — actual submission happens in settlePermit().
1988
+ */
1989
+ async verifyPermit(payload, requirements) {
1990
+ if (!this.settlerWallet) {
1991
+ return { valid: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
1992
+ }
1993
+ const p = payload.permit;
1994
+ if (!p || !p.owner || !p.spender || !p.value || !p.deadline) {
1995
+ return { valid: false, error: "Invalid permit payload: missing fields" };
1996
+ }
1997
+ if (p.spender.toLowerCase() !== this.settlerWallet.address.toLowerCase()) {
1998
+ return {
1999
+ valid: false,
2000
+ error: `Permit spender ${p.spender} does not match configured settler ${this.settlerWallet.address}`
2001
+ };
2002
+ }
2003
+ const deadline = BigInt(p.deadline);
2004
+ const now = BigInt(Math.floor(Date.now() / 1e3));
2005
+ if (deadline <= now) {
2006
+ return { valid: false, error: "Permit deadline has expired" };
2007
+ }
2008
+ if (BigInt(p.value) < BigInt(requirements.amount || "0")) {
2009
+ return {
2010
+ valid: false,
2011
+ error: `Permit value ${p.value} is less than required ${requirements.amount}`
2012
+ };
2013
+ }
2014
+ return { valid: true, details: { scheme: "permit", owner: p.owner } };
2015
+ }
2016
+ async verifyTxHash(paymentPayload, requirements) {
1808
2017
  try {
1809
2018
  const tempoPayload = paymentPayload.payload;
1810
2019
  if (!tempoPayload?.txHash) {
@@ -1862,7 +2071,11 @@ var TempoFacilitator = class extends BaseFacilitator {
1862
2071
  }
1863
2072
  }
1864
2073
  async settle(paymentPayload, requirements) {
1865
- const verifyResult = await this.verify(paymentPayload, requirements);
2074
+ const inner = paymentPayload.payload;
2075
+ if (inner && "permit" in inner && inner.permit) {
2076
+ return this.settlePermit(inner, requirements);
2077
+ }
2078
+ const verifyResult = await this.verifyTxHash(paymentPayload, requirements);
1866
2079
  if (!verifyResult.valid) {
1867
2080
  return { success: false, error: verifyResult.error };
1868
2081
  }
@@ -1873,6 +2086,52 @@ var TempoFacilitator = class extends BaseFacilitator {
1873
2086
  status: "settled"
1874
2087
  };
1875
2088
  }
2089
+ /**
2090
+ * EIP-2612 permit settlement path. Submits two transactions on Tempo:
2091
+ * 1. pathUSD.permit(owner, spender=settler, value, deadline, v, r, s)
2092
+ * 2. pathUSD.transferFrom(owner, payTo, value)
2093
+ *
2094
+ * The settler EOA pays Tempo gas (via the TIP-20 `feeToken` mechanism — no
2095
+ * native tTEMPO required; any held TIP-20 token balance covers fees).
2096
+ */
2097
+ async settlePermit(payload, requirements) {
2098
+ if (!this.settlerWallet) {
2099
+ return { success: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
2100
+ }
2101
+ if (!requirements.asset || !requirements.payTo) {
2102
+ return { success: false, error: "Missing asset or payTo in requirements" };
2103
+ }
2104
+ const verifyResult = await this.verifyPermit(payload, requirements);
2105
+ if (!verifyResult.valid) {
2106
+ return { success: false, error: verifyResult.error };
2107
+ }
2108
+ const token = new ethers3.Contract(requirements.asset, TIP20_PERMIT_ABI, this.settlerWallet);
2109
+ const p = payload.permit;
2110
+ try {
2111
+ const permitTx = await token.permit(
2112
+ p.owner,
2113
+ p.spender,
2114
+ p.value,
2115
+ p.deadline,
2116
+ p.v,
2117
+ p.r,
2118
+ p.s
2119
+ );
2120
+ await permitTx.wait();
2121
+ const transferTx = await token.transferFrom(p.owner, requirements.payTo, p.value);
2122
+ await transferTx.wait();
2123
+ return {
2124
+ success: true,
2125
+ transaction: transferTx.hash,
2126
+ status: "settled"
2127
+ };
2128
+ } catch (err) {
2129
+ return {
2130
+ success: false,
2131
+ error: `Tempo permit settlement failed: ${err.message}`
2132
+ };
2133
+ }
2134
+ }
1876
2135
  async getTransactionReceipt(txHash) {
1877
2136
  const response = await fetch(this.rpcUrl, {
1878
2137
  method: "POST",
@@ -2116,12 +2375,12 @@ var BNBFacilitator = class extends BaseFacilitator {
2116
2375
  return this.spenderAddress;
2117
2376
  }
2118
2377
  async getServerAddress() {
2119
- const { ethers: ethers3 } = await import("ethers");
2120
- const wallet = new ethers3.Wallet(this.serverPrivateKey);
2378
+ const { ethers: ethers5 } = await import("ethers");
2379
+ const wallet = new ethers5.Wallet(this.serverPrivateKey);
2121
2380
  return wallet.address;
2122
2381
  }
2123
2382
  async recoverIntentSigner(intent, chainId) {
2124
- const { ethers: ethers3 } = await import("ethers");
2383
+ const { ethers: ethers5 } = await import("ethers");
2125
2384
  const domain = {
2126
2385
  ...EIP712_DOMAIN,
2127
2386
  chainId
@@ -2135,7 +2394,7 @@ var BNBFacilitator = class extends BaseFacilitator {
2135
2394
  nonce: intent.nonce,
2136
2395
  deadline: intent.deadline
2137
2396
  };
2138
- const recoveredAddress = ethers3.verifyTypedData(
2397
+ const recoveredAddress = ethers5.verifyTypedData(
2139
2398
  domain,
2140
2399
  INTENT_TYPES,
2141
2400
  message,
@@ -2179,10 +2438,10 @@ var BNBFacilitator = class extends BaseFacilitator {
2179
2438
  return result.result || "0x0";
2180
2439
  }
2181
2440
  async executeTransferFrom(from, to, amount, token, rpcUrl) {
2182
- const { ethers: ethers3 } = await import("ethers");
2183
- const provider = new ethers3.JsonRpcProvider(rpcUrl);
2184
- const wallet = new ethers3.Wallet(this.serverPrivateKey, provider);
2185
- const tokenContract = new ethers3.Contract(token, [
2441
+ const { ethers: ethers5 } = await import("ethers");
2442
+ const provider = new ethers5.JsonRpcProvider(rpcUrl);
2443
+ const wallet = new ethers5.Wallet(this.serverPrivateKey, provider);
2444
+ const tokenContract = new ethers5.Contract(token, [
2186
2445
  "function transferFrom(address from, address to, uint256 amount) returns (bool)"
2187
2446
  ], wallet);
2188
2447
  const tx = await tokenContract.transferFrom(from, to, amount);
@@ -2207,7 +2466,7 @@ var BNBFacilitator = class extends BaseFacilitator {
2207
2466
 
2208
2467
  // src/facilitators/registry.ts
2209
2468
  init_esm_shims();
2210
- import { Keypair as Keypair4 } from "@solana/web3.js";
2469
+ import { Keypair as Keypair5 } from "@solana/web3.js";
2211
2470
  import bs582 from "bs58";
2212
2471
  var FacilitatorRegistry = class {
2213
2472
  factories = /* @__PURE__ */ new Map();
@@ -2223,7 +2482,7 @@ var FacilitatorRegistry = class {
2223
2482
  const feePayerKey = config?.feePayerPrivateKey || process.env.SOLANA_FEE_PAYER_KEY;
2224
2483
  if (feePayerKey) {
2225
2484
  try {
2226
- feePayerKeypair = Keypair4.fromSecretKey(bs582.decode(feePayerKey));
2485
+ feePayerKeypair = Keypair5.fromSecretKey(bs582.decode(feePayerKey));
2227
2486
  } catch (e) {
2228
2487
  console.warn(`[SolanaFacilitator] Invalid fee payer key: ${e.message}`);
2229
2488
  }
@@ -2498,7 +2757,7 @@ var TOKEN_ADDRESSES = {
2498
2757
  // Devnet USDC
2499
2758
  }
2500
2759
  };
2501
- var CHAIN_TO_NETWORK = {
2760
+ var CHAIN_TO_NETWORK2 = {
2502
2761
  "base": "eip155:8453",
2503
2762
  "base_sepolia": "eip155:84532",
2504
2763
  "polygon": "eip155:137",
@@ -2529,9 +2788,13 @@ var TOKEN_DOMAINS = {
2529
2788
  USDT: { name: "(PoS) Tether USD", version: "2" }
2530
2789
  },
2531
2790
  // Tempo Moderato testnet - TIP-20 stablecoins
2791
+ // Domain names verified against on-chain DOMAIN_SEPARATOR values on 2026-04-21.
2792
+ // See docs/TEMPO-WEB-SUPPORT.md Section 2 and test/server/tempo-domain.test.ts.
2793
+ // All 4 Tempo TIP-20 tokens (pathUSD / AlphaUSD / BetaUSD / ThetaUSD) use
2794
+ // the token symbol with first letter capitalized + version "1".
2532
2795
  "eip155:42431": {
2533
- USDC: { name: "pathUSD", version: "1" },
2534
- USDT: { name: "alphaUSD", version: "1" }
2796
+ USDC: { name: "PathUSD", version: "1" },
2797
+ USDT: { name: "AlphaUSD", version: "1" }
2535
2798
  },
2536
2799
  // BNB Smart Chain mainnet
2537
2800
  "eip155:56": {
@@ -2654,14 +2917,14 @@ var MoltsPayServer = class {
2654
2917
  const chainName = typeof c === "string" ? c : c.chain;
2655
2918
  const explicitWallet = typeof c === "object" ? c.wallet : null;
2656
2919
  return {
2657
- network: CHAIN_TO_NETWORK[chainName] || "eip155:8453",
2920
+ network: CHAIN_TO_NETWORK2[chainName] || "eip155:8453",
2658
2921
  wallet: getWalletForChain(chainName, explicitWallet || void 0),
2659
2922
  tokens: (typeof c === "object" ? c.tokens : null) || ["USDC"]
2660
2923
  };
2661
2924
  });
2662
2925
  }
2663
2926
  const chain = provider.chain || "base";
2664
- const network = CHAIN_TO_NETWORK[chain] || this.networkId;
2927
+ const network = CHAIN_TO_NETWORK2[chain] || this.networkId;
2665
2928
  return [{
2666
2929
  network,
2667
2930
  wallet: getWalletForChain(chain),
@@ -2700,13 +2963,62 @@ var MoltsPayServer = class {
2700
2963
  });
2701
2964
  }
2702
2965
  /**
2703
- * Handle incoming request
2966
+ * Apply CORS response headers according to the `cors` option.
2967
+ *
2968
+ * Default (`cors` unset or `true`): `Access-Control-Allow-Origin: *`. Matches 1.5.x behavior
2969
+ * and works for every browser client whose origin does not need to send cookies.
2970
+ *
2971
+ * `cors: false`: emit no CORS headers. Same-origin only.
2972
+ * `cors: string[]`: origin allowlist — echo the origin back iff it matches.
2973
+ * `cors: CorsOptions`: full control (allowlist + credentials + maxAge).
2974
+ *
2975
+ * The required-for-Web response headers are always exposed when CORS is active:
2976
+ * `X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt`.
2704
2977
  */
2705
- async handleRequest(req, res) {
2706
- res.setHeader("Access-Control-Allow-Origin", "*");
2978
+ applyCorsHeaders(req, res) {
2979
+ const cors = this.options.cors;
2980
+ if (cors === false) {
2981
+ return;
2982
+ }
2983
+ const requestOrigin = req.headers.origin ?? "*";
2984
+ if (cors === void 0 || cors === true) {
2985
+ this.writeCorsHeaders(res, "*");
2986
+ return;
2987
+ }
2988
+ if (Array.isArray(cors)) {
2989
+ if (cors.includes(requestOrigin)) {
2990
+ this.writeCorsHeaders(res, requestOrigin);
2991
+ res.setHeader("Vary", "Origin");
2992
+ }
2993
+ return;
2994
+ }
2995
+ const opt = cors;
2996
+ const isAllowed = typeof opt.origins === "function" ? opt.origins(requestOrigin) : opt.origins.includes(requestOrigin);
2997
+ if (!isAllowed) {
2998
+ return;
2999
+ }
3000
+ this.writeCorsHeaders(res, requestOrigin);
3001
+ res.setHeader("Vary", "Origin");
3002
+ if (opt.credentials) {
3003
+ res.setHeader("Access-Control-Allow-Credentials", "true");
3004
+ }
3005
+ const maxAge = opt.maxAge ?? 600;
3006
+ res.setHeader("Access-Control-Max-Age", String(maxAge));
3007
+ }
3008
+ writeCorsHeaders(res, origin) {
3009
+ res.setHeader("Access-Control-Allow-Origin", origin);
2707
3010
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
2708
3011
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Payment, Authorization");
2709
- res.setHeader("Access-Control-Expose-Headers", "X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt");
3012
+ res.setHeader(
3013
+ "Access-Control-Expose-Headers",
3014
+ "X-Payment-Required, X-Payment-Response, WWW-Authenticate, Payment-Receipt"
3015
+ );
3016
+ }
3017
+ /**
3018
+ * Handle incoming request
3019
+ */
3020
+ async handleRequest(req, res) {
3021
+ this.applyCorsHeaders(req, res);
2710
3022
  if (req.method === "OPTIONS") {
2711
3023
  res.writeHead(204);
2712
3024
  res.end();
@@ -2929,6 +3241,14 @@ var MoltsPayServer = class {
2929
3241
  console.log(`[MoltsPay] Payment settled by ${settlement.facilitator}: ${settlement.transaction || "pending"}`);
2930
3242
  } catch (err) {
2931
3243
  console.error("[MoltsPay] Settlement failed:", err.message);
3244
+ settlement = { success: false, error: err.message, facilitator: "none" };
3245
+ }
3246
+ if (!settlement?.success) {
3247
+ return this.sendJson(res, 402, {
3248
+ error: "Payment settlement failed",
3249
+ message: settlement?.error || "Settlement returned no success state",
3250
+ facilitator: settlement?.facilitator
3251
+ });
2932
3252
  }
2933
3253
  }
2934
3254
  const responseHeaders = {};
@@ -3183,7 +3503,7 @@ var MoltsPayServer = class {
3183
3503
  }
3184
3504
  const scheme = payment.accepted?.scheme || payment.scheme;
3185
3505
  const network = payment.accepted?.network || payment.network || this.networkId;
3186
- if (scheme !== "exact") {
3506
+ if (scheme !== "exact" && scheme !== "permit") {
3187
3507
  return { valid: false, error: `Unsupported scheme: ${scheme}` };
3188
3508
  }
3189
3509
  if (!this.isNetworkAccepted(network)) {
@@ -3205,8 +3525,10 @@ var MoltsPayServer = class {
3205
3525
  const tokenAddresses = TOKEN_ADDRESSES[selectedNetwork] || {};
3206
3526
  const tokenAddress = tokenAddresses[selectedToken];
3207
3527
  const tokenDomain = getTokenDomain(selectedNetwork, selectedToken);
3528
+ const isTempo = selectedNetwork === "eip155:42431";
3529
+ const scheme = isTempo ? "permit" : "exact";
3208
3530
  const requirements = {
3209
- scheme: "exact",
3531
+ scheme,
3210
3532
  network: selectedNetwork,
3211
3533
  asset: tokenAddress,
3212
3534
  amount: amountInUnits,
@@ -3234,6 +3556,16 @@ var MoltsPayServer = class {
3234
3556
  };
3235
3557
  }
3236
3558
  }
3559
+ if (isTempo) {
3560
+ const tempoFacilitator = this.registry.get("tempo");
3561
+ const tempoSpender = tempoFacilitator?.getSpenderAddress?.();
3562
+ if (tempoSpender) {
3563
+ requirements.extra = {
3564
+ ...requirements.extra || {},
3565
+ tempoSpender
3566
+ };
3567
+ }
3568
+ }
3237
3569
  return requirements;
3238
3570
  }
3239
3571
  /**
@@ -3368,10 +3700,10 @@ var MoltsPayServer = class {
3368
3700
  }
3369
3701
  const scheme = payment.accepted?.scheme || payment.scheme;
3370
3702
  const network = payment.accepted?.network || payment.network;
3371
- if (scheme !== "exact") {
3703
+ if (scheme !== "exact" && scheme !== "permit") {
3372
3704
  return this.sendJson(res, 402, { error: `Unsupported scheme: ${scheme}` });
3373
3705
  }
3374
- const expectedNetwork = chain ? CHAIN_TO_NETWORK[chain] || this.networkId : this.networkId;
3706
+ const expectedNetwork = chain ? CHAIN_TO_NETWORK2[chain] || this.networkId : this.networkId;
3375
3707
  if (network !== expectedNetwork) {
3376
3708
  return this.sendJson(res, 402, { error: `Network mismatch: expected ${expectedNetwork}, got ${network}` });
3377
3709
  }
@@ -3633,7 +3965,7 @@ var MoltsPayServer = class {
3633
3965
  buildProxyPaymentRequirements(config, wallet, token, chain) {
3634
3966
  const amountInUnits = Math.floor(config.price * 1e6).toString();
3635
3967
  const acceptedTokens = getAcceptedCurrencies(config);
3636
- const networkId = chain ? CHAIN_TO_NETWORK[chain] || this.networkId : this.networkId;
3968
+ const networkId = chain ? CHAIN_TO_NETWORK2[chain] || this.networkId : this.networkId;
3637
3969
  const selectedToken = token && acceptedTokens.includes(token) ? token : acceptedTokens[0];
3638
3970
  const tokenAddresses = TOKEN_ADDRESSES[networkId] || TOKEN_ADDRESSES[this.networkId] || {};
3639
3971
  const tokenAddress = tokenAddresses[selectedToken];
@@ -3731,7 +4063,7 @@ var ERC20_APPROVE_ABI = [
3731
4063
  ];
3732
4064
  async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = false) {
3733
4065
  const chainConfig = CHAINS[chain];
3734
- const provider = new ethers2.JsonRpcProvider(chainConfig.rpc);
4066
+ const provider = new ethers4.JsonRpcProvider(chainConfig.rpc);
3735
4067
  const wallet = client.getWallet();
3736
4068
  if (!wallet) {
3737
4069
  console.log(" \u274C No wallet found");
@@ -3740,15 +4072,15 @@ async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = fal
3740
4072
  const signer = wallet.connect(provider);
3741
4073
  console.log(` Spender: ${spenderAddress}`);
3742
4074
  let bnbBalance = await provider.getBalance(wallet.address);
3743
- const minGasRequired = ethers2.parseEther("0.0005");
4075
+ const minGasRequired = ethers4.parseEther("0.0005");
3744
4076
  if (bnbBalance < minGasRequired) {
3745
4077
  if (sponsorGas && BNB_SPONSOR_KEY) {
3746
4078
  console.log(" \u23F3 Sponsoring BNB gas for approvals...");
3747
4079
  try {
3748
- const sponsorWallet = new ethers2.Wallet(BNB_SPONSOR_KEY, provider);
4080
+ const sponsorWallet = new ethers4.Wallet(BNB_SPONSOR_KEY, provider);
3749
4081
  const tx = await sponsorWallet.sendTransaction({
3750
4082
  to: wallet.address,
3751
- value: ethers2.parseEther("0.001")
4083
+ value: ethers4.parseEther("0.001")
3752
4084
  });
3753
4085
  await tx.wait();
3754
4086
  console.log(` \u2705 Sponsored 0.001 BNB (tx: ${tx.hash.slice(0, 10)}...)`);
@@ -3767,7 +4099,7 @@ async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = fal
3767
4099
  }
3768
4100
  for (const tokenSymbol of ["USDT", "USDC"]) {
3769
4101
  const tokenConfig = chainConfig.tokens[tokenSymbol];
3770
- const tokenContract = new ethers2.Contract(tokenConfig.address, ERC20_APPROVE_ABI, signer);
4102
+ const tokenContract = new ethers4.Contract(tokenConfig.address, ERC20_APPROVE_ABI, signer);
3771
4103
  const allowance = await tokenContract.allowance(wallet.address, spenderAddress);
3772
4104
  if (allowance > 0n) {
3773
4105
  console.log(` \u2705 ${tokenSymbol}: already approved for ${spenderAddress.slice(0, 10)}...`);
@@ -3775,7 +4107,7 @@ async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = fal
3775
4107
  }
3776
4108
  console.log(` \u23F3 Approving ${tokenSymbol}...`);
3777
4109
  try {
3778
- const tx = await tokenContract.approve(spenderAddress, ethers2.MaxUint256);
4110
+ const tx = await tokenContract.approve(spenderAddress, ethers4.MaxUint256);
3779
4111
  await tx.wait();
3780
4112
  console.log(` \u2705 ${tokenSymbol}: approved (tx: ${tx.hash.slice(0, 10)}...)`);
3781
4113
  } catch (err) {
@@ -3786,7 +4118,7 @@ async function setupBNBApprovals(client, chain, spenderAddress, sponsorGas = fal
3786
4118
  }
3787
4119
  async function checkBNBApprovals(address, chain, configDir = DEFAULT_CONFIG_DIR2) {
3788
4120
  const chainConfig = CHAINS[chain];
3789
- const provider = new ethers2.JsonRpcProvider(chainConfig.rpc);
4121
+ const provider = new ethers4.JsonRpcProvider(chainConfig.rpc);
3790
4122
  let spenderAddress = null;
3791
4123
  try {
3792
4124
  const walletPath = join5(configDir, "wallet.json");
@@ -3800,7 +4132,7 @@ async function checkBNBApprovals(address, chain, configDir = DEFAULT_CONFIG_DIR2
3800
4132
  }
3801
4133
  for (const tokenSymbol of ["USDT", "USDC"]) {
3802
4134
  const tokenConfig = chainConfig.tokens[tokenSymbol];
3803
- const tokenContract = new ethers2.Contract(tokenConfig.address, ERC20_APPROVE_ABI, provider);
4135
+ const tokenContract = new ethers4.Contract(tokenConfig.address, ERC20_APPROVE_ABI, provider);
3804
4136
  const allowance = await tokenContract.allowance(address, spenderAddress);
3805
4137
  result[tokenSymbol.toLowerCase()] = allowance > 0n;
3806
4138
  }