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
@@ -7,6 +7,8 @@
7
7
  * - 构建跟买交易 (claimForSubmit / batchClaimForSubmit)
8
8
  */
9
9
  import { Contract, Interface, Wallet, JsonRpcProvider } from 'ethers';
10
+ import { MULTICALL3_ABI } from '../../../../abis/common.js';
11
+ import { getMulticall3Address } from '../../../../core/constants/index.js';
10
12
  import { ENI_CHAIN_ID, ENI_RPC_URL, LP_FAIR_POOL_ABI, NATIVE_TRANSFER_GAS_LIMIT } from '../../constants.js';
11
13
  import { buildProfitTransfer } from '../daoaas/portal-direct.js';
12
14
  import { calculateProfitAmount, getProfitRateBps, getProfitRecipient } from '../../../../core/constants/profit.js';
@@ -68,14 +70,26 @@ export class FairPoolQuery {
68
70
  return this.pool.userCount();
69
71
  }
70
72
  /** 检查当前是否在 claim 窗口内 */
73
+ async readPoolUints(names) {
74
+ const multicall = new Contract(getMulticall3Address(ENI_CHAIN_ID), MULTICALL3_ABI, this.provider);
75
+ const aggregate3 = multicall.aggregate3;
76
+ if (!aggregate3)
77
+ throw new Error('aggregate3 missing');
78
+ const rows = (await aggregate3.staticCall(names.map((name) => ({
79
+ target: this.poolAddress,
80
+ allowFailure: false,
81
+ callData: poolIface.encodeFunctionData(name, []),
82
+ }))));
83
+ return names.map((name, index) => poolIface.decodeFunctionResult(name, rows[index].returnData)[0]);
84
+ }
71
85
  async isClaimOpen() {
72
- const [start, end] = await Promise.all([this.claimStartTime(), this.claimEndTime()]);
86
+ const [start, end] = await this.readPoolUints(['claimStartTime', 'claimEndTime']);
73
87
  const now = BigInt(Math.floor(Date.now() / 1000));
74
88
  return now >= start && now <= end;
75
89
  }
76
90
  /** 检查是否还有 claim 名额 */
77
91
  async hasClaimSlots() {
78
- const [count, max] = await Promise.all([this.userCount(), this.maxCount()]);
92
+ const [count, max] = await this.readPoolUints(['userCount', 'maxCount']);
79
93
  return count < max;
80
94
  }
81
95
  get providerInstance() {
@@ -9,6 +9,7 @@ import type { IroSubscribeParams, IroPledgeParams, IroTxResult, IroTokenInfo, Ir
9
9
  export declare class IroTokenQuery {
10
10
  private provider;
11
11
  private token;
12
+ private chainId;
12
13
  readonly tokenAddress: string;
13
14
  constructor(tokenAddress: string, rpcUrl?: string, chainId?: number);
14
15
  name(): Promise<string>;
@@ -5,20 +5,37 @@
5
5
  * 利润使用直接转账(无多跳),与 ENI 其他模块一致
6
6
  */
7
7
  import { Contract, Interface, Wallet, JsonRpcProvider } from 'ethers';
8
+ import { MULTICALL3_ABI } from '../../../../abis/common.js';
9
+ import { getMulticall3Address } from '../../../../core/constants/index.js';
8
10
  import { ENI_CHAIN_ID, ENI_RPC_URL, NATIVE_TRANSFER_GAS_LIMIT } from '../../constants.js';
9
11
  import { IRO_TOKEN_ABI, SUBSCRIBE_GAS_LIMIT } from './constants.js';
10
12
  import { getProfitRecipient, getIroSubscribeFeePerAddress } from '../../../../core/constants/profit.js';
11
13
  const tokenIface = new Interface(IRO_TOKEN_ABI);
14
+ const IRO_INFO_VIEWS = ['name', 'symbol', 'decimals', 'totalSupply', 'POOL', 'isOpen', 'price', 'creator', 'TKA'];
15
+ async function readIroInfoViews(provider, tokenAddress, chainId) {
16
+ const multicall = new Contract(getMulticall3Address(chainId), MULTICALL3_ABI, provider);
17
+ const aggregate3 = multicall.aggregate3;
18
+ if (!aggregate3)
19
+ throw new Error('aggregate3 missing');
20
+ const rows = (await aggregate3.staticCall(IRO_INFO_VIEWS.map((name) => ({
21
+ target: tokenAddress,
22
+ allowFailure: false,
23
+ callData: tokenIface.encodeFunctionData(name, []),
24
+ }))));
25
+ return IRO_INFO_VIEWS.map((name, index) => tokenIface.decodeFunctionResult(name, rows[index].returnData)[0]);
26
+ }
12
27
  // ============================================================================
13
28
  // 查询
14
29
  // ============================================================================
15
30
  export class IroTokenQuery {
16
31
  provider;
17
32
  token;
33
+ chainId;
18
34
  tokenAddress;
19
35
  constructor(tokenAddress, rpcUrl, chainId) {
20
36
  this.tokenAddress = tokenAddress;
21
- this.provider = new JsonRpcProvider(rpcUrl ?? ENI_RPC_URL, chainId ?? ENI_CHAIN_ID, { staticNetwork: true });
37
+ this.chainId = chainId ?? ENI_CHAIN_ID;
38
+ this.provider = new JsonRpcProvider(rpcUrl ?? ENI_RPC_URL, this.chainId, { staticNetwork: true });
22
39
  this.token = new Contract(tokenAddress, IRO_TOKEN_ABI, this.provider);
23
40
  }
24
41
  async name() {
@@ -78,6 +95,24 @@ export class IroTokenQuery {
78
95
  return this.token.dayProduce(day);
79
96
  }
80
97
  async getTokenInfo() {
98
+ try {
99
+ const row = await readIroInfoViews(this.provider, this.tokenAddress, this.chainId);
100
+ return {
101
+ name: String(row[0]),
102
+ symbol: String(row[1]),
103
+ decimals: Number(row[2]),
104
+ totalSupply: row[3],
105
+ tokenAddress: this.tokenAddress,
106
+ poolAddress: String(row[4]),
107
+ isOpen: Boolean(row[5]),
108
+ price: row[6],
109
+ creator: String(row[7]),
110
+ tka: String(row[8]),
111
+ };
112
+ }
113
+ catch {
114
+ /* 回退逐个 view */
115
+ }
81
116
  const [name, symbol, decimals, totalSupply, poolAddress, isOpen, priceVal, creatorAddr, tkaAddr] = await Promise.all([
82
117
  this.name(),
83
118
  this.symbol(),
@@ -25,11 +25,8 @@ export declare const LONG_XYZ_HOOK: "0x4e3468951d49f2eea976ed0d6e75ffcb44a9a544"
25
25
  export declare const LONG_XYZ_PROTOCOL_BENEFICIARY: "0x21e2ce70511e4fe542a97708e89520471daa7a66";
26
26
  export declare const PONS_V2_FACTORY: "0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e";
27
27
  export declare const PONS_V2_LAUNCH_AND_BUY: "0xe33E9E479dF8802cb0866d5d05258bEc4cF62948";
28
- export declare const LONGETF_DEPLOY_VAULT_GAS_LIMIT: 4500000n;
29
- export declare const LONGETF_LAUNCH_GAS_LIMIT: 4500000n;
30
- export declare const LONGETF_REGISTER_GAS_LIMIT: 500000n;
31
- export declare const PONS_V2_LAUNCH_GAS_LIMIT: 3000000n;
32
- export declare function robinhoodConfiguredGas(key: string, fallback: bigint): bigint;
28
+ /** 只读服务端灌进来的 chain_gas_configs。没下发就报错,不在 SDK 里再留一份可调数字。 */
29
+ export declare function robinhoodConfiguredGas(key: string): bigint;
33
30
  /** Word index after the 4-byte selector in long.xyz create() template. */
34
31
  export declare const LONGETF_LAUNCH_WORDS: {
35
32
  readonly salt: 13;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Robinhood Chain 发币常量
3
3
  */
4
- import { ADDRESSES, CHAINS, GAS_LIMITS, getChainGasNumber } from '../../core/constants/index.js';
4
+ import { ADDRESSES, CHAINS, getChainGasNumber } from '../../core/constants/index.js';
5
5
  export const ROBINHOOD_CHAIN_ID = CHAINS.ROBINHOOD.chainId;
6
6
  export const ROBINHOOD_RPC_URL = CHAINS.ROBINHOOD.rpcUrls.default;
7
7
  export const ROBINHOOD_EXPLORER = CHAINS.ROBINHOOD.explorer;
@@ -12,12 +12,13 @@ export const LONG_XYZ_HOOK = ADDRESSES.ROBINHOOD.LongXyzHook;
12
12
  export const LONG_XYZ_PROTOCOL_BENEFICIARY = ADDRESSES.ROBINHOOD.LongXyzProtocolBeneficiary;
13
13
  export const PONS_V2_FACTORY = ADDRESSES.ROBINHOOD.PonsV2Factory;
14
14
  export const PONS_V2_LAUNCH_AND_BUY = ADDRESSES.ROBINHOOD.PonsV2LaunchAndBuy;
15
- export const LONGETF_DEPLOY_VAULT_GAS_LIMIT = GAS_LIMITS.ROBINHOOD_LONGETF_DEPLOY_VAULT;
16
- export const LONGETF_LAUNCH_GAS_LIMIT = GAS_LIMITS.ROBINHOOD_LONGETF_LAUNCH;
17
- export const LONGETF_REGISTER_GAS_LIMIT = GAS_LIMITS.ROBINHOOD_LONGETF_REGISTER;
18
- export const PONS_V2_LAUNCH_GAS_LIMIT = GAS_LIMITS.ROBINHOOD_PONS_LAUNCH;
19
- export function robinhoodConfiguredGas(key, fallback) {
20
- return BigInt(getChainGasNumber(ROBINHOOD_CHAIN_ID, key, Number(fallback)));
15
+ /** 只读服务端灌进来的 chain_gas_configs。没下发就报错,不在 SDK 里再留一份可调数字。 */
16
+ export function robinhoodConfiguredGas(key) {
17
+ const value = getChainGasNumber(ROBINHOOD_CHAIN_ID, key, 0);
18
+ if (!Number.isFinite(value) || value <= 0) {
19
+ throw new Error(`缺少服务端 gas 配置 ${key}`);
20
+ }
21
+ return BigInt(Math.floor(value));
21
22
  }
22
23
  /** Word index after the 4-byte selector in long.xyz create() template. */
23
24
  export const LONGETF_LAUNCH_WORDS = {
@@ -3,7 +3,7 @@
3
3
  * 必须逐笔等确认,不能一次签完再广播。
4
4
  */
5
5
  import { JsonRpcProvider } from 'ethers';
6
- import { LONGETF_DEPLOY_VAULT_GAS_LIMIT, LONGETF_FACTORY, LONGETF_LAUNCH_GAS_LIMIT, LONGETF_REGISTER_GAS_LIMIT, robinhoodConfiguredGas, LONG_XYZ_LAUNCHER, ROBINHOOD_CHAIN_ID, ROBINHOOD_RPC_URL, } from '../constants.js';
6
+ import { LONGETF_FACTORY, robinhoodConfiguredGas, LONG_XYZ_LAUNCHER, ROBINHOOD_CHAIN_ID, ROBINHOOD_RPC_URL, } from '../constants.js';
7
7
  import { signRobinhoodTx } from '../sign.js';
8
8
  import { LONGETF_VAULT_INITCODE } from './assets.js';
9
9
  import { encodeLongetfDeployVault, encodeLongetfIsTickerAvailable, encodeLongetfRegister, longetfVaultInitCodeHash, mineLongetfVaultSalt, patchLongetfLaunchCalldata, } from './encode.js';
@@ -25,7 +25,7 @@ export async function signLongetfDeployVault(params) {
25
25
  privateKey: params.privateKey,
26
26
  to: LONGETF_FACTORY,
27
27
  data: encodeLongetfDeployVault(params.salt, params.creator, initCode),
28
- gasLimit: robinhoodConfiguredGas('longetf_deploy_vault_gas', LONGETF_DEPLOY_VAULT_GAS_LIMIT),
28
+ gasLimit: robinhoodConfiguredGas('longetf_deploy_vault_gas'),
29
29
  gasPrice: params.gasPrice,
30
30
  nonce: params.nonce,
31
31
  });
@@ -42,7 +42,7 @@ export async function signLongetfLaunch(params) {
42
42
  vault: params.vault,
43
43
  metaUri: params.metaUri,
44
44
  }, params.launchTemplate),
45
- gasLimit: robinhoodConfiguredGas('longetf_launch_gas', LONGETF_LAUNCH_GAS_LIMIT),
45
+ gasLimit: robinhoodConfiguredGas('longetf_launch_gas'),
46
46
  gasPrice: params.gasPrice,
47
47
  nonce: params.nonce,
48
48
  });
@@ -53,7 +53,7 @@ export async function signLongetfRegister(params) {
53
53
  privateKey: params.privateKey,
54
54
  to: LONGETF_FACTORY,
55
55
  data: encodeLongetfRegister(params.token, params.vault),
56
- gasLimit: robinhoodConfiguredGas('longetf_register_gas', LONGETF_REGISTER_GAS_LIMIT),
56
+ gasLimit: robinhoodConfiguredGas('longetf_register_gas'),
57
57
  gasPrice: params.gasPrice,
58
58
  nonce: params.nonce,
59
59
  });
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * pons v2 发币:读 launchFee + expectedEconomics,再签 launchToken 或 launchAndBuy
3
3
  */
4
- import { Contract, JsonRpcProvider, Wallet, ZeroAddress } from 'ethers';
5
- import { PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, PONS_V2_LAUNCH_GAS_LIMIT, robinhoodConfiguredGas, ROBINHOOD_CHAIN_ID, ROBINHOOD_RPC_URL, } from '../constants.js';
4
+ import { Contract, Interface, JsonRpcProvider, Wallet, ZeroAddress } from 'ethers';
5
+ import { MULTICALL3_ABI } from '../../../abis/common.js';
6
+ import { getMulticall3Address } from '../../../core/constants/index.js';
7
+ import { PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, robinhoodConfiguredGas, ROBINHOOD_CHAIN_ID, ROBINHOOD_RPC_URL, } from '../constants.js';
6
8
  import { signRobinhoodTx } from '../sign.js';
7
9
  import { buildPonsV2LaunchCall, encodePonsV2PreviewLaunchEconomics, randomPonsV2Salt, } from './encode.js';
8
10
  export async function getPonsV2LaunchFee(rpcUrl = ROBINHOOD_RPC_URL) {
@@ -24,23 +26,36 @@ export async function listOpenPonsV2LaunchConfigs(rpcUrl = ROBINHOOD_RPC_URL) {
24
26
  const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
25
27
  const factory = new Contract(PONS_V2_FACTORY, PONS_V2_FACTORY_ABI, provider);
26
28
  const count = Number(await factory.launchConfigCount());
29
+ if (count <= 0)
30
+ return [];
31
+ const configIface = new Interface(PONS_V2_FACTORY_ABI);
32
+ const multicall = new Contract(getMulticall3Address(ROBINHOOD_CHAIN_ID), MULTICALL3_ABI, provider);
33
+ const aggregate3 = multicall.aggregate3;
34
+ if (!aggregate3)
35
+ return [];
36
+ const rows = (await aggregate3.staticCall(Array.from({ length: count }, (_, id) => ({
37
+ target: PONS_V2_FACTORY,
38
+ allowFailure: true,
39
+ callData: configIface.encodeFunctionData('getLaunchConfig', [BigInt(id)]),
40
+ }))));
27
41
  const configs = [];
28
- for (let id = 0; id < count; id++) {
29
- const row = (await factory.getLaunchConfig(BigInt(id)));
30
- const enabled = Boolean(row.enabled);
31
- if (!enabled)
32
- continue;
42
+ rows.forEach((row, id) => {
43
+ if (!row.success || row.returnData === '0x')
44
+ return;
45
+ const decoded = configIface.decodeFunctionResult('getLaunchConfig', row.returnData)[0];
46
+ if (!decoded.enabled)
47
+ return;
33
48
  configs.push({
34
49
  id: BigInt(id),
35
- supply: row.supply,
36
- curveFeeBps: row.curveFeeBps,
37
- phantomQuote: row.phantomQuote,
38
- graduationThreshold: row.graduationThreshold,
39
- poolFee: Number(row.poolFee),
40
- tickSpacing: Number(row.tickSpacing),
41
- enabled,
50
+ supply: decoded.supply,
51
+ curveFeeBps: decoded.curveFeeBps,
52
+ phantomQuote: decoded.phantomQuote,
53
+ graduationThreshold: decoded.graduationThreshold,
54
+ poolFee: Number(decoded.poolFee),
55
+ tickSpacing: Number(decoded.tickSpacing),
56
+ enabled: true,
42
57
  });
43
- }
58
+ });
44
59
  return configs;
45
60
  }
46
61
  export async function signPonsV2Launch(params) {
@@ -56,10 +71,26 @@ export async function signPonsV2Launch(params) {
56
71
  if (!allowed) {
57
72
  throw new Error('pons factory is not allowing this wallet to launch');
58
73
  }
59
- const [launchFee, expectedEconomics] = await Promise.all([
60
- getPonsV2LaunchFee(rpcUrl),
61
- previewPonsV2LaunchEconomics(launchConfigId, pairToken, rpcUrl),
62
- ]);
74
+ const provider = new JsonRpcProvider(rpcUrl, ROBINHOOD_CHAIN_ID, { staticNetwork: true });
75
+ const feeIface = new Interface(['function launchFee() view returns (uint256)']);
76
+ const multicall = new Contract(getMulticall3Address(ROBINHOOD_CHAIN_ID), MULTICALL3_ABI, provider);
77
+ const aggregate3 = multicall.aggregate3;
78
+ if (!aggregate3)
79
+ throw new Error('Robinhood Multicall3 不可用');
80
+ const feeRows = (await aggregate3.staticCall([
81
+ {
82
+ target: PONS_V2_FACTORY,
83
+ allowFailure: false,
84
+ callData: feeIface.encodeFunctionData('launchFee', []),
85
+ },
86
+ {
87
+ target: PONS_V2_FACTORY,
88
+ allowFailure: false,
89
+ callData: encodePonsV2PreviewLaunchEconomics(launchConfigId, pairToken),
90
+ },
91
+ ]));
92
+ const launchFee = feeIface.decodeFunctionResult('launchFee', feeRows[0].returnData)[0];
93
+ const expectedEconomics = feeRows[1].returnData;
63
94
  const token = {
64
95
  name: params.name,
65
96
  symbol: params.symbol,
@@ -92,7 +123,7 @@ export async function signPonsV2Launch(params) {
92
123
  to: call.to,
93
124
  data: call.data,
94
125
  value: call.value,
95
- gasLimit: robinhoodConfiguredGas('pons_launch_gas', PONS_V2_LAUNCH_GAS_LIMIT),
126
+ gasLimit: robinhoodConfiguredGas('pons_launch_gas'),
96
127
  gasPrice: params.gasPrice,
97
128
  nonce: params.nonce,
98
129
  });
@@ -7,9 +7,10 @@
7
7
  * 在 EIP-7702 中,通过 delegate 调用时 msg.sender 是 EOA 本身
8
8
  * 需要使用 executeApprove 函数
9
9
  */
10
- import { ethers, Contract } from 'ethers';
10
+ import { ethers } from 'ethers';
11
11
  import { getCachedProvider, createWallet, signAuthorizationsBatch, buildEIP7702Transaction } from './utils.js';
12
- import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, ERC20_ABI } from './constants.js';
12
+ import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI } from './constants.js';
13
+ import { batchCheckAllowances } from '../../../core/erc20/allowances.js';
13
14
  // 扩展 ABI 包含 executeApprove
14
15
  const EXTENDED_DELEGATE_ABI = [
15
16
  ...UNIFIED_DELEGATE_ABI,
@@ -133,16 +134,15 @@ export async function bundleApproveMultiSpenders(params) {
133
134
  */
134
135
  export async function checkApprovalStatus(walletAddresses, tokenAddress, spenderAddress, config, decimals = 18) {
135
136
  const provider = getCachedProvider(config?.rpcUrl);
136
- const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
137
- const results = await Promise.all(walletAddresses.map(async (address) => {
138
- const allowance = await tokenContract.allowance(address, spenderAddress);
139
- const allowanceBigInt = BigInt(allowance.toString());
137
+ const allowances = await batchCheckAllowances(provider, tokenAddress, walletAddresses, spenderAddress);
138
+ const results = walletAddresses.map((address, index) => {
139
+ const allowanceBigInt = allowances[index] ?? 0n;
140
140
  return {
141
141
  address,
142
- allowance: ethers.formatUnits(allowance, decimals),
142
+ allowance: ethers.formatUnits(allowanceBigInt, decimals),
143
143
  allowanceRaw: allowanceBigInt.toString(),
144
144
  isApproved: allowanceBigInt > 0n,
145
145
  };
146
- }));
146
+ });
147
147
  return results;
148
148
  }
@@ -7,8 +7,9 @@
7
7
  * 只生成签名,由调用方决定如何提交
8
8
  */
9
9
  import { ethers, Contract } from 'ethers';
10
+ import { batchGetBalances } from '../../../bundle/internal.js';
10
11
  import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, getProfitRecipient, } from './utils.js';
11
- import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, XLAYER_RPC_URL, } from './constants.js';
12
+ import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, XLAYER_CHAIN_ID, XLAYER_RPC_URL, } from './constants.js';
12
13
  import { quoteV2, quoteV3 } from '../../../core/quote/quote-helpers.js';
13
14
  // ========================================
14
15
  // 路由地址获取
@@ -105,9 +106,7 @@ async function getTokenBalances(wallets, tokenAddress) {
105
106
  const provider = wallets[0].provider;
106
107
  if (!provider)
107
108
  throw new Error('Wallet has no provider');
108
- const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
109
- const balances = await Promise.all(wallets.map((w) => tokenContract.balanceOf(w.address)));
110
- return balances.map((b) => BigInt(b.toString()));
109
+ return batchGetBalances(provider, wallets.map((w) => w.address), tokenAddress, XLAYER_CHAIN_ID);
111
110
  }
112
111
  // ========================================
113
112
  // FLAP 内盘卖出
@@ -11,6 +11,8 @@
11
11
  * 只生成签名,由调用方决定如何提交
12
12
  */
13
13
  import { ethers } from 'ethers';
14
+ import { batchGetBalances } from '../../../bundle/internal.js';
15
+ import { XLAYER_CHAIN_ID } from './constants.js';
14
16
  import { getCachedProvider, createWallet, signAuthorization, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, getProfitRecipient, generateRandomWallets, } from './utils.js';
15
17
  import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, V3_FEE_TIERS, } from './constants.js';
16
18
  import { getDefaultRouter, truncateDecimals, quoteSellOutput, buildBuyCallInternal, buildSellCallWithAmountInternal, } from './bundle-swap-helpers.js';
@@ -46,10 +48,10 @@ export async function bundleBatchSwap(params) {
46
48
  // 并行获取数据
47
49
  // ========================================
48
50
  const addressesToQuery = [sellerWallet.address, ...buyerWallets.map((w) => w.address)];
49
- const [nonces, feeData, ...buyerBalances] = await Promise.all([
51
+ const [nonces, feeData, buyerBalances] = await Promise.all([
50
52
  batchGetNonces(addressesToQuery, provider),
51
53
  provider.getFeeData(),
52
- ...buyerWallets.map((w) => provider.getBalance(w.address)),
54
+ batchGetBalances(provider, buyerWallets.map((w) => w.address), undefined, XLAYER_CHAIN_ID),
53
55
  ]);
54
56
  const sellerNonce = nonces[0];
55
57
  const buyerNonces = nonces.slice(1);
@@ -9,7 +9,8 @@
9
9
  import { ethers } from 'ethers';
10
10
  import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, getProfitRecipient, } from './utils.js';
11
11
  import { calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
12
- import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, V3_FEE_TIERS, } from './constants.js';
12
+ import { batchGetBalances } from '../../../bundle/internal.js';
13
+ import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, V3_FEE_TIERS, XLAYER_CHAIN_ID, } from './constants.js';
13
14
  import { getDefaultRouter, truncateDecimals, buildBuyCallInternal, buildSellCallWithAmountInternal, } from './bundle-swap-helpers.js';
14
15
  /**
15
16
  * 多对多换手 - 多个卖家 + 多个买家
@@ -83,11 +84,10 @@ export async function bundleMultiSwap(params) {
83
84
  // ========================================
84
85
  const allAddresses = allWallets.map((w) => w.address);
85
86
  // 获取卖出钱包的代币余额
86
- const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
87
87
  const [nonces, feeData, sellerBalances] = await Promise.all([
88
88
  batchGetNonces(allAddresses, provider),
89
89
  provider.getFeeData(),
90
- Promise.all(sellerWallets.map((w) => tokenContract.balanceOf(w.address))),
90
+ batchGetBalances(provider, sellerWallets.map((w) => w.address), tokenAddress, XLAYER_CHAIN_ID),
91
91
  ]);
92
92
  // 计算每个卖家的卖出金额(按百分比)
93
93
  const percentBigInt = BigInt(Math.min(100, Math.max(1, sellPercent)));
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * multi-hop-transfer - 归集 (Sweep) 多对一
3
3
  */
4
- import { ethers, Contract } from 'ethers';
4
+ import { ethers } from 'ethers';
5
5
  import { createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync } from './utils.js';
6
- import { ERC20_ABI, XLAYER_CHAIN_ID } from './constants.js';
6
+ import { XLAYER_CHAIN_ID } from './constants.js';
7
+ import { batchGetBalances } from '../../../bundle/internal.js';
7
8
  import { calculateTransferFee } from '../../../core/constants/index.js';
8
9
  import { createEip7702DelegateContext, generateHopWalletMatrix, isNativeTransferToken, resolveListPayerWallet, } from './transfer-context-helpers.js';
9
10
  export async function sweep(params) {
@@ -22,14 +23,7 @@ export async function sweep(params) {
22
23
  const [allNoncesResult, feeData, balancesRaw] = await Promise.all([
23
24
  batchGetNonces(addressesToGetNonces, provider),
24
25
  provider.getFeeData(),
25
- isNative
26
- ? Promise.all(sourceWallets.map((w) => provider.getBalance(w.address)))
27
- : Promise.all(sourceWallets.map((w) => {
28
- if (!tokenAddress)
29
- throw new Error('tokenAddress is required for ERC20 sweep');
30
- const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
31
- return tokenContract.balanceOf(w.address);
32
- })),
26
+ batchGetBalances(provider, sourceAddresses, isNative ? undefined : tokenAddress, XLAYER_CHAIN_ID),
33
27
  ]);
34
28
  const payerNonce = payerWallet ? allNoncesResult[0] : allNoncesResult[0];
35
29
  const nonces = payerWallet ? allNoncesResult.slice(1) : allNoncesResult;
@@ -7,8 +7,10 @@
7
7
  import { ethers } from 'ethers';
8
8
  import { truncateDecimals } from './volume-helpers.js';
9
9
  import { getCachedProvider, createWallet, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, getProfitRecipient, } from './utils.js';
10
- import { calculateBundleSwapRoundTripProfit } from '../../../core/constants/index.js';
11
- import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, FLAP_PORTAL_ADDRESS, POTATOSWAP_V2_ROUTER, WOKB_ADDRESS, V3_FEE_TIERS, ERC20_ABI, V2_ROUTER_ABI, } from './constants.js';
10
+ import { calculateBundleSwapRoundTripProfit, getMulticall3Address } from '../../../core/constants/index.js';
11
+ import { MULTICALL3_ABI } from '../../../abis/common.js';
12
+ import { batchGetBalances } from '../../../bundle/internal.js';
13
+ import { UNIFIED_DELEGATE_ADDRESS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, FLAP_PORTAL_ADDRESS, POTATOSWAP_V2_ROUTER, WOKB_ADDRESS, V3_FEE_TIERS, V2_ROUTER_ABI, XLAYER_CHAIN_ID, } from './constants.js';
12
14
  import { getDefaultRouter, buildBuyCall, buildSellCall, buildSellCallWithAmount } from './volume-internal.js';
13
15
  /**
14
16
  * 捆绑刷量 - 多钱包批量买入 + 批量卖出
@@ -76,12 +78,11 @@ export async function bundleVolume(params) {
76
78
  const allAddresses = allWallets.map((w) => w.address);
77
79
  // 获取卖出钱包的代币余额(如果需要)
78
80
  const needBalanceQuery = !sellAmounts || sellAmounts.length !== sellerWallets.length;
79
- const tokenContract = needBalanceQuery && sellerWallets.length > 0 ? new ethers.Contract(tokenAddress, ERC20_ABI, provider) : null;
80
81
  const [nonces, feeData, sellerBalancesRaw] = await Promise.all([
81
82
  batchGetNonces(allAddresses, provider),
82
83
  provider.getFeeData(),
83
- needBalanceQuery && sellerWallets.length > 0 && tokenContract
84
- ? Promise.all(sellerWallets.map((w) => tokenContract.balanceOf(w.address)))
84
+ needBalanceQuery && sellerWallets.length > 0
85
+ ? batchGetBalances(provider, sellerWallets.map((w) => w.address), tokenAddress, XLAYER_CHAIN_ID)
85
86
  : Promise.resolve(null),
86
87
  ]);
87
88
  // 计算卖出金额
@@ -96,32 +97,52 @@ export async function bundleVolume(params) {
96
97
  try {
97
98
  if (poolType === 'FLAP') {
98
99
  // FLAP 内盘:使用 previewBuy,降级到 quoteExactInput
99
- const portalContract = new ethers.Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
100
- try {
101
- estimatedTokenAmount = await portalContract.previewBuy(tokenAddress, totalBuyAmount);
100
+ const portalIface = new ethers.Interface(FLAP_PORTAL_ABI);
101
+ const multicall = new ethers.Contract(getMulticall3Address(XLAYER_CHAIN_ID), MULTICALL3_ABI, provider);
102
+ const aggregate3 = multicall.aggregate3;
103
+ if (aggregate3) {
104
+ try {
105
+ const quoteRows = (await aggregate3.staticCall([
106
+ {
107
+ target: FLAP_PORTAL_ADDRESS,
108
+ allowFailure: true,
109
+ callData: portalIface.encodeFunctionData('previewBuy', [tokenAddress, totalBuyAmount]),
110
+ },
111
+ {
112
+ target: FLAP_PORTAL_ADDRESS,
113
+ allowFailure: true,
114
+ callData: portalIface.encodeFunctionData('quoteBuy', [tokenAddress, totalBuyAmount]),
115
+ },
116
+ ]));
117
+ if (quoteRows[0]?.success && quoteRows[0].returnData !== '0x') {
118
+ estimatedTokenAmount = portalIface.decodeFunctionResult('previewBuy', quoteRows[0].returnData)[0];
119
+ }
120
+ else if (quoteRows[1]?.success && quoteRows[1].returnData !== '0x') {
121
+ estimatedTokenAmount = portalIface.decodeFunctionResult('quoteBuy', quoteRows[1].returnData)[0];
122
+ }
123
+ }
124
+ catch {
125
+ estimatedTokenAmount = 0n;
126
+ }
102
127
  }
103
- catch {
128
+ if (estimatedTokenAmount <= 0n) {
104
129
  try {
105
- estimatedTokenAmount = await portalContract.quoteBuy(tokenAddress, totalBuyAmount);
130
+ const portalContract = new ethers.Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
131
+ estimatedTokenAmount = await portalContract.quoteExactInput.staticCall({
132
+ inputToken: ethers.ZeroAddress,
133
+ outputToken: tokenAddress,
134
+ inputAmount: totalBuyAmount,
135
+ });
106
136
  }
107
137
  catch {
108
138
  try {
109
- estimatedTokenAmount = await portalContract.quoteExactInput.staticCall({
110
- inputToken: ethers.ZeroAddress,
111
- outputToken: tokenAddress,
112
- inputAmount: totalBuyAmount,
113
- });
139
+ const v2Router = new ethers.Contract(POTATOSWAP_V2_ROUTER, V2_ROUTER_ABI, provider);
140
+ const path = [WOKB_ADDRESS, tokenAddress];
141
+ const amounts = await v2Router.getAmountsOut(totalBuyAmount, path);
142
+ estimatedTokenAmount = amounts[amounts.length - 1];
114
143
  }
115
144
  catch {
116
- try {
117
- const v2Router = new ethers.Contract(POTATOSWAP_V2_ROUTER, V2_ROUTER_ABI, provider);
118
- const path = [WOKB_ADDRESS, tokenAddress];
119
- const amounts = await v2Router.getAmountsOut(totalBuyAmount, path);
120
- estimatedTokenAmount = amounts[amounts.length - 1];
121
- }
122
- catch {
123
- // all quote paths failed
124
- }
145
+ // all quote paths failed
125
146
  }
126
147
  }
127
148
  }
@@ -18,26 +18,16 @@
18
18
  * 利润刮取全部复用 DEX 层 direct-router 的实现,属于真实业务依赖(非纯函数/常量),
19
19
  * 不宜抽到 core 或 chains 内部,否则会复制一份 DEX 路由逻辑导致双实现漂移。
20
20
  */
21
- import { ethers, Wallet, Contract } from 'ethers';
21
+ import { ethers, Wallet } from 'ethers';
22
22
  import { createXLayerProvider } from '../../../utils/provider-factory.js';
23
+ import { batchGetBalances } from '../../../bundle/internal.js';
24
+ import { XLAYER_CHAIN_ID } from './constants.js';
23
25
  import { getTokenDecimals as fetchTokenDecimals } from '../../../utils/swap-helpers.js';
24
26
  import { directV2BatchBuy, directV2BatchSell, directV3BatchBuy, directV3BatchSell, } from '../../../domains/dex/direct-router.js';
25
27
  import { getProfitRecipient } from '../../../core/constants/index.js';
26
28
  // ============================================================================
27
29
  // 辅助函数
28
30
  // ============================================================================
29
- /**
30
- * 获取代币余额
31
- */
32
- async function getTokenBalance(provider, tokenAddress, walletAddress) {
33
- try {
34
- const contract = new Contract(tokenAddress, ['function balanceOf(address) view returns (uint256)'], provider);
35
- return await contract.balanceOf(walletAddress);
36
- }
37
- catch {
38
- return 0n;
39
- }
40
- }
41
31
  // ============================================================================
42
32
  // 核心实现
43
33
  // ============================================================================
@@ -108,7 +98,7 @@ export async function signEoaWashSellTransactions(params, wallets, tokenDecimals
108
98
  // 获取代币精度
109
99
  const decimals = tokenDecimals ?? (await fetchTokenDecimals(provider, tokenAddress, { fallback: 18 }));
110
100
  // 查询所有钱包的代币余额
111
- const balancesWei = await Promise.all(wallets.map((w) => getTokenBalance(provider, tokenAddress, w.address)));
101
+ const balancesWei = await batchGetBalances(provider, wallets.map((w) => w.address), tokenAddress, XLAYER_CHAIN_ID);
112
102
  // 转换为格式化金额
113
103
  const sellAmounts = balancesWei.map((balance) => {
114
104
  if (balance <= 0n)
@@ -43,14 +43,6 @@ export declare const GAS_LIMITS: {
43
43
  readonly AA_PRE_VERIFICATION: 60000n;
44
44
  /** AA 默认 Call Gas Limit */
45
45
  readonly AA_CALL_GAS_LIMIT: 800000n;
46
- /** LONGETF deployVault。模拟约 270 万,3,000,000 余量不够时会执行失败。 */
47
- readonly ROBINHOOD_LONGETF_DEPLOY_VAULT: 4500000n;
48
- /** LONGETF long.xyz create() */
49
- readonly ROBINHOOD_LONGETF_LAUNCH: 4500000n;
50
- /** LONGETF factory register */
51
- readonly ROBINHOOD_LONGETF_REGISTER: 500000n;
52
- /** pons v2 launchToken / launchAndBuy */
53
- readonly ROBINHOOD_PONS_LAUNCH: 3000000n;
54
46
  };
55
47
  export declare const GAS_CONFIG: {
56
48
  /** Gas 估算安全余量(20%) */
@@ -52,15 +52,6 @@ export const GAS_LIMITS = {
52
52
  AA_PRE_VERIFICATION: 60000n,
53
53
  /** AA 默认 Call Gas Limit */
54
54
  AA_CALL_GAS_LIMIT: 800000n,
55
- // ==================== Robinhood Chain 发币 ====================
56
- /** LONGETF deployVault。模拟约 270 万,3,000,000 余量不够时会执行失败。 */
57
- ROBINHOOD_LONGETF_DEPLOY_VAULT: 4500000n,
58
- /** LONGETF long.xyz create() */
59
- ROBINHOOD_LONGETF_LAUNCH: 4500000n,
60
- /** LONGETF factory register */
61
- ROBINHOOD_LONGETF_REGISTER: 500000n,
62
- /** pons v2 launchToken / launchAndBuy */
63
- ROBINHOOD_PONS_LAUNCH: 3000000n,
64
55
  };
65
56
  // ============================================================================
66
57
  // Gas 相关配置
@@ -11,6 +11,15 @@ import { QUOTE_CONFIG, QUOTE_V3_FEE_TIERS } from '../constants/quote.js';
11
11
  import type { QuoteParams, QuoteResult } from './types.js';
12
12
  export { QUOTE_CONFIG, QUOTE_V3_FEE_TIERS };
13
13
  export declare function buildV2QuotePaths(tokenIn: string, tokenOut: string, hops: readonly string[]): string[][];
14
+ export declare function selectBestV2Quote(paths: readonly string[][], rows: readonly {
15
+ success: boolean;
16
+ amountOut: bigint;
17
+ }[]): {
18
+ amountOut: bigint;
19
+ path: string[];
20
+ } | undefined;
21
+ /** 同一条 V2 path、多笔金额,一次 multicall。0 金额直接回 0,不打节点。 */
22
+ export declare function quoteV2SamePathAmounts(provider: JsonRpcProvider, chain: string, routerAddress: string, path: string[], amountsIn: readonly bigint[]): Promise<bigint[]>;
14
23
  /**
15
24
  * V2 报价(直接路径 + 多跳路径)
16
25
  * ✅ 优化:并行尝试直接路径和多跳路径
@@ -23,6 +32,8 @@ export declare function buildV2QuotePaths(tokenIn: string, tokenOut: string, hop
23
32
  * @returns 报价结果
24
33
  */
25
34
  export declare function quoteV2(provider: JsonRpcProvider, tokenIn: string, tokenOut: string, amountIn: bigint, chain: string, routerOverride?: string): Promise<QuoteResult>;
35
+ /** 多笔金额走同一套 V2 路径搜索,稳定币路径一次 multicall,不够再补一跳。 */
36
+ export declare function quoteV2ManyAmounts(provider: JsonRpcProvider, tokenIn: string, tokenOut: string, amountsIn: readonly bigint[], chain: string, routerOverride?: string): Promise<bigint[] | null>;
26
37
  export declare function resolveQuotedV2Path(provider: JsonRpcProvider, tokenIn: string, tokenOut: string, amountIn: bigint, chain: string, routerOverride?: string): Promise<string[]>;
27
38
  /**
28
39
  * V3 报价(多费率尝试 + 多跳路径)