four-flap-meme-sdk 1.5.29 → 1.5.31

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.
@@ -15,6 +15,13 @@ const MULTICALL3_BY_CHAIN = {
15
15
  2818: DEFAULT_MULTICALL3, // Morph
16
16
  143: DEFAULT_MULTICALL3, // Monad
17
17
  };
18
+ /**
19
+ * ✅ Multicall3 每批最多查询的地址数量
20
+ * - 考虑因素:RPC 节点 gas 限制(通常 50M)、返回数据大小限制、超时风险
21
+ * - 每个地址的调用数 = 1 (native) + N (tokens)
22
+ * - 安全值:100 个地址,即使查询 10 个代币也只有 ~1100 个 calls
23
+ */
24
+ const MULTICALL_BATCH_SIZE = 100;
18
25
  // ============================================================================
19
26
  // 钱包生成与验证
20
27
  // ============================================================================
@@ -66,8 +73,28 @@ function normalizePrivateKey(input) {
66
73
  // ============================================================================
67
74
  /**
68
75
  * ✅ 内部:批量查询多代币余额(统一逻辑)
76
+ * ✅ 自动分批:如果地址数量超过 MULTICALL_BATCH_SIZE,自动分批查询并合并结果
69
77
  */
70
78
  async function _queryMultiTokenBalances(provider, multicallAddress, tokenAddresses, holders) {
79
+ if (!holders?.length)
80
+ return [];
81
+ // ✅ 如果地址数量超过批次上限,分批处理
82
+ if (holders.length > MULTICALL_BATCH_SIZE) {
83
+ const allResults = [];
84
+ for (let i = 0; i < holders.length; i += MULTICALL_BATCH_SIZE) {
85
+ const batchHolders = holders.slice(i, i + MULTICALL_BATCH_SIZE);
86
+ const batchResults = await _queryMultiTokenBalancesSingle(provider, multicallAddress, tokenAddresses, batchHolders);
87
+ allResults.push(...batchResults);
88
+ }
89
+ return allResults;
90
+ }
91
+ // ✅ 地址数量在限制内,直接查询
92
+ return _queryMultiTokenBalancesSingle(provider, multicallAddress, tokenAddresses, holders);
93
+ }
94
+ /**
95
+ * ✅ 内部:单批次查询多代币余额(不超过 MULTICALL_BATCH_SIZE 个地址)
96
+ */
97
+ async function _queryMultiTokenBalancesSingle(provider, multicallAddress, tokenAddresses, holders) {
71
98
  if (!holders?.length)
72
99
  return [];
73
100
  const erc20Iface = new Interface(ERC20_ABI);
@@ -153,8 +180,27 @@ async function _queryMultiTokenBalances(provider, multicallAddress, tokenAddress
153
180
  }
154
181
  /**
155
182
  * ✅ 内部:查询单代币余额(旧接口兼容)
183
+ * ✅ 自动分批:如果地址数量超过 MULTICALL_BATCH_SIZE,自动分批查询并合并结果
156
184
  */
157
185
  async function _querySingleTokenBalances(provider, multicallAddress, token, holders) {
186
+ if (!holders?.length)
187
+ return [];
188
+ // ✅ 如果地址数量超过批次上限,分批处理
189
+ if (holders.length > MULTICALL_BATCH_SIZE) {
190
+ const allResults = [];
191
+ for (let i = 0; i < holders.length; i += MULTICALL_BATCH_SIZE) {
192
+ const batchHolders = holders.slice(i, i + MULTICALL_BATCH_SIZE);
193
+ const batchResults = await _querySingleTokenBalancesSingle(provider, multicallAddress, token, batchHolders);
194
+ allResults.push(...batchResults);
195
+ }
196
+ return allResults;
197
+ }
198
+ return _querySingleTokenBalancesSingle(provider, multicallAddress, token, holders);
199
+ }
200
+ /**
201
+ * ✅ 内部:单批次查询单代币余额(不超过 MULTICALL_BATCH_SIZE 个地址)
202
+ */
203
+ async function _querySingleTokenBalancesSingle(provider, multicallAddress, token, holders) {
158
204
  if (!holders?.length)
159
205
  return [];
160
206
  const erc20Iface = new Interface(ERC20_ABI);
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { Wallet, ethers } from 'ethers';
5
5
  import { AANonceMap, } from './types.js';
6
- import { FLAP_PORTAL, } from './constants.js';
6
+ import { FLAP_PORTAL, ZERO_ADDRESS, } from './constants.js';
7
7
  import { AAAccountManager, encodeExecute } from './aa-account.js';
8
8
  import { encodeBuyCall, encodeSellCall, encodeApproveCall, PortalQuery, parseOkb, } from './portal-ops.js';
9
9
  import { BundleExecutor } from './bundle.js';
@@ -70,7 +70,18 @@ export class AAPortalSwapExecutor {
70
70
  needApprove = allowance < sellAmountWei;
71
71
  }
72
72
  // 3. 报价买入 OKB
73
- const quoted = await this.portalQuery.previewSell(tokenAddress, sellAmountWei);
73
+ // 关键修复:
74
+ // 在部分代币(例如已迁移到 DEX 的 token)上,FlapPortal.previewSell 会 revert(unknown custom error: 0xac5f6092)。
75
+ // 这会导致 AA 捆绑换手在“签名前置报价阶段”直接失败。
76
+ // 对这类 token,Portal 的 quoteExactInput.staticCall 可以正常返回报价,因此这里做降级。
77
+ const quoted = await (async () => {
78
+ try {
79
+ return await this.portalQuery.previewSell(tokenAddress, sellAmountWei);
80
+ }
81
+ catch {
82
+ return await this.portalQuery.quoteExactInput(tokenAddress, ZERO_ADDRESS, sellAmountWei);
83
+ }
84
+ })();
74
85
  const finalBuyAmountWei = buyAmountOkb
75
86
  ? parseOkb(String(buyAmountOkb))
76
87
  : (quoted * BigInt(10000 - slippageBps)) / 10000n;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.5.29",
3
+ "version": "1.5.31",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",