four-flap-meme-sdk 4.3.21 → 4.4.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.
Files changed (82) hide show
  1. package/dist/bundle/four-meme/approve-tokenmanager.js +46 -16
  2. package/dist/bundle/four-meme/core/create-token-quote.d.ts +5 -1
  3. package/dist/bundle/four-meme/core/create-token-quote.js +29 -4
  4. package/dist/bundle/four-meme/core/sell-flow.js +3 -3
  5. package/dist/bundle/four-meme/pancake-proxy/approve.js +47 -10
  6. package/dist/bundle/four-meme/private/sell.js +2 -10
  7. package/dist/bundle/four-meme/swap/bundle.js +4 -4
  8. package/dist/bundle/four-meme/swap-buy-first/multi-wallet.js +25 -8
  9. package/dist/bundle/four-meme/swap-buy-first/single.js +30 -9
  10. package/dist/bundle/four-meme/swap-sign-helpers.js +56 -3
  11. package/dist/bundle/four-meme/utils-sweep/erc20-no-hop.js +18 -7
  12. package/dist/bundle/four-meme/utils-sweep/with-hops.js +10 -4
  13. package/dist/bundle/internal.d.ts +5 -0
  14. package/dist/bundle/internal.js +36 -0
  15. package/dist/chains/bsc/pancake/bundle-buy-first-quote.js +24 -8
  16. package/dist/chains/bsc/pancake/bundle-buy-first-types.d.ts +2 -0
  17. package/dist/chains/bsc/pancake/bundle-buy-first-validate.d.ts +1 -1
  18. package/dist/chains/bsc/pancake/bundle-buy-first-validate.js +2 -2
  19. package/dist/chains/bsc/pancake/bundle-buy-first.js +1 -0
  20. package/dist/chains/bsc/pancake/bundle-swap-types.d.ts +2 -0
  21. package/dist/chains/bsc/pancake/quote.d.ts +7 -0
  22. package/dist/chains/bsc/pancake/quote.js +9 -1
  23. package/dist/chains/bsc/pancake/swap-batch.js +15 -16
  24. package/dist/chains/bsc/pancake/swap-build.d.ts +1 -1
  25. package/dist/chains/bsc/pancake/swap-build.js +15 -9
  26. package/dist/chains/bsc/pancake/swap-quick.js +4 -4
  27. package/dist/chains/bsc/pancake/swap-single.js +1 -0
  28. package/dist/chains/bsc/platforms/iro/token.d.ts +1 -0
  29. package/dist/chains/bsc/platforms/iro/token.js +36 -1
  30. package/dist/chains/eni/batch-router/transfer-sweep.js +10 -14
  31. package/dist/chains/eni/platforms/daoaas/graduation.js +60 -14
  32. package/dist/chains/eni/platforms/daoaas/meta.d.ts +0 -4
  33. package/dist/chains/eni/platforms/daoaas/meta.js +112 -27
  34. package/dist/chains/eni/platforms/daoaas/portal-direct-submit.js +11 -6
  35. package/dist/chains/eni/platforms/daoaas/portal-direct-tx.js +4 -2
  36. package/dist/chains/eni/platforms/daoaas/portal.d.ts +5 -0
  37. package/dist/chains/eni/platforms/daoaas/portal.js +63 -9
  38. package/dist/chains/eni/platforms/dswap/liquidity.js +144 -49
  39. package/dist/chains/eni/platforms/fair-launch/launcher.js +2 -10
  40. package/dist/chains/eni/platforms/fair-launch/pool.d.ts +1 -0
  41. package/dist/chains/eni/platforms/fair-launch/pool.js +16 -2
  42. package/dist/chains/eni/platforms/iro/token.d.ts +1 -0
  43. package/dist/chains/eni/platforms/iro/token.js +36 -1
  44. package/dist/chains/robinhood/constants.d.ts +2 -5
  45. package/dist/chains/robinhood/constants.js +8 -7
  46. package/dist/chains/robinhood/longetf/launch.js +4 -4
  47. package/dist/chains/robinhood/pons-v2/launch.js +51 -20
  48. package/dist/chains/xlayer/eip7702/bundle-approve.js +8 -8
  49. package/dist/chains/xlayer/eip7702/bundle-sell.js +3 -4
  50. package/dist/chains/xlayer/eip7702/bundle-swap-batch.js +4 -2
  51. package/dist/chains/xlayer/eip7702/bundle-swap-multi.js +3 -3
  52. package/dist/chains/xlayer/eip7702/multi-hop-transfer-sweep.js +4 -10
  53. package/dist/chains/xlayer/eip7702/volume-bundle.js +45 -24
  54. package/dist/chains/xlayer/eoa/eoa-wash-volume.js +4 -14
  55. package/dist/core/constants/gas.d.ts +0 -8
  56. package/dist/core/constants/gas.js +0 -9
  57. package/dist/core/quote/quote-helpers.d.ts +11 -0
  58. package/dist/core/quote/quote-helpers.js +121 -20
  59. package/dist/core/quote/quote-helpers.test.js +11 -1
  60. package/dist/domains/dex/direct-router-v2-buy.js +4 -2
  61. package/dist/domains/dex/direct-router-v2-sell.js +63 -12
  62. package/dist/domains/dex/direct-router-v3.js +3 -4
  63. package/dist/domains/dex/quote.js +15 -5
  64. package/dist/domains/flap/portal-bundle-merkle/pancake-proxy/approve.js +48 -8
  65. package/dist/domains/flap/portal-bundle-merkle/swap-batch.js +8 -10
  66. package/dist/domains/flap/portal-bundle-merkle/swap-buy-first-quote.js +24 -9
  67. package/dist/domains/flap/portal-bundle-merkle/swap-planning.js +6 -2
  68. package/dist/domains/flap/portal-bundle-merkle/utils/sweep.js +14 -7
  69. package/dist/domains/flap/reader.d.ts +6 -0
  70. package/dist/domains/flap/reader.js +77 -1
  71. package/dist/domains/four/contracts/tm-bundle-batch.js +2 -10
  72. package/dist/domains/four/contracts/tm-bundle-create.js +5 -2
  73. package/dist/domains/four/contracts/tm.d.ts +2 -0
  74. package/dist/domains/four/contracts/tm.js +59 -2
  75. package/dist/domains/four/tax-token.js +60 -28
  76. package/dist/domains/lp-inspect/inspect-tax.js +98 -12
  77. package/dist/domains/lp-inspect/inspect.js +57 -44
  78. package/dist/domains/lp-inspect/registry.js +33 -7
  79. package/dist/domains/openfour/registry.js +15 -5
  80. package/dist/utils/airdrop-sweep.js +37 -17
  81. package/dist/utils/swap-helpers.js +35 -7
  82. package/package.json +1 -1
@@ -1,12 +1,51 @@
1
- import { ethers, Wallet } from 'ethers';
1
+ import { ethers, Interface, Wallet } from 'ethers';
2
2
  import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
3
- import { ADDRESSES, CHAINS } from '../../core/constants/index.js';
4
- import { ERC20_ABI } from '../../abis/common.js';
3
+ import { ADDRESSES, CHAINS, getMulticall3Address } from '../../core/constants/index.js';
4
+ import { ERC20_ABI, MULTICALL3_ABI } from '../../abis/common.js';
5
5
  import { getErrorMessageFromUnknown } from '../../core/index.js';
6
6
  import { getTxType, getGasPriceConfig } from '../config-helpers.js';
7
7
  import { createSignProvider } from '../../core/sign-context-helpers.js';
8
8
  import { batchCheckAllowances } from '../../core/erc20/index.js';
9
9
  const APPROVAL_THRESHOLD = ethers.MaxUint256 / 2n;
10
+ async function readApprovalViews(provider, chainId, tokenAddress, owners, spender, allMax) {
11
+ if (allMax) {
12
+ return {
13
+ decimals: 18,
14
+ allowances: await batchCheckAllowances(provider, tokenAddress, owners, spender),
15
+ };
16
+ }
17
+ const iface = new Interface(ERC20_ABI);
18
+ const multicall = new ethers.Contract(getMulticall3Address(chainId), MULTICALL3_ABI, provider);
19
+ const aggregate3 = multicall.aggregate3;
20
+ if (!aggregate3)
21
+ throw new Error('aggregate3 missing');
22
+ try {
23
+ const rows = (await aggregate3.staticCall([
24
+ { target: tokenAddress, allowFailure: false, callData: iface.encodeFunctionData('decimals', []) },
25
+ ...owners.map((owner) => ({
26
+ target: tokenAddress,
27
+ allowFailure: true,
28
+ callData: iface.encodeFunctionData('allowance', [owner, spender]),
29
+ })),
30
+ ]));
31
+ const decimals = Number(iface.decodeFunctionResult('decimals', rows[0].returnData)[0]);
32
+ const allowances = owners.map((_, index) => {
33
+ const row = rows[index + 1];
34
+ if (!row?.success || row.returnData === '0x')
35
+ return 0n;
36
+ try {
37
+ return iface.decodeFunctionResult('allowance', row.returnData)[0];
38
+ }
39
+ catch {
40
+ return 0n;
41
+ }
42
+ });
43
+ return { decimals, allowances };
44
+ }
45
+ catch (error) {
46
+ throw new Error(`查询代币 decimals 失败: ${getErrorMessageFromUnknown(error)}`, { cause: error });
47
+ }
48
+ }
10
49
  function resolveApprovalGasLimit(config) {
11
50
  if (config.gasLimit)
12
51
  return BigInt(config.gasLimit);
@@ -28,21 +67,12 @@ export async function approveFourTokenManagerBatch(params) {
28
67
  const provider = createSignProvider(config.rpcUrl, chainId);
29
68
  const wallets = privateKeys.map((k) => new Wallet(k, provider));
30
69
  const allMax = amounts.every((a) => a === 'max');
31
- const [gasPrice, decimals, allowances] = await Promise.all([
70
+ const owners = wallets.map((w) => w.address);
71
+ const [gasPrice, tokenViews] = await Promise.all([
32
72
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
33
- allMax
34
- ? Promise.resolve(18)
35
- : (async () => {
36
- try {
37
- const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
38
- return await tokenContract.decimals();
39
- }
40
- catch (error) {
41
- throw new Error(`查询代币 decimals 失败: ${getErrorMessageFromUnknown(error)}`, { cause: error });
42
- }
43
- })(),
44
- batchCheckAllowances(provider, tokenAddress, wallets.map((w) => w.address), tmAddr),
73
+ readApprovalViews(provider, chainId, tokenAddress, owners, tmAddr, allMax),
45
74
  ]);
75
+ const { decimals, allowances } = tokenViews;
46
76
  const amountsBigInt = amounts.map((a) => (a === 'max' ? ethers.MaxUint256 : ethers.parseUnits(a, decimals)));
47
77
  const needApproval = wallets.filter((_, i) => allowances[i] < APPROVAL_THRESHOLD);
48
78
  const needApprovalAmounts = amountsBigInt.filter((_, i) => allowances[i] < APPROVAL_THRESHOLD);
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Four.meme 发币:计价币、create value、ERC20 预购授权
3
3
  */
4
- import { ethers, Wallet } from 'ethers';
4
+ import { ethers, Wallet, type Provider } from 'ethers';
5
5
  import { FourClient, buildCreateTokenReq, type CreateTokenCustom } from '../../../core/clients/four.js';
6
6
  import { type FourRaisedToken } from '../../../domains/four/raised-token.js';
7
7
  export type FourLaunchQuoteInfo = {
@@ -11,6 +11,10 @@ export type FourLaunchQuoteInfo = {
11
11
  export declare function resolveCreateRaisedToken(fourClient: FourClient, tokenInfo: FourLaunchQuoteInfo): Promise<FourRaisedToken>;
12
12
  export declare function readLaunchFeeWei(tm: ethers.Contract): Promise<bigint>;
13
13
  export declare function fourCreateValueWei(raisedToken: FourRaisedToken, preSale: string, launchFeeWei: bigint): bigint;
14
+ export declare function readQuoteDecimalsAndAllowance(provider: Provider, tokenAddress: string, owner: string, spender: string): Promise<{
15
+ decimals: number;
16
+ allowance: bigint;
17
+ }>;
14
18
  export declare function signQuoteApproveIfNeeded(params: {
15
19
  raisedToken: FourRaisedToken;
16
20
  preSale: string;
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Four.meme 发币:计价币、create value、ERC20 预购授权
3
3
  */
4
- import { ethers } from 'ethers';
5
- import { ERC20_ABI } from '../../../abis/common.js';
4
+ import { ethers, Interface } from 'ethers';
5
+ import { ERC20_ABI, MULTICALL3_ABI } from '../../../abis/common.js';
6
+ import { CHAINS, getMulticall3Address } from '../../../core/constants/index.js';
6
7
  import { buildCreateTokenReq } from '../../../core/clients/four.js';
7
8
  import { computeFourCreateValueWei, isNativeRaisedToken, } from '../../../domains/four/raised-token.js';
8
9
  import { PLATFORM_CREATE_FEE } from './constants.js';
@@ -25,6 +26,28 @@ export async function readLaunchFeeWei(tm) {
25
26
  export function fourCreateValueWei(raisedToken, preSale, launchFeeWei) {
26
27
  return computeFourCreateValueWei({ raisedToken, preSale, launchFeeWei });
27
28
  }
29
+ export async function readQuoteDecimalsAndAllowance(provider, tokenAddress, owner, spender) {
30
+ const iface = new Interface([
31
+ 'function decimals() view returns (uint8)',
32
+ 'function allowance(address,address) view returns (uint256)',
33
+ ]);
34
+ const multicall = new ethers.Contract(getMulticall3Address(CHAINS.BSC.chainId), MULTICALL3_ABI, provider);
35
+ const aggregate3 = multicall.aggregate3;
36
+ if (!aggregate3)
37
+ throw new Error('aggregate3 missing');
38
+ const rows = (await aggregate3.staticCall([
39
+ { target: tokenAddress, allowFailure: false, callData: iface.encodeFunctionData('decimals', []) },
40
+ {
41
+ target: tokenAddress,
42
+ allowFailure: false,
43
+ callData: iface.encodeFunctionData('allowance', [owner, spender]),
44
+ },
45
+ ]));
46
+ return {
47
+ decimals: Number(iface.decodeFunctionResult('decimals', rows[0].returnData)[0]),
48
+ allowance: iface.decodeFunctionResult('allowance', rows[1].returnData)[0],
49
+ };
50
+ }
28
51
  export async function signQuoteApproveIfNeeded(params) {
29
52
  if (isNativeRaisedToken(params.raisedToken))
30
53
  return undefined;
@@ -32,9 +55,11 @@ export async function signQuoteApproveIfNeeded(params) {
32
55
  if (preSale === '0')
33
56
  return undefined;
34
57
  const token = new ethers.Contract(params.raisedToken.symbolAddress, ERC20_ABI, params.wallet);
35
- const decimals = Number(await token.decimals());
58
+ const provider = params.wallet.provider;
59
+ if (!provider)
60
+ throw new Error('wallet has no provider');
61
+ const { decimals, allowance } = await readQuoteDecimalsAndAllowance(provider, params.raisedToken.symbolAddress, params.wallet.address, params.spender);
36
62
  const amount = ethers.parseUnits(preSale, decimals);
37
- const allowance = await token.allowance(params.wallet.address, params.spender);
38
63
  if (allowance >= amount)
39
64
  return undefined;
40
65
  const unsigned = await token.approve.populateTransaction(params.spender, amount);
@@ -5,7 +5,7 @@ import { ethers, Contract, Interface } from 'ethers';
5
5
  import { getGasLimit } from '../../../core/gas/bundle-gas.js';
6
6
  import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
7
7
  import { ADDRESSES, CHAINS, applySlippageBps, reverseSlippageBps } from '../../../core/constants/index.js';
8
- import { MULTICALL3_ABI, ERC20_ABI } from '../../../abis/common.js';
8
+ import { MULTICALL3_ABI } from '../../../abis/common.js';
9
9
  import { getBundleErrorMessage } from '../../errors.js';
10
10
  import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient, getBribeAmount, } from '../../config-helpers.js';
11
11
  import { createSignProvider, signBribeTransaction } from '../../../core/sign-context-helpers.js';
@@ -14,6 +14,7 @@ import { createChainContext, createWallets } from '../core-helpers.js';
14
14
  import { TokenManagerHelper3 as Helper3Abi } from '../../../abis/contracts/index.js';
15
15
  import { TM2_ABI, MULTICALL3_ADDRESS } from './constants.js';
16
16
  import { prefixSumAmounts, sequentialFundsFromPrefixQuotes } from '../sequential-sell-quote.js';
17
+ import { batchGetBalances } from '../../internal.js';
17
18
  export async function batchSellWithBundleMerkle(params) {
18
19
  const { privateKeys, sellAmounts, tokenAddress, minOutputAmounts, config } = params;
19
20
  if (privateKeys.length === 0 || sellAmounts.length !== privateKeys.length) {
@@ -178,8 +179,7 @@ async function resolveSellOutputs(params) {
178
179
  }
179
180
  async function ensureSellerBalances(params) {
180
181
  const { provider, tokenAddress, sellers, amountsWei } = params;
181
- const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
182
- const balances = await Promise.all(sellers.map((wallet) => tokenContract.balanceOf(wallet.address)));
182
+ const balances = await batchGetBalances(provider, sellers.map((wallet) => wallet.address), tokenAddress, CHAINS.BSC.chainId);
183
183
  for (let i = 0; i < sellers.length; i++) {
184
184
  if (balances[i] < amountsWei[i]) {
185
185
  throw new Error(`钱包 ${i} 代币余额不足:需要 ${ethers.formatUnits(amountsWei[i], 18)},实际 ${ethers.formatUnits(balances[i], 18)}`);
@@ -1,15 +1,38 @@
1
1
  /**
2
2
  * PancakeSwap 官方 Router 交易模块 — 授权
3
3
  */
4
- import { ethers, Wallet, Contract } from 'ethers';
4
+ import { ethers, Wallet, Contract, Interface } from 'ethers';
5
5
  import { getGasLimit } from '../../../core/gas/bundle-gas.js';
6
6
  import { createSignProvider } from '../../../core/sign-context-helpers.js';
7
7
  import { NonceManager, getOptimizedGasPrice } from '../../../utils/bundle-helpers.js';
8
- import { CHAINS } from '../../../core/constants/index.js';
9
- import { ERC20_ABI } from '../../../abis/common.js';
8
+ import { CHAINS, getMulticall3Address } from '../../../core/constants/index.js';
9
+ import { ERC20_ABI, MULTICALL3_ABI } from '../../../abis/common.js';
10
10
  import { getErrorMessageFromUnknown } from '../../../core/index.js';
11
11
  import { getTxType, getGasPriceConfig } from '../../config-helpers.js';
12
12
  import { getTokenDecimals, getApprovalTarget } from './helpers.js';
13
+ import { batchCheckAllowances } from '../../../core/erc20/allowances.js';
14
+ async function readDecimalsAndAllowances(provider, tokenAddress, owners, spender, needDecimals) {
15
+ const iface = new Interface(ERC20_ABI);
16
+ const multicall = new Contract(getMulticall3Address(CHAINS.BSC.chainId), MULTICALL3_ABI, provider);
17
+ const aggregate3 = multicall.aggregate3;
18
+ if (!aggregate3)
19
+ throw new Error('aggregate3 missing');
20
+ const rows = (await aggregate3.staticCall([
21
+ ...(needDecimals
22
+ ? [{ target: tokenAddress, allowFailure: false, callData: iface.encodeFunctionData('decimals', []) }]
23
+ : []),
24
+ ...owners.map((owner) => ({
25
+ target: tokenAddress,
26
+ allowFailure: false,
27
+ callData: iface.encodeFunctionData('allowance', [owner, spender]),
28
+ })),
29
+ ]));
30
+ const offset = needDecimals ? 1 : 0;
31
+ return {
32
+ decimals: needDecimals ? Number(iface.decodeFunctionResult('decimals', rows[0].returnData)[0]) : 18,
33
+ allowances: owners.map((_, index) => iface.decodeFunctionResult('allowance', rows[index + offset].returnData)[0]),
34
+ };
35
+ }
13
36
  /**
14
37
  * ✅ 授权代币给 PancakeSwap Router(根据 routeType 选择 V2/V3)
15
38
  */
@@ -20,8 +43,18 @@ export async function approveFourPancakeProxy(params) {
20
43
  const provider = createSignProvider(rpcUrl, 56, 'BSC');
21
44
  const wallet = new Wallet(privateKey, provider);
22
45
  const token = new Contract(tokenAddress, ERC20_ABI, wallet);
23
- const decimals = await getTokenDecimals(tokenAddress, provider);
24
- const currentAllowance = await token.allowance(wallet.address, approvalTarget);
46
+ const needDecimals = amount !== 'max';
47
+ let decimals;
48
+ let currentAllowance;
49
+ try {
50
+ const views = await readDecimalsAndAllowances(provider, tokenAddress, [wallet.address], approvalTarget, needDecimals);
51
+ decimals = views.decimals;
52
+ currentAllowance = views.allowances[0];
53
+ }
54
+ catch {
55
+ decimals = needDecimals ? await getTokenDecimals(tokenAddress, provider) : 18;
56
+ currentAllowance = await token.allowance(wallet.address, approvalTarget);
57
+ }
25
58
  const amountBigInt = amount === 'max' ? ethers.MaxUint256 : ethers.parseUnits(amount, decimals);
26
59
  if (currentAllowance >= amountBigInt) {
27
60
  return { txHash: '', approved: true };
@@ -44,13 +77,17 @@ export async function approveFourPancakeProxyBatch(params) {
44
77
  // 根据 routeType 选择授权目标
45
78
  const approvalTarget = getApprovalTarget(routeType || 'v2');
46
79
  const provider = createSignProvider(config.rpcUrl, 56, 'BSC');
47
- const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
48
- const decimals = await getTokenDecimals(tokenAddress, provider);
49
80
  const wallets = privateKeys.map((k) => new Wallet(k, provider));
81
+ const needDecimals = amounts.some((amount) => amount !== 'max');
82
+ const [gasPrice, views] = await Promise.all([
83
+ getOptimizedGasPrice(provider, getGasPriceConfig(config)),
84
+ readDecimalsAndAllowances(provider, tokenAddress, wallets.map((wallet) => wallet.address), approvalTarget, needDecimals).catch(async () => ({
85
+ decimals: needDecimals ? await getTokenDecimals(tokenAddress, provider) : 18,
86
+ allowances: await batchCheckAllowances(provider, tokenAddress, wallets.map((wallet) => wallet.address), approvalTarget),
87
+ })),
88
+ ]);
89
+ const { decimals, allowances } = views;
50
90
  const amountsBigInt = amounts.map((a) => (a === 'max' ? ethers.MaxUint256 : ethers.parseUnits(a, decimals)));
51
- // 检查现有授权
52
- const tokens = wallets.map((w) => new Contract(tokenAddress, ERC20_ABI, w));
53
- const allowances = await Promise.all(tokens.map((token, i) => token.allowance(wallets[i].address, approvalTarget)));
54
91
  // 只授权不足的
55
92
  const needApproval = wallets.filter((_, i) => allowances[i] < amountsBigInt[i]);
56
93
  const needApprovalAmounts = amountsBigInt.filter((amount, i) => allowances[i] < amount);
@@ -8,7 +8,7 @@ import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA, CHAINS, applySlippageBps, reverseSli
8
8
  import { prefixSumAmounts, sequentialFundsFromPrefixQuotes } from '../sequential-sell-quote.js';
9
9
  import { TM2_ABI } from '../../../abis/common.js';
10
10
  import { getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient, getBribeAmount, } from '../../config-helpers.js';
11
- import { trySell } from '../../../domains/four/contracts/tm.js';
11
+ import { trySellFundsBatch } from '../../../domains/four/contracts/tm.js';
12
12
  import { createSignProvider } from '../../../core/sign-context-helpers.js';
13
13
  const CHAIN_ID = CHAINS.BSC.chainId;
14
14
  /**
@@ -124,15 +124,7 @@ export async function fourBatchPrivateSellMerkle(params) {
124
124
  })())
125
125
  : (async () => {
126
126
  const prefixes = prefixSumAmounts(amountsWei);
127
- const prefixFunds = await Promise.all(prefixes.map(async (amount) => {
128
- try {
129
- const result = await trySell('BSC', rpcUrl, tokenAddress, amount);
130
- return result.funds;
131
- }
132
- catch {
133
- return 0n;
134
- }
135
- }));
127
+ const prefixFunds = await trySellFundsBatch('BSC', rpcUrl, tokenAddress, prefixes);
136
128
  return sequentialFundsFromPrefixQuotes(prefixFunds);
137
129
  })(),
138
130
  ]);
@@ -2,7 +2,7 @@
2
2
  * Four.meme 内盘捆绑换手(Merkle Bundle)— 单卖单买流程
3
3
  */
4
4
  import { ethers, Contract, Wallet } from 'ethers';
5
- import { countSellerNonceSlots, createFourSwapGasContext, fetchFourSwapSellQuoteAndAllowance, grossUpFourSellProceedsToBuySide, needsTmTokenApproval, planSellerNonces, signTmMaxApprovalTx, } from '../swap-sign-helpers.js';
5
+ import { countSellerNonceSlots, createFourSwapGasContext, fetchFourSwapSellQuoteAllowanceAndBalances, grossUpFourSellProceedsToBuySide, needsTmTokenApproval, planSellerNonces, signTmMaxApprovalTx, } from '../swap-sign-helpers.js';
6
6
  import { signBribeTransaction } from '../../../core/sign-context-helpers.js';
7
7
  import { calculateSellAmount } from '../../../utils/swap-helpers.js';
8
8
  import { NonceManager, getOptimizedGasPrice, getGasPriceConfig, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
@@ -25,10 +25,10 @@ export async function fourBundleSwapMerkle(params) {
25
25
  getOptimizedGasPrice(provider, getGasPriceConfig(bundleConfig)),
26
26
  ]);
27
27
  const { amount: sellAmountWei, decimals } = sellAmountResult;
28
- const [{ sellFunds: sellerWillGetBNB, allowance: currentAllowance }, buyerBalance] = await Promise.all([
29
- fetchFourSwapSellQuoteAndAllowance(provider, tokenAddress, seller.address, sellAmountWei),
30
- provider.getBalance(buyer.address),
28
+ const { sellFunds: sellerWillGetBNB, allowance: currentAllowance, buyerBalances, } = await fetchFourSwapSellQuoteAllowanceAndBalances(provider, tokenAddress, seller.address, sellAmountWei, [
29
+ buyer.address,
31
30
  ]);
31
+ const buyerBalance = buyerBalances[0] ?? 0n;
32
32
  const buyerFunds = sellerWillGetBNB;
33
33
  // 验证买方余额
34
34
  const reserveGas = ethers.parseEther((config.reserveGasBNB || 0.0005).toString());
@@ -2,11 +2,12 @@
2
2
  * Four.meme 内盘捆绑换手(Merkle Bundle)- 先买后卖 — 多钱包模式
3
3
  */
4
4
  import { getGasLimit } from '../../../core/gas/bundle-gas.js';
5
- import { ethers, Contract, Wallet } from 'ethers';
5
+ import { ethers, Contract, Interface, Wallet } from 'ethers';
6
+ import { MULTICALL3_ABI } from '../../../abis/common.js';
6
7
  import { createSignProvider } from '../../../core/sign-context-helpers.js';
7
8
  import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
8
- import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA, CHAINS, calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
9
- import { TM_ABI, HELPER3_ABI, TM_ADDRESS } from '../swap-internal.js';
9
+ import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA, CHAINS, calculateBundleSwapRoundTripProfit, getMulticall3Address, } from '../../../core/constants/index.js';
10
+ import { TM_ABI, TM_ADDRESS } from '../swap-internal.js';
10
11
  import { getTxType, getGasPriceConfig, getProfitRecipient, getBribeAmount } from '../../config-helpers.js';
11
12
  import { trySell } from '../../../domains/four/contracts/tm.js';
12
13
  import { splitAmountByWeight as splitAmount } from '../../../core/amount/split-by-weight.js';
@@ -49,13 +50,29 @@ export async function fourBundleBuyFirstMultiWallet(params) {
49
50
  const bribeAmount = getBribeAmount(config);
50
51
  const needBribeTx = bribeAmount > 0n;
51
52
  // ✅ 第一批并行:报价 + Gas价格 + 代币精度
52
- const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
53
- const erc20 = new Contract(tokenAddress, ['function decimals() view returns (uint8)'], provider);
54
- const [buyQuote, gasPrice, decimals] = await Promise.all([
55
- helper3.tryBuy(tokenAddress, 0n, totalFundsWei),
53
+ const tryBuyIface = new Interface([
54
+ 'function tryBuy(address token, uint256 amount, uint256 funds) view returns (address tokenManager, address quote, uint256 estimatedAmount, uint256 estimatedCost, uint256 estimatedFee, uint256 amountMsgValue, uint256 amountApproval, uint256 amountFunds)',
55
+ ]);
56
+ const ercIface = new Interface(['function decimals() view returns (uint8)']);
57
+ const [quoteRows, gasPrice] = await Promise.all([
58
+ (async () => {
59
+ const multicall = new Contract(getMulticall3Address(CHAINS.BSC.chainId), MULTICALL3_ABI, provider);
60
+ const aggregate3 = multicall.aggregate3;
61
+ if (!aggregate3)
62
+ throw new Error('aggregate3 missing');
63
+ return (await aggregate3.staticCall([
64
+ {
65
+ target: ADDRESSES.BSC.TokenManagerHelper3,
66
+ allowFailure: false,
67
+ callData: tryBuyIface.encodeFunctionData('tryBuy', [tokenAddress, 0n, totalFundsWei]),
68
+ },
69
+ { target: tokenAddress, allowFailure: false, callData: ercIface.encodeFunctionData('decimals', []) },
70
+ ]));
71
+ })(),
56
72
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
57
- erc20.decimals(),
58
73
  ]);
74
+ const buyQuote = tryBuyIface.decodeFunctionResult('tryBuy', quoteRows[0].returnData);
75
+ const decimals = Number(ercIface.decodeFunctionResult('decimals', quoteRows[1].returnData)[0]);
59
76
  const estimatedTokenAmount = buyQuote.estimatedAmount ?? buyQuote[2];
60
77
  if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
61
78
  throw new Error('报价失败:无法估算可买入的代币数量');
@@ -4,11 +4,12 @@
4
4
  * 功能:钱包B先买入代币 → 钱包A卖出相同数量 → 原子执行
5
5
  */
6
6
  import { getGasLimit } from '../../../core/gas/bundle-gas.js';
7
- import { ethers, Contract, Wallet } from 'ethers';
7
+ import { ethers, Contract, Interface, Wallet } from 'ethers';
8
+ import { MULTICALL3_ABI } from '../../../abis/common.js';
8
9
  import { createSignProvider } from '../../../core/sign-context-helpers.js';
9
10
  import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
10
- import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA, CHAINS, calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
11
- import { TM_ABI, HELPER3_ABI, TM_ADDRESS } from '../swap-internal.js';
11
+ import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA, CHAINS, calculateBundleSwapRoundTripProfit, getMulticall3Address, } from '../../../core/constants/index.js';
12
+ import { TM_ABI, TM_ADDRESS } from '../swap-internal.js';
12
13
  import { getTxType, getGasPriceConfig, getProfitRecipient, getBribeAmount } from '../../config-helpers.js';
13
14
  import { trySell } from '../../../domains/four/contracts/tm.js';
14
15
  import { splitAmountByWeight as splitAmount } from '../../../core/amount/split-by-weight.js';
@@ -66,13 +67,33 @@ export async function fourBundleBuyFirstMerkle(params) {
66
67
  throw new Error('buyerFunds 需要大于 0');
67
68
  }
68
69
  // ✅ 优化:第二批并行 - buyQuote、sellerTokenBal、decimals
69
- const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
70
- const erc20 = new Contract(tokenAddress, ['function balanceOf(address) view returns (uint256)', 'function decimals() view returns (uint8)'], provider);
71
- const [buyQuote, sellerTokenBal, decimals] = await Promise.all([
72
- helper3.tryBuy(tokenAddress, 0n, buyerFundsWei),
73
- erc20.balanceOf(seller.address),
74
- erc20.decimals(),
70
+ const tryBuyIface = new Interface([
71
+ 'function tryBuy(address token, uint256 amount, uint256 funds) view returns (address tokenManager, address quote, uint256 estimatedAmount, uint256 estimatedCost, uint256 estimatedFee, uint256 amountMsgValue, uint256 amountApproval, uint256 amountFunds)',
75
72
  ]);
73
+ const ercIface = new Interface([
74
+ 'function balanceOf(address) view returns (uint256)',
75
+ 'function decimals() view returns (uint8)',
76
+ ]);
77
+ const multicall = new Contract(getMulticall3Address(CHAINS.BSC.chainId), MULTICALL3_ABI, provider);
78
+ const aggregate3 = multicall.aggregate3;
79
+ if (!aggregate3)
80
+ throw new Error('aggregate3 missing');
81
+ const quoteRows = (await aggregate3.staticCall([
82
+ {
83
+ target: ADDRESSES.BSC.TokenManagerHelper3,
84
+ allowFailure: false,
85
+ callData: tryBuyIface.encodeFunctionData('tryBuy', [tokenAddress, 0n, buyerFundsWei]),
86
+ },
87
+ {
88
+ target: tokenAddress,
89
+ allowFailure: false,
90
+ callData: ercIface.encodeFunctionData('balanceOf', [seller.address]),
91
+ },
92
+ { target: tokenAddress, allowFailure: false, callData: ercIface.encodeFunctionData('decimals', []) },
93
+ ]));
94
+ const buyQuote = tryBuyIface.decodeFunctionResult('tryBuy', quoteRows[0].returnData);
95
+ const sellerTokenBal = ercIface.decodeFunctionResult('balanceOf', quoteRows[1].returnData)[0];
96
+ const decimals = Number(ercIface.decodeFunctionResult('decimals', quoteRows[2].returnData)[0]);
76
97
  const estimatedTokenAmount = buyQuote.estimatedAmount ?? buyQuote[2];
77
98
  if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
78
99
  throw new Error('报价失败:无法估算可买入的代币数量');
@@ -1,12 +1,59 @@
1
1
  import { ethers, Contract } from 'ethers';
2
2
  import { getGasLimit } from '../../utils/bundle-helpers.js';
3
- import { ADDRESSES, CHAINS } from '../../core/constants/index.js';
3
+ import { ADDRESSES, CHAINS, getMulticall3Address } from '../../core/constants/index.js';
4
+ import { MULTICALL3_ABI } from '../../abis/common.js';
5
+ import { batchGetBalances } from '../internal.js';
4
6
  import { getProfitRateBps as getSharedProfitRateBps } from '../../core/constants/index.js';
5
7
  import { getTxType } from '../config-helpers.js';
6
8
  import { createSignProvider } from '../../core/sign-context-helpers.js';
7
9
  import { HELPER3_ABI, TM_ADDRESS } from './swap-internal.js';
8
10
  const APPROVE_INTERFACE = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
9
11
  const ERC20_ALLOWANCE_ABI = ['function allowance(address,address) view returns (uint256)'];
12
+ const TRY_SELL_IFACE = new ethers.Interface([
13
+ 'function trySell(address token, uint256 amount) view returns (address tokenManager, address quote, uint256 funds, uint256 fee)',
14
+ ]);
15
+ const BALANCE_IFACE = new ethers.Interface(['function getEthBalance(address) view returns (uint256)']);
16
+ async function readSellQuoteAllowanceBalances(provider, tokenAddress, sellerAddress, sellAmountWei, buyerAddresses) {
17
+ const multicallAddress = getMulticall3Address(CHAINS.BSC.chainId);
18
+ const multicall = new Contract(multicallAddress, MULTICALL3_ABI, provider);
19
+ const aggregate3 = multicall.aggregate3;
20
+ if (!aggregate3)
21
+ return null;
22
+ const calls = [
23
+ {
24
+ target: ADDRESSES.BSC.TokenManagerHelper3,
25
+ allowFailure: false,
26
+ callData: TRY_SELL_IFACE.encodeFunctionData('trySell', [tokenAddress, sellAmountWei]),
27
+ },
28
+ {
29
+ target: tokenAddress,
30
+ allowFailure: false,
31
+ callData: new ethers.Interface(ERC20_ALLOWANCE_ABI).encodeFunctionData('allowance', [sellerAddress, TM_ADDRESS]),
32
+ },
33
+ ...buyerAddresses.map((address) => ({
34
+ target: multicallAddress,
35
+ allowFailure: true,
36
+ callData: BALANCE_IFACE.encodeFunctionData('getEthBalance', [address]),
37
+ })),
38
+ ];
39
+ const rows = (await aggregate3.staticCall(calls));
40
+ if (!rows[0]?.success || !rows[1]?.success)
41
+ return null;
42
+ const sellFunds = TRY_SELL_IFACE.decodeFunctionResult('trySell', rows[0].returnData).funds;
43
+ const allowance = new ethers.Interface(ERC20_ALLOWANCE_ABI).decodeFunctionResult('allowance', rows[1].returnData)[0];
44
+ const buyerBalances = buyerAddresses.map((_, index) => {
45
+ const row = rows[index + 2];
46
+ if (!row?.success || row.returnData === '0x')
47
+ return 0n;
48
+ try {
49
+ return BALANCE_IFACE.decodeFunctionResult('getEthBalance', row.returnData)[0];
50
+ }
51
+ catch {
52
+ return 0n;
53
+ }
54
+ });
55
+ return { sellFunds, allowance, buyerBalances };
56
+ }
10
57
  export function getFourSwapProfitRateBps(_userType) {
11
58
  return getSharedProfitRateBps('bundleSwap');
12
59
  }
@@ -71,6 +118,9 @@ export async function grossUpFourSellProceedsToBuySide(provider, tokenAddress, s
71
118
  }
72
119
  }
73
120
  export async function fetchFourSwapSellQuoteAndAllowance(provider, tokenAddress, sellerAddress, sellAmountWei) {
121
+ const batched = await readSellQuoteAllowanceBalances(provider, tokenAddress, sellerAddress, sellAmountWei, []).catch(() => null);
122
+ if (batched)
123
+ return { sellFunds: batched.sellFunds, allowance: batched.allowance };
74
124
  const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
75
125
  const erc20 = new Contract(tokenAddress, ERC20_ALLOWANCE_ABI, provider);
76
126
  const [sellQuote, allowance] = await Promise.all([
@@ -80,12 +130,15 @@ export async function fetchFourSwapSellQuoteAndAllowance(provider, tokenAddress,
80
130
  return { sellFunds: sellQuote.funds, allowance };
81
131
  }
82
132
  export async function fetchFourSwapSellQuoteAllowanceAndBalances(provider, tokenAddress, sellerAddress, sellAmountWei, buyerAddresses) {
133
+ const batched = await readSellQuoteAllowanceBalances(provider, tokenAddress, sellerAddress, sellAmountWei, buyerAddresses).catch(() => null);
134
+ if (batched)
135
+ return batched;
83
136
  const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
84
137
  const erc20 = new Contract(tokenAddress, ERC20_ALLOWANCE_ABI, provider);
85
- const [sellQuote, allowance, ...buyerBalances] = await Promise.all([
138
+ const [sellQuote, allowance, buyerBalances] = await Promise.all([
86
139
  helper3.trySell(tokenAddress, sellAmountWei),
87
140
  erc20.allowance(sellerAddress, TM_ADDRESS),
88
- ...buyerAddresses.map((addr) => provider.getBalance(addr)),
141
+ batchGetBalances(provider, buyerAddresses, undefined, CHAINS.BSC.chainId),
89
142
  ]);
90
143
  return {
91
144
  sellFunds: sellQuote.funds,
@@ -5,7 +5,7 @@ import { ethers, Wallet } from 'ethers';
5
5
  import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
6
6
  import { getGasPriceConfig } from '../../config-helpers.js';
7
7
  import { calculateTransferFee, floorErc20TransferGasLimit } from '../../../core/constants/index.js';
8
- import { batchGetBalances as _batchGetBalances, getErc20Decimals as _getErc20Decimals } from '../../internal.js';
8
+ import { batchGetBalances as _batchGetBalances, batchGetTokenAndNativeBalances, getErc20Decimals as _getErc20Decimals, } from '../../internal.js';
9
9
  import { clampRatio } from './context.js';
10
10
  export async function sweepErc20NoHop(ctx) {
11
11
  const { provider, chainIdNum, txType, config, target, targetWallet, tokenAddress, tokenDecimals, skipIfInsufficient, extractProfit, profitAddr, actualKeys, sources, ratios, amounts, amount, finalGasLimit, ratio, } = ctx;
@@ -16,13 +16,26 @@ export async function sweepErc20NoHop(ctx) {
16
16
  const wallets = actualKeys.map((pk) => new Wallet(pk, provider));
17
17
  const addresses = wallets.map((w) => w.address);
18
18
  const nonceManager = ctx.nonceManager ?? new NonceManager(provider);
19
+ const targetIndexInSources = addresses.findIndex((addr) => addr.toLowerCase() === target.toLowerCase());
20
+ const balanceAddresses = config.checkBnbForErc20NoHop && targetWallet && targetIndexInSources < 0 ? [...addresses, target] : addresses;
19
21
  // ✅ ERC20:并行获取 gasPrice、decimals、余额(传入 chainIdNum 避免 NETWORK_ERROR)
20
- const [gasPrice, decimals, balances, bnbBalances] = await Promise.all([
22
+ const [gasPrice, decimals, paired] = await Promise.all([
21
23
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
22
24
  Promise.resolve(tokenDecimals ?? (await _getErc20Decimals(provider, tokenAddress, chainIdNum))),
23
- _batchGetBalances(provider, addresses, tokenAddress),
24
- config.checkBnbForErc20NoHop ? _batchGetBalances(provider, addresses) : Promise.resolve([]),
25
+ config.checkBnbForErc20NoHop
26
+ ? batchGetTokenAndNativeBalances(provider, balanceAddresses, tokenAddress, chainIdNum)
27
+ : _batchGetBalances(provider, addresses, tokenAddress, chainIdNum).then((token) => ({
28
+ token,
29
+ native: [],
30
+ })),
25
31
  ]);
32
+ const balances = paired.token.slice(0, addresses.length);
33
+ const bnbBalances = paired.native.slice(0, addresses.length);
34
+ const targetNativeBalance = !config.checkBnbForErc20NoHop || !targetWallet
35
+ ? 0n
36
+ : targetIndexInSources >= 0
37
+ ? (paired.native[targetIndexInSources] ?? 0n)
38
+ : (paired.native[addresses.length] ?? 0n);
26
39
  const iface = new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
27
40
  // ✅ 修复:利润多跳需要 PROFIT_HOP_COUNT + 1 笔交易的 gas
28
41
  // ✅ 统一使用 21055n 作为利润多跳的 gas limit
@@ -92,9 +105,7 @@ export async function sweepErc20NoHop(ctx) {
92
105
  : null;
93
106
  // ✅ 如果需要提取费用,检查支付者是否有足够原生代币支付
94
107
  if (extractProfit && nativeProfitAmount > 0n && profitPayerWalletErc20 && config.checkBnbForErc20NoHop) {
95
- const payerBnbBalance = useTargetAsPayerErc20
96
- ? await provider.getBalance(target)
97
- : (bnbBalances[maxSweepIndex] ?? 0n);
108
+ const payerBnbBalance = useTargetAsPayerErc20 ? targetNativeBalance : (bnbBalances[maxSweepIndex] ?? 0n);
98
109
  const payerNeedGas = (useTargetAsPayerErc20 ? 0n : finalGasLimit * gasPrice) + profitTxGas + nativeProfitAmount;
99
110
  // 如果支付者原生代币余额不足,跳过费用提取
100
111
  if (payerBnbBalance < payerNeedGas) {
@@ -5,7 +5,7 @@ import { ethers, Wallet } from 'ethers';
5
5
  import { NonceManager, getOptimizedGasPrice, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
6
6
  import { getGasPriceConfig } from '../../config-helpers.js';
7
7
  import { calculateTransferFee, floorErc20TransferGasLimit, GAS_LIMITS } from '../../../core/constants/index.js';
8
- import { batchGetBalances as _batchGetBalances, getErc20Decimals as _getErc20Decimals } from '../../internal.js';
8
+ import { batchGetBalances as _batchGetBalances, batchGetTokenAndNativeBalances, getErc20Decimals as _getErc20Decimals, } from '../../internal.js';
9
9
  import { clampRatio } from './context.js';
10
10
  export async function sweepWithHops(ctx, preparedHops) {
11
11
  const { provider, chainIdNum, txType, config, target, targetWallet, tokenAddress, tokenDecimals, skipIfInsufficient, isNative, extractProfit, profitAddr, actualKeys, sources, ratios, amounts, amount, finalGasLimit, nativeGasLimit, ratio, } = ctx;
@@ -27,14 +27,20 @@ export async function sweepWithHops(ctx, preparedHops) {
27
27
  }
28
28
  const sourceAddresses = sourceWallets.map((w) => w.address);
29
29
  // ✅ 优化:并行获取 gasPrice、decimals、余额
30
- const [gasPrice, decimals, balances, bnbBalances] = await Promise.all([
30
+ const [gasPrice, decimals, paired] = await Promise.all([
31
31
  getOptimizedGasPrice(provider, getGasPriceConfig(config)),
32
32
  isNative
33
33
  ? Promise.resolve(18)
34
34
  : Promise.resolve(tokenDecimals ?? (await _getErc20Decimals(provider, tokenAddress, chainIdNum))),
35
- _batchGetBalances(provider, sourceAddresses, tokenAddress),
36
- isNative ? Promise.resolve([]) : _batchGetBalances(provider, sourceAddresses),
35
+ isNative || !tokenAddress
36
+ ? _batchGetBalances(provider, sourceAddresses, tokenAddress, chainIdNum).then((token) => ({
37
+ token,
38
+ native: [],
39
+ }))
40
+ : batchGetTokenAndNativeBalances(provider, sourceAddresses, tokenAddress, chainIdNum),
37
41
  ]);
42
+ const balances = paired.token;
43
+ const bnbBalances = paired.native;
38
44
  const iface = isNative ? null : new ethers.Interface(['function transfer(address,uint256) returns (bool)']);
39
45
  // ✅ Gas limit 设置(与分散函数保持一致)
40
46
  const nativeTransferGasLimit = GAS_LIMITS.BRIBE;
@@ -9,6 +9,11 @@ export declare function normalizeAmounts(recipients: string[], amount?: AmountLi
9
9
  * 批量获取余额(原生代币或 ERC20,优先 Multicall3)
10
10
  */
11
11
  export declare function batchGetBalances(provider: JsonRpcProvider, addresses: string[], tokenAddress?: string, chainId?: number): Promise<bigint[]>;
12
+ /** 同一批地址的 ERC20 余额和原生币余额,一次 aggregate3。 */
13
+ export declare function batchGetTokenAndNativeBalances(provider: JsonRpcProvider, addresses: string[], tokenAddress: string, chainId?: number): Promise<{
14
+ token: bigint[];
15
+ native: bigint[];
16
+ }>;
12
17
  export declare function estimateErc20TransferGas(provider: JsonRpcProvider, tokenAddress: string, from: string, to: string, amount: bigint, bufferPercent?: number): Promise<bigint>;
13
18
  export declare function estimateMaxErc20TransferGas(provider: JsonRpcProvider, tokenAddress: string, from: string, recipients: string[], amounts: bigint[], bufferPercent?: number): Promise<bigint>;
14
19
  export declare function calculateGasLimit(config: BundleGasConfig, isNative: boolean, _hasHops: boolean, _hopCount?: number): bigint;