four-flap-meme-sdk 1.2.74 → 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.
@@ -2,11 +2,9 @@ import { ethers, Wallet } from 'ethers';
2
2
  import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
3
3
  import { ADDRESSES } from '../../utils/constants.js';
4
4
  import { FourClient, buildLoginMessage } from '../../clients/four.js';
5
- import axios from 'axios';
6
5
  import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient } from './config.js';
7
6
  import { batchCheckAllowances } from '../../utils/erc20.js';
8
7
  import { trySell } from '../tm.js';
9
- import TM2Abi from '../../abis/TokenManager2.json' with { type: 'json' };
10
8
  const TM2_ABI = [
11
9
  'function createToken(bytes args, bytes signature) payable',
12
10
  'function buyTokenAMAP(uint256 origin, address token, address to, uint256 funds, uint256 minAmount) payable',
@@ -141,27 +139,21 @@ export async function createTokenWithBundleBuyMerkle(params) {
141
139
  }
142
140
  nonceManager.clearTemp();
143
141
  console.log(`✅ 总共生成了 ${signedTxs.length} 个交易签名 (1创建 + ${buyerKeys.length}买入 + 利润)`);
144
- // ✅ 使用 48.club eth_sendRawTransaction 捆绑提交
145
- console.log('📡 开始通过 48.club 捆绑提交交易...');
146
- const club48Endpoint = config.club48Endpoint || 'https://puissant-bsc.48.club';
142
+ // ✅ 使用公共 BSC RPC 提交交易(无 CORS 问题)
143
+ console.log('📡 开始通过公共 RPC 提交交易...');
147
144
  const txHashes = [];
148
- // ✅ 使用 eth_sendRawTransaction 逐个提交(48.club 会自动捆绑连续的交易)
145
+ // ✅ 使用 provider.broadcastTransaction 逐个提交
149
146
  for (let i = 0; i < signedTxs.length; i++) {
150
147
  try {
151
148
  const tx = signedTxs[i];
152
149
  console.log(`📤 提交第 ${i + 1}/${signedTxs.length} 个交易...`);
153
- const response = await axios.post(club48Endpoint, {
154
- jsonrpc: "2.0",
155
- id: i + 1,
156
- method: "eth_sendRawTransaction",
157
- params: [tx]
158
- });
159
- if (response.data.error) {
160
- throw new Error(`交易 ${i + 1} 提交失败: ${response.data.error.message}`);
150
+ const txResponse = await provider.broadcastTransaction(tx);
151
+ txHashes.push(txResponse.hash);
152
+ console.log(`✅ 交易 ${i + 1} 提交成功: ${txResponse.hash}`);
153
+ // 短暂延迟,避免 nonce 冲突
154
+ if (i < signedTxs.length - 1) {
155
+ await new Promise(resolve => setTimeout(resolve, 200));
161
156
  }
162
- const txHash = response.data.result;
163
- txHashes.push(txHash);
164
- console.log(`✅ 交易 ${i + 1} 提交成功: ${txHash}`);
165
157
  }
166
158
  catch (error) {
167
159
  console.error(`❌ 交易 ${i + 1} 提交失败:`, error.message);
@@ -195,18 +187,25 @@ export async function createTokenWithBundleBuyMerkle(params) {
195
187
  throw new Error('等待交易确认超时');
196
188
  }
197
189
  console.log(`✅ 创建交易已确认,区块: ${receipt.blockNumber}`);
198
- // 解析 TokenCreated 事件
190
+ // 解析 TokenCreate 事件(Four.meme 使用的事件名,注意没有 'd')
199
191
  let tokenAddress;
192
+ // 使用 TM2 ABI 解析事件
193
+ const tm2Interface = new ethers.Interface(TM2_ABI);
200
194
  for (const log of receipt.logs) {
201
195
  try {
202
- // TokenCreated(address indexed token, ...)
203
- if (log.topics[0] === ethers.id('TokenCreated(address,address,string,string,uint256,uint256,uint256)')) {
204
- 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}`);
205
204
  break;
206
205
  }
207
206
  }
208
207
  catch (e) {
209
- // 忽略解析错误
208
+ // 忽略无法解析的日志
210
209
  }
211
210
  }
212
211
  if (!tokenAddress) {
@@ -220,69 +219,24 @@ export async function createTokenWithBundleBuyMerkle(params) {
220
219
  txHashes
221
220
  };
222
221
  }
223
- /**
224
- * 等待交易上链并解析代币地址
225
- */
226
- async function waitAndParseTokenAddress(provider, createTxSigned, bundleUuid) {
227
- console.log('⏳ 等待交易上链,解析代币地址...');
228
- let tokenAddress = ZERO_ADDRESS;
229
- const maxAttempts = 30; // 最多等待 30 秒
230
- for (let i = 0; i < maxAttempts; i++) {
231
- await new Promise(resolve => setTimeout(resolve, 1000)); // 等待 1 秒
232
- try {
233
- // 获取创建交易的回执
234
- const createTxHash = ethers.keccak256(createTxSigned);
235
- const receipt = await provider.getTransactionReceipt(createTxHash);
236
- if (receipt && receipt.status === 1) {
237
- // 从事件日志中解析代币地址
238
- const tm2Interface = new ethers.Interface(TM2Abi);
239
- for (const log of receipt.logs) {
240
- try {
241
- const parsed = tm2Interface.parseLog({
242
- topics: log.topics,
243
- data: log.data
244
- });
245
- if (parsed && parsed.name === 'TokenCreate') {
246
- tokenAddress = parsed.args.token;
247
- console.log(`✅ 成功解析代币地址: ${tokenAddress}`);
248
- return tokenAddress;
249
- }
250
- }
251
- catch (e) {
252
- // 忽略无法解析的日志
253
- }
254
- }
255
- // 如果没有找到 TokenCreate 事件,尝试 TokenCreated
256
- const tokenCreatedTopic = ethers.id('TokenCreated(address,address,uint256,uint256,uint256,uint256)');
257
- for (const log of receipt.logs) {
258
- if (log.topics[0] === tokenCreatedTopic) {
259
- tokenAddress = ethers.getAddress('0x' + log.topics[1].slice(26));
260
- console.log(`✅ 成功解析代币地址: ${tokenAddress}`);
261
- return tokenAddress;
262
- }
263
- }
264
- break;
265
- }
266
- }
267
- catch (error) {
268
- // 继续等待
269
- }
270
- }
271
- if (tokenAddress === ZERO_ADDRESS) {
272
- console.warn('⚠️ 未能从事件中解析代币地址,返回 0x0');
273
- }
274
- return tokenAddress;
275
- }
276
222
  export async function batchBuyWithBundleMerkle(params) {
277
223
  const { privateKeys, buyAmounts, tokenAddress, config } = params;
278
224
  if (privateKeys.length === 0 || buyAmounts.length !== privateKeys.length) {
279
225
  throw new Error(getErrorMessage('KEY_AMOUNT_MISMATCH'));
280
226
  }
281
227
  const { provider, chainId } = createChainContext(config.rpcUrl);
282
- const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
283
- const nonceManager = new NonceManager(provider);
284
- const txType = getTxType(config);
285
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
+ ]);
286
240
  const buyFlow = await executeBuyFlow({
287
241
  wallets: buyers,
288
242
  buyAmounts,
@@ -307,10 +261,18 @@ export async function batchSellWithBundleMerkle(params) {
307
261
  throw new Error(getErrorMessage('SELL_KEY_AMOUNT_MISMATCH'));
308
262
  }
309
263
  const { provider, chainId } = createChainContext(config.rpcUrl);
310
- const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
311
- const nonceManager = new NonceManager(provider);
312
264
  const sellers = createWallets(privateKeys, provider);
313
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
+ ]);
314
276
  const sellFlow = await executeSellFlow({
315
277
  wallets: sellers,
316
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.74",
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",