four-flap-meme-sdk 1.2.75 → 1.2.76

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.
@@ -5,7 +5,6 @@ import { FourClient, buildLoginMessage } from '../../clients/four.js';
5
5
  import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient } from './config.js';
6
6
  import { batchCheckAllowances } from '../../utils/erc20.js';
7
7
  import { trySell } from '../tm.js';
8
- import TM2Abi from '../../abis/TokenManager2.json' with { type: 'json' };
9
8
  const TM2_ABI = [
10
9
  'function createToken(bytes args, bytes signature) payable',
11
10
  'function buyTokenAMAP(uint256 origin, address token, address to, uint256 funds, uint256 minAmount) payable',
@@ -188,18 +187,25 @@ export async function createTokenWithBundleBuyMerkle(params) {
188
187
  throw new Error('等待交易确认超时');
189
188
  }
190
189
  console.log(`✅ 创建交易已确认,区块: ${receipt.blockNumber}`);
191
- // 解析 TokenCreated 事件
190
+ // 解析 TokenCreate 事件(Four.meme 使用的事件名,注意没有 'd')
192
191
  let tokenAddress;
192
+ // 使用 TM2 ABI 解析事件
193
+ const tm2Interface = new ethers.Interface(TM2_ABI);
193
194
  for (const log of receipt.logs) {
194
195
  try {
195
- // TokenCreated(address indexed token, ...)
196
- if (log.topics[0] === ethers.id('TokenCreated(address,address,string,string,uint256,uint256,uint256)')) {
197
- tokenAddress = ethers.getAddress('0x' + log.topics[1].slice(26));
196
+ const parsed = tm2Interface.parseLog({
197
+ topics: log.topics,
198
+ data: log.data
199
+ });
200
+ // Four.meme 的事件是 TokenCreate(不是 TokenCreated)
201
+ if (parsed && parsed.name === 'TokenCreate') {
202
+ tokenAddress = parsed.args.token;
203
+ console.log(`✅ 从 TokenCreate 事件解析代币地址: ${tokenAddress}`);
198
204
  break;
199
205
  }
200
206
  }
201
207
  catch (e) {
202
- // 忽略解析错误
208
+ // 忽略无法解析的日志
203
209
  }
204
210
  }
205
211
  if (!tokenAddress) {
@@ -213,69 +219,24 @@ export async function createTokenWithBundleBuyMerkle(params) {
213
219
  txHashes
214
220
  };
215
221
  }
216
- /**
217
- * 等待交易上链并解析代币地址
218
- */
219
- async function waitAndParseTokenAddress(provider, createTxSigned, bundleUuid) {
220
- console.log('⏳ 等待交易上链,解析代币地址...');
221
- let tokenAddress = ZERO_ADDRESS;
222
- const maxAttempts = 30; // 最多等待 30 秒
223
- for (let i = 0; i < maxAttempts; i++) {
224
- await new Promise(resolve => setTimeout(resolve, 1000)); // 等待 1 秒
225
- try {
226
- // 获取创建交易的回执
227
- const createTxHash = ethers.keccak256(createTxSigned);
228
- const receipt = await provider.getTransactionReceipt(createTxHash);
229
- if (receipt && receipt.status === 1) {
230
- // 从事件日志中解析代币地址
231
- const tm2Interface = new ethers.Interface(TM2Abi);
232
- for (const log of receipt.logs) {
233
- try {
234
- const parsed = tm2Interface.parseLog({
235
- topics: log.topics,
236
- data: log.data
237
- });
238
- if (parsed && parsed.name === 'TokenCreate') {
239
- tokenAddress = parsed.args.token;
240
- console.log(`✅ 成功解析代币地址: ${tokenAddress}`);
241
- return tokenAddress;
242
- }
243
- }
244
- catch (e) {
245
- // 忽略无法解析的日志
246
- }
247
- }
248
- // 如果没有找到 TokenCreate 事件,尝试 TokenCreated
249
- const tokenCreatedTopic = ethers.id('TokenCreated(address,address,uint256,uint256,uint256,uint256)');
250
- for (const log of receipt.logs) {
251
- if (log.topics[0] === tokenCreatedTopic) {
252
- tokenAddress = ethers.getAddress('0x' + log.topics[1].slice(26));
253
- console.log(`✅ 成功解析代币地址: ${tokenAddress}`);
254
- return tokenAddress;
255
- }
256
- }
257
- break;
258
- }
259
- }
260
- catch (error) {
261
- // 继续等待
262
- }
263
- }
264
- if (tokenAddress === ZERO_ADDRESS) {
265
- console.warn('⚠️ 未能从事件中解析代币地址,返回 0x0');
266
- }
267
- return tokenAddress;
268
- }
269
222
  export async function batchBuyWithBundleMerkle(params) {
270
223
  const { privateKeys, buyAmounts, tokenAddress, config } = params;
271
224
  if (privateKeys.length === 0 || buyAmounts.length !== privateKeys.length) {
272
225
  throw new Error(getErrorMessage('KEY_AMOUNT_MISMATCH'));
273
226
  }
274
227
  const { provider, chainId } = createChainContext(config.rpcUrl);
275
- const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
276
- const nonceManager = new NonceManager(provider);
277
- const txType = getTxType(config);
278
228
  const buyers = createWallets(privateKeys, provider);
229
+ const txType = getTxType(config);
230
+ // ✅ 优化:并行执行 gasPrice 获取和 nonce 预加载
231
+ const [gasPrice, nonceManager] = await Promise.all([
232
+ getOptimizedGasPrice(provider, getGasPriceConfig(config)),
233
+ (async () => {
234
+ const nm = new NonceManager(provider);
235
+ // 预加载所有钱包的 nonce(批量查询)
236
+ await Promise.all(buyers.map(w => nm.getNextNonce(w)));
237
+ return nm;
238
+ })()
239
+ ]);
279
240
  const buyFlow = await executeBuyFlow({
280
241
  wallets: buyers,
281
242
  buyAmounts,
@@ -300,10 +261,18 @@ export async function batchSellWithBundleMerkle(params) {
300
261
  throw new Error(getErrorMessage('SELL_KEY_AMOUNT_MISMATCH'));
301
262
  }
302
263
  const { provider, chainId } = createChainContext(config.rpcUrl);
303
- const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
304
- const nonceManager = new NonceManager(provider);
305
264
  const sellers = createWallets(privateKeys, provider);
306
265
  const txType = getTxType(config);
266
+ // ✅ 优化:并行执行 gasPrice 获取和 nonce 预加载
267
+ const [gasPrice, nonceManager] = await Promise.all([
268
+ getOptimizedGasPrice(provider, getGasPriceConfig(config)),
269
+ (async () => {
270
+ const nm = new NonceManager(provider);
271
+ // 预加载所有钱包的 nonce(批量查询)
272
+ await Promise.all(sellers.map(w => nm.getNextNonce(w)));
273
+ return nm;
274
+ })()
275
+ ]);
307
276
  const sellFlow = await executeSellFlow({
308
277
  wallets: sellers,
309
278
  sellAmounts,
@@ -128,15 +128,23 @@ export async function batchBuyWithBundleMerkle(params) {
128
128
  throw new Error(getErrorMessage('KEY_AMOUNT_MISMATCH'));
129
129
  }
130
130
  const { provider, chainId } = createChainContext(chain, config.rpcUrl);
131
- const gasPrice = await resolveGasPrice(provider, config);
132
- const nonceManager = new NonceManager(provider);
133
- const signedTxs = [];
134
131
  const buyers = createWallets(privateKeys, provider);
135
132
  const extractProfit = shouldExtractProfit(config);
136
133
  const { fundsList, originalAmounts, totalBuyAmount, totalProfit } = analyzeBuyFunds(buyAmounts, config, extractProfit);
137
134
  const maxFundsIndex = findMaxIndex(originalAmounts);
138
- const unsignedBuys = await populateBuyTransactions(buyers, FLAP_PORTAL_ADDRESSES[chain], tokenAddress, fundsList);
139
135
  const gasLimits = buildGasLimitList(buyers.length, config);
136
+ const signedTxs = [];
137
+ // ✅ 优化:并行执行所有 RPC 调用(gasPrice + nonces + populateTransaction)
138
+ const [gasPrice, nonceManager, unsignedBuys] = await Promise.all([
139
+ resolveGasPrice(provider, config),
140
+ (async () => {
141
+ const nm = new NonceManager(provider);
142
+ // 预加载所有钱包的 nonce(批量查询)
143
+ await Promise.all(buyers.map(w => nm.getNextNonce(w)));
144
+ return nm;
145
+ })(),
146
+ populateBuyTransactions(buyers, FLAP_PORTAL_ADDRESSES[chain], tokenAddress, fundsList)
147
+ ]);
140
148
  const buyerNonces = await allocateBuyerNonces(buyers, extractProfit, maxFundsIndex, totalProfit, nonceManager);
141
149
  const signedBuys = await signBuyTransactions({
142
150
  unsignedBuys,
@@ -176,25 +184,35 @@ export async function batchSellWithBundleMerkle(params) {
176
184
  throw new Error(getErrorMessage('SELL_KEY_AMOUNT_MISMATCH'));
177
185
  }
178
186
  const { provider, chainId } = createChainContext(chain, config.rpcUrl);
179
- const gasPrice = await resolveGasPrice(provider, config);
180
- const nonceManager = new NonceManager(provider);
181
- const signedTxs = [];
182
187
  const wallets = createWallets(privateKeys, provider);
183
188
  const amountsWei = sellAmounts.map(a => ethers.parseUnits(a, 18));
184
189
  const portalAddr = FLAP_PORTAL_ADDRESSES[chain];
185
190
  const readOnlyPortal = new ethers.Contract(portalAddr, PORTAL_ABI, provider);
186
- const quotedOutputs = await quoteSellOutputs(readOnlyPortal, tokenAddress, amountsWei);
191
+ const gasLimits = buildGasLimitList(wallets.length, config);
192
+ const signedTxs = [];
193
+ // ✅ 优化:并行执行所有 RPC 调用(gasPrice + nonces + quotes)
194
+ const [gasPrice, nonceManager, quotedOutputs] = await Promise.all([
195
+ resolveGasPrice(provider, config),
196
+ (async () => {
197
+ const nm = new NonceManager(provider);
198
+ // 预加载所有钱包的 nonce(批量查询)
199
+ await Promise.all(wallets.map(w => nm.getNextNonce(w)));
200
+ return nm;
201
+ })(),
202
+ quoteSellOutputs(readOnlyPortal, tokenAddress, amountsWei)
203
+ ]);
187
204
  const minOuts = resolveMinOutputs(minOutputAmounts, wallets.length, quotedOutputs);
188
- const portals = wallets.map(w => new ethers.Contract(portalAddr, PORTAL_ABI, w));
189
- const unsignedList = await Promise.all(portals.map((portal, i) => portal.swapExactInput.populateTransaction({
205
+ // 优化:使用单个 portal 实例生成所有 populateTransaction(避免重复创建合约实例)
206
+ const unsignedList = await Promise.all(wallets.map((_, i) => readOnlyPortal.swapExactInput.populateTransaction({
190
207
  inputToken: tokenAddress,
191
208
  outputToken: ZERO_ADDRESS,
192
209
  inputAmount: amountsWei[i],
193
210
  minOutputAmount: minOuts[i],
194
211
  permitData: '0x'
195
212
  })));
196
- const gasLimits = buildGasLimitList(wallets.length, config);
213
+ // 优化:批量获取 nonces(已预加载,直接从缓存读取)
197
214
  const nonces = await Promise.all(wallets.map(w => nonceManager.getNextNonce(w)));
215
+ // ✅ 优化:批量签名交易
198
216
  const signedList = await Promise.all(unsignedList.map((unsigned, i) => wallets[i].signTransaction({
199
217
  ...unsigned,
200
218
  from: wallets[i].address,
@@ -270,14 +288,15 @@ function findMaxIndex(values) {
270
288
  return maxIndex;
271
289
  }
272
290
  async function populateBuyTransactions(buyers, portalAddr, tokenAddress, fundsList) {
273
- const portals = buyers.map(wallet => new ethers.Contract(portalAddr, PORTAL_ABI, wallet));
274
- return await Promise.all(portals.map((portal, i) => portal.swapExactInput.populateTransaction({
291
+ // 优化:使用单个合约实例(避免重复创建)
292
+ const portal = new ethers.Contract(portalAddr, PORTAL_ABI, buyers[0]);
293
+ return await Promise.all(fundsList.map((funds) => portal.swapExactInput.populateTransaction({
275
294
  inputToken: ZERO_ADDRESS,
276
295
  outputToken: tokenAddress,
277
- inputAmount: fundsList[i],
296
+ inputAmount: funds,
278
297
  minOutputAmount: 0n,
279
298
  permitData: '0x'
280
- }, { value: fundsList[i] })));
299
+ }, { value: funds })));
281
300
  }
282
301
  function buildGasLimitList(length, config) {
283
302
  const gasLimit = getGasLimit(config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.2.75",
3
+ "version": "1.2.76",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",