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.
@@ -1,8 +1,8 @@
1
- // src/client/index.ts
1
+ // src/client/node/index.ts
2
2
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, statSync, chmodSync } from "fs";
3
3
  import { homedir as homedir2 } from "os";
4
4
  import { join as join2 } from "path";
5
- import { Wallet, ethers } from "ethers";
5
+ import { Wallet as Wallet2, ethers as ethers2 } from "ethers";
6
6
 
7
7
  // src/chains/index.ts
8
8
  var CHAINS = {
@@ -253,16 +253,16 @@ import {
253
253
  getAccount as getAccount2,
254
254
  createAssociatedTokenAccountInstruction
255
255
  } from "@solana/spl-token";
256
- async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey) {
256
+ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey, connection) {
257
257
  const chainConfig = SOLANA_CHAINS[chain];
258
- const connection = new Connection3(chainConfig.rpc, "confirmed");
258
+ const conn = connection ?? new Connection3(chainConfig.rpc, "confirmed");
259
259
  const mint = new PublicKey3(chainConfig.tokens.USDC.mint);
260
260
  const actualFeePayer = feePayerPubkey || senderPubkey;
261
261
  const senderATA = await getAssociatedTokenAddress2(mint, senderPubkey);
262
262
  const recipientATA = await getAssociatedTokenAddress2(mint, recipientPubkey);
263
263
  const transaction = new Transaction();
264
264
  try {
265
- await getAccount2(connection, recipientATA);
265
+ await getAccount2(conn, recipientATA);
266
266
  } catch {
267
267
  transaction.add(
268
268
  createAssociatedTokenAccountInstruction(
@@ -293,17 +293,178 @@ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amo
293
293
  // decimals
294
294
  )
295
295
  );
296
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
296
+ const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();
297
297
  transaction.recentBlockhash = blockhash;
298
298
  transaction.feePayer = actualFeePayer;
299
299
  return transaction;
300
300
  }
301
301
 
302
- // src/client/index.ts
302
+ // src/client/node/index.ts
303
303
  import { PublicKey as PublicKey4 } from "@solana/web3.js";
304
+
305
+ // src/client/core/types.ts
304
306
  var X402_VERSION = 2;
305
307
  var PAYMENT_REQUIRED_HEADER = "x-payment-required";
306
308
  var PAYMENT_HEADER = "x-payment";
309
+
310
+ // src/client/core/chain-map.ts
311
+ var NETWORK_TO_CHAIN = {
312
+ "eip155:8453": "base",
313
+ "eip155:137": "polygon",
314
+ "eip155:84532": "base_sepolia",
315
+ "eip155:42431": "tempo_moderato",
316
+ "eip155:56": "bnb",
317
+ "eip155:97": "bnb_testnet",
318
+ "solana:mainnet": "solana",
319
+ "solana:devnet": "solana_devnet"
320
+ };
321
+ var CHAIN_TO_NETWORK = Object.fromEntries(
322
+ Object.entries(NETWORK_TO_CHAIN).map(([network, chain]) => [chain, network])
323
+ );
324
+ function networkToChainName(network) {
325
+ return NETWORK_TO_CHAIN[network] ?? null;
326
+ }
327
+
328
+ // src/client/core/base64.ts
329
+ var BufferCtor = globalThis.Buffer;
330
+
331
+ // src/client/core/eip3009.ts
332
+ var EIP3009_TYPES = {
333
+ TransferWithAuthorization: [
334
+ { name: "from", type: "address" },
335
+ { name: "to", type: "address" },
336
+ { name: "value", type: "uint256" },
337
+ { name: "validAfter", type: "uint256" },
338
+ { name: "validBefore", type: "uint256" },
339
+ { name: "nonce", type: "bytes32" }
340
+ ]
341
+ };
342
+ function buildEIP3009TypedData(args) {
343
+ const validAfter = args.validAfter ?? "0";
344
+ const validBefore = args.validBefore ?? (Math.floor(Date.now() / 1e3) + 3600).toString();
345
+ const authorization = {
346
+ from: args.from,
347
+ to: args.to,
348
+ value: args.value,
349
+ validAfter,
350
+ validBefore,
351
+ nonce: args.nonce
352
+ };
353
+ return {
354
+ domain: {
355
+ name: args.tokenName,
356
+ version: args.tokenVersion,
357
+ chainId: args.chainId,
358
+ verifyingContract: args.tokenAddress
359
+ },
360
+ types: EIP3009_TYPES,
361
+ primaryType: "TransferWithAuthorization",
362
+ message: authorization
363
+ };
364
+ }
365
+
366
+ // src/client/core/bnb-intent.ts
367
+ var BNB_INTENT_TYPES = {
368
+ PaymentIntent: [
369
+ { name: "from", type: "address" },
370
+ { name: "to", type: "address" },
371
+ { name: "amount", type: "uint256" },
372
+ { name: "token", type: "address" },
373
+ { name: "service", type: "string" },
374
+ { name: "nonce", type: "uint256" },
375
+ { name: "deadline", type: "uint256" }
376
+ ]
377
+ };
378
+ var BNB_DOMAIN_NAME = "MoltsPay";
379
+ var BNB_DOMAIN_VERSION = "1";
380
+ function buildBnbIntentTypedData(args) {
381
+ const intent = {
382
+ from: args.from,
383
+ to: args.to,
384
+ amount: args.amount,
385
+ token: args.tokenAddress,
386
+ service: args.service,
387
+ nonce: args.nonce,
388
+ deadline: args.deadline
389
+ };
390
+ return {
391
+ domain: {
392
+ name: BNB_DOMAIN_NAME,
393
+ version: BNB_DOMAIN_VERSION,
394
+ chainId: args.chainId
395
+ },
396
+ types: BNB_INTENT_TYPES,
397
+ primaryType: "PaymentIntent",
398
+ message: intent
399
+ };
400
+ }
401
+
402
+ // src/client/node/signer.ts
403
+ import { ethers } from "ethers";
404
+ import { Transaction as Transaction2 } from "@solana/web3.js";
405
+ var NodeSigner = class {
406
+ evmWallet;
407
+ getSolanaKeypair;
408
+ constructor(evmWallet, options = {}) {
409
+ this.evmWallet = evmWallet;
410
+ this.getSolanaKeypair = options.getSolanaKeypair ?? (() => null);
411
+ }
412
+ async getEvmAddress() {
413
+ return this.evmWallet.address;
414
+ }
415
+ async getSolanaAddress() {
416
+ const kp = this.getSolanaKeypair();
417
+ return kp ? kp.publicKey.toBase58() : null;
418
+ }
419
+ async signTypedData(envelope) {
420
+ const mutableTypes = {};
421
+ for (const [key, fields] of Object.entries(envelope.types)) {
422
+ mutableTypes[key] = [...fields];
423
+ }
424
+ return this.evmWallet.signTypedData(
425
+ envelope.domain,
426
+ mutableTypes,
427
+ envelope.message
428
+ );
429
+ }
430
+ async sendEvmTransaction(args) {
431
+ const chain = findChainByChainId(args.chainId);
432
+ if (!chain) {
433
+ throw new Error(`sendEvmTransaction: unknown chainId ${args.chainId}`);
434
+ }
435
+ const provider = new ethers.JsonRpcProvider(chain.rpc);
436
+ const connected = this.evmWallet.connect(provider);
437
+ const tx = await connected.sendTransaction({
438
+ to: args.to,
439
+ data: args.data,
440
+ value: args.value ? BigInt(args.value) : 0n
441
+ });
442
+ return tx.hash;
443
+ }
444
+ async signSolanaTransaction(args) {
445
+ const kp = this.getSolanaKeypair();
446
+ if (!kp) {
447
+ throw new Error("signSolanaTransaction: no Solana wallet configured");
448
+ }
449
+ const tx = Transaction2.from(Buffer.from(args.transactionBase64, "base64"));
450
+ if (args.partialSign) {
451
+ tx.partialSign(kp);
452
+ } else {
453
+ tx.sign(kp);
454
+ }
455
+ return tx.serialize({ requireAllSignatures: false }).toString("base64");
456
+ }
457
+ };
458
+ function findChainByChainId(chainId) {
459
+ for (const cfg of Object.values(CHAINS)) {
460
+ if (cfg.chainId === chainId) {
461
+ return cfg;
462
+ }
463
+ }
464
+ return void 0;
465
+ }
466
+
467
+ // src/client/node/index.ts
307
468
  var DEFAULT_CONFIG = {
308
469
  chain: "base",
309
470
  limits: {
@@ -316,6 +477,7 @@ var MoltsPayClient = class {
316
477
  config;
317
478
  walletData = null;
318
479
  wallet = null;
480
+ signer = null;
319
481
  todaySpending = 0;
320
482
  lastSpendingReset = 0;
321
483
  constructor(options = {}) {
@@ -324,7 +486,11 @@ var MoltsPayClient = class {
324
486
  this.walletData = this.loadWallet();
325
487
  this.loadSpending();
326
488
  if (this.walletData) {
327
- this.wallet = new Wallet(this.walletData.privateKey);
489
+ this.wallet = new Wallet2(this.walletData.privateKey);
490
+ const configDir = this.configDir;
491
+ this.signer = new NodeSigner(this.wallet, {
492
+ getSolanaKeypair: () => loadSolanaWallet(configDir)
493
+ });
328
494
  }
329
495
  }
330
496
  /**
@@ -452,20 +618,6 @@ var MoltsPayClient = class {
452
618
  } catch {
453
619
  throw new Error("Invalid x-payment-required header");
454
620
  }
455
- const networkToChainName = (network2) => {
456
- if (network2 === "solana:mainnet") return "solana";
457
- if (network2 === "solana:devnet") return "solana_devnet";
458
- const match = network2.match(/^eip155:(\d+)$/);
459
- if (!match) return null;
460
- const chainId = parseInt(match[1]);
461
- if (chainId === 8453) return "base";
462
- if (chainId === 137) return "polygon";
463
- if (chainId === 84532) return "base_sepolia";
464
- if (chainId === 42431) return "tempo_moderato";
465
- if (chainId === 56) return "bnb";
466
- if (chainId === 97) return "bnb_testnet";
467
- return null;
468
- };
469
621
  const serverChains = requirements.map((r) => networkToChainName(r.network)).filter((c) => c !== null);
470
622
  const userSpecifiedChain = options.chain;
471
623
  let selectedChain;
@@ -694,14 +846,14 @@ Please specify: --chain <chain_name>`
694
846
  async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
695
847
  const { to, amount, token, chainName, chain, spender } = paymentDetails;
696
848
  const tokenConfig = chain.tokens[token];
697
- const provider = new ethers.JsonRpcProvider(chain.rpc);
849
+ const provider = new ethers2.JsonRpcProvider(chain.rpc);
698
850
  const allowance = await this.checkAllowance(tokenConfig.address, spender, provider);
699
851
  const amountWeiCheck = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals));
700
852
  if (allowance < amountWeiCheck) {
701
853
  const nativeBalance = await provider.getBalance(this.wallet.address);
702
- const minGasBalance = ethers.parseEther("0.0005");
854
+ const minGasBalance = ethers2.parseEther("0.0005");
703
855
  if (nativeBalance < minGasBalance) {
704
- const nativeBNB = parseFloat(ethers.formatEther(nativeBalance)).toFixed(4);
856
+ const nativeBNB = parseFloat(ethers2.formatEther(nativeBalance)).toFixed(4);
705
857
  const isTestnet = chainName === "bnb_testnet";
706
858
  if (isTestnet) {
707
859
  throw new Error(
@@ -735,35 +887,21 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
735
887
  );
736
888
  }
737
889
  const amountWei = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
738
- const intent = {
890
+ const intentNonce = Date.now();
891
+ const intentDeadline = Date.now() + 36e5;
892
+ const envelope = buildBnbIntentTypedData({
739
893
  from: this.wallet.address,
740
894
  to,
741
895
  amount: amountWei,
742
- token: tokenConfig.address,
896
+ tokenAddress: tokenConfig.address,
743
897
  service,
744
- nonce: Date.now(),
745
- // Use timestamp as nonce for simplicity
746
- deadline: Date.now() + 36e5
747
- // 1 hour
748
- };
749
- const domain = {
750
- name: "MoltsPay",
751
- version: "1",
898
+ nonce: intentNonce,
899
+ deadline: intentDeadline,
752
900
  chainId: chain.chainId
753
- };
754
- const types = {
755
- PaymentIntent: [
756
- { name: "from", type: "address" },
757
- { name: "to", type: "address" },
758
- { name: "amount", type: "uint256" },
759
- { name: "token", type: "address" },
760
- { name: "service", type: "string" },
761
- { name: "nonce", type: "uint256" },
762
- { name: "deadline", type: "uint256" }
763
- ]
764
- };
901
+ });
765
902
  console.log(`[MoltsPay] Signing BNB payment intent...`);
766
- const signature = await this.wallet.signTypedData(domain, types, intent);
903
+ const signature = await this.signer.signTypedData(envelope);
904
+ const intent = envelope.message;
767
905
  const network = `eip155:${chain.chainId}`;
768
906
  const payload = {
769
907
  x402Version: 2,
@@ -837,12 +975,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
837
975
  feePayerPubkey
838
976
  // Optional fee payer for gasless mode
839
977
  );
840
- if (feePayerPubkey) {
841
- transaction.partialSign(solanaWallet);
842
- } else {
843
- transaction.sign(solanaWallet);
844
- }
845
- const signedTx = transaction.serialize({ requireAllSignatures: false }).toString("base64");
978
+ const unsignedBase64 = transaction.serialize({ requireAllSignatures: false, verifySignatures: false }).toString("base64");
979
+ const signedTx = await this.signer.signSolanaTransaction({
980
+ transactionBase64: unsignedBase64,
981
+ partialSign: !!feePayerPubkey
982
+ });
846
983
  console.log(`[MoltsPay] Transaction signed, sending to server...`);
847
984
  const network = chain === "solana" ? "solana:mainnet" : "solana:devnet";
848
985
  const payload = {
@@ -889,7 +1026,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
889
1026
  * Check ERC20 allowance for a spender
890
1027
  */
891
1028
  async checkAllowance(tokenAddress, spender, provider) {
892
- const contract = new ethers.Contract(
1029
+ const contract = new ethers2.Contract(
893
1030
  tokenAddress,
894
1031
  ["function allowance(address owner, address spender) view returns (uint256)"],
895
1032
  provider
@@ -900,41 +1037,29 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
900
1037
  * Sign EIP-3009 transferWithAuthorization (GASLESS)
901
1038
  * This only signs - no on-chain transaction, no gas needed.
902
1039
  * Supports both USDC and USDT.
1040
+ *
1041
+ * Delegates typed-data construction to `core/eip3009.ts` and the signature
1042
+ * itself to `this.signer`. That way Web Client (Phase 4) can reuse the same
1043
+ * flow with an EIP-1193 signer without duplicating typed-data layout.
903
1044
  */
904
1045
  async signEIP3009(to, amount, chain, token = "USDC", domainOverride) {
905
- const validAfter = 0;
906
- const validBefore = Math.floor(Date.now() / 1e3) + 3600;
907
- const nonce = ethers.hexlify(ethers.randomBytes(32));
908
1046
  const tokenConfig = chain.tokens[token];
909
1047
  const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
910
- const authorization = {
1048
+ const nonce = ethers2.hexlify(ethers2.randomBytes(32));
1049
+ const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
1050
+ const tokenVersion = domainOverride?.version || "2";
1051
+ const envelope = buildEIP3009TypedData({
911
1052
  from: this.wallet.address,
912
1053
  to,
913
1054
  value,
914
- validAfter: validAfter.toString(),
915
- validBefore: validBefore.toString(),
916
- nonce
917
- };
918
- const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
919
- const tokenVersion = domainOverride?.version || "2";
920
- const domain = {
921
- name: tokenName,
922
- version: tokenVersion,
1055
+ nonce,
923
1056
  chainId: chain.chainId,
924
- verifyingContract: tokenConfig.address
925
- };
926
- const types = {
927
- TransferWithAuthorization: [
928
- { name: "from", type: "address" },
929
- { name: "to", type: "address" },
930
- { name: "value", type: "uint256" },
931
- { name: "validAfter", type: "uint256" },
932
- { name: "validBefore", type: "uint256" },
933
- { name: "nonce", type: "bytes32" }
934
- ]
935
- };
936
- const signature = await this.wallet.signTypedData(domain, types, authorization);
937
- return { authorization, signature };
1057
+ tokenAddress: tokenConfig.address,
1058
+ tokenName,
1059
+ tokenVersion
1060
+ });
1061
+ const signature = await this.signer.signTypedData(envelope);
1062
+ return { authorization: envelope.message, signature };
938
1063
  }
939
1064
  /**
940
1065
  * Check spending limits
@@ -1038,7 +1163,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1038
1163
  */
1039
1164
  static init(configDir, options) {
1040
1165
  mkdirSync2(configDir, { recursive: true });
1041
- const wallet = Wallet.createRandom();
1166
+ const wallet = Wallet2.createRandom();
1042
1167
  const walletData = {
1043
1168
  address: wallet.address,
1044
1169
  privateKey: wallet.privateKey,
@@ -1070,17 +1195,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1070
1195
  } catch {
1071
1196
  throw new Error(`Unknown chain: ${this.config.chain}`);
1072
1197
  }
1073
- const provider = new ethers.JsonRpcProvider(chain.rpc);
1198
+ const provider = new ethers2.JsonRpcProvider(chain.rpc);
1074
1199
  const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
1075
1200
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
1076
1201
  provider.getBalance(this.wallet.address),
1077
- new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1078
- new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1202
+ new ethers2.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1203
+ new ethers2.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1079
1204
  ]);
1080
1205
  return {
1081
- usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1082
- usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1083
- native: parseFloat(ethers.formatEther(nativeBalance))
1206
+ usdc: parseFloat(ethers2.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1207
+ usdt: parseFloat(ethers2.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1208
+ native: parseFloat(ethers2.formatEther(nativeBalance))
1084
1209
  };
1085
1210
  }
1086
1211
  /**
@@ -1103,38 +1228,38 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1103
1228
  supportedChains.map(async (chainName) => {
1104
1229
  try {
1105
1230
  const chain = getChain(chainName);
1106
- const provider = new ethers.JsonRpcProvider(chain.rpc);
1231
+ const provider = new ethers2.JsonRpcProvider(chain.rpc);
1107
1232
  if (chainName === "tempo_moderato") {
1108
1233
  const [nativeBalance, pathUSD, alphaUSD, betaUSD, thetaUSD] = await Promise.all([
1109
1234
  provider.getBalance(this.wallet.address),
1110
- new ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1111
- new ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1112
- new ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1113
- new ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
1235
+ new ethers2.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1236
+ new ethers2.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1237
+ new ethers2.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
1238
+ new ethers2.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
1114
1239
  ]);
1115
1240
  results[chainName] = {
1116
- usdc: parseFloat(ethers.formatUnits(pathUSD, 6)),
1241
+ usdc: parseFloat(ethers2.formatUnits(pathUSD, 6)),
1117
1242
  // pathUSD as default USDC
1118
- usdt: parseFloat(ethers.formatUnits(alphaUSD, 6)),
1243
+ usdt: parseFloat(ethers2.formatUnits(alphaUSD, 6)),
1119
1244
  // alphaUSD as default USDT
1120
- native: parseFloat(ethers.formatEther(nativeBalance)),
1245
+ native: parseFloat(ethers2.formatEther(nativeBalance)),
1121
1246
  tempo: {
1122
- pathUSD: parseFloat(ethers.formatUnits(pathUSD, 6)),
1123
- alphaUSD: parseFloat(ethers.formatUnits(alphaUSD, 6)),
1124
- betaUSD: parseFloat(ethers.formatUnits(betaUSD, 6)),
1125
- thetaUSD: parseFloat(ethers.formatUnits(thetaUSD, 6))
1247
+ pathUSD: parseFloat(ethers2.formatUnits(pathUSD, 6)),
1248
+ alphaUSD: parseFloat(ethers2.formatUnits(alphaUSD, 6)),
1249
+ betaUSD: parseFloat(ethers2.formatUnits(betaUSD, 6)),
1250
+ thetaUSD: parseFloat(ethers2.formatUnits(thetaUSD, 6))
1126
1251
  }
1127
1252
  };
1128
1253
  } else {
1129
1254
  const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
1130
1255
  provider.getBalance(this.wallet.address),
1131
- new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1132
- new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1256
+ new ethers2.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
1257
+ new ethers2.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
1133
1258
  ]);
1134
1259
  results[chainName] = {
1135
- usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1136
- usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1137
- native: parseFloat(ethers.formatEther(nativeBalance))
1260
+ usdc: parseFloat(ethers2.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
1261
+ usdt: parseFloat(ethers2.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
1262
+ native: parseFloat(ethers2.formatEther(nativeBalance))
1138
1263
  };
1139
1264
  }
1140
1265
  } catch (err) {