four-flap-meme-sdk 1.2.69 → 1.2.71

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,6 +5,7 @@ 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' };
8
9
  const TM2_ABI = [
9
10
  'function createToken(bytes args, bytes signature) payable',
10
11
  'function buyTokenAMAP(uint256 origin, address token, address to, uint256 funds, uint256 minAmount) payable',
@@ -139,13 +140,78 @@ export async function createTokenWithBundleBuyMerkle(params) {
139
140
  }
140
141
  nonceManager.clearTemp();
141
142
  console.log(`✅ 总共生成了 ${signedTxs.length} 个交易签名 (1创建 + ${buyerKeys.length}买入 + 利润)`);
142
- // ✅ 返回签名交易,由服务器端提交到 48.club Bundle
143
- // 注意:浏览器无法直接调用 48.club Bundle API(CORS 限制)
144
- return {
145
- signedTransactions: signedTxs,
146
- tokenAddress: predictedTokenAddress, // 返回 0x0,服务器提交后需要从事件中解析真实地址
147
- metadata
148
- };
143
+ // ✅ 内部提交到 48.club Bundle
144
+ console.log('📡 开始提交到 48.club Bundle...');
145
+ // 检查是否有自定义提交函数
146
+ if (config.customSubmitFn) {
147
+ // 使用自定义提交函数(通过服务器代理)
148
+ const bundleUuid = await config.customSubmitFn(signedTxs);
149
+ console.log(`✅ Bundle 提交成功!UUID: ${bundleUuid}`);
150
+ // 等待交易上链并解析代币地址
151
+ const tokenAddress = await waitAndParseTokenAddress(provider, signedTxs[0], bundleUuid);
152
+ return {
153
+ signedTransactions: signedTxs,
154
+ tokenAddress,
155
+ metadata,
156
+ bundleUuid
157
+ };
158
+ }
159
+ else {
160
+ throw new Error('❌ 浏览器环境必须提供 customSubmitFn 来通过服务器代理提交 Bundle(避免 CORS 限制)');
161
+ }
162
+ }
163
+ /**
164
+ * 等待交易上链并解析代币地址
165
+ */
166
+ async function waitAndParseTokenAddress(provider, createTxSigned, bundleUuid) {
167
+ console.log('⏳ 等待交易上链,解析代币地址...');
168
+ let tokenAddress = ZERO_ADDRESS;
169
+ const maxAttempts = 30; // 最多等待 30 秒
170
+ for (let i = 0; i < maxAttempts; i++) {
171
+ await new Promise(resolve => setTimeout(resolve, 1000)); // 等待 1 秒
172
+ try {
173
+ // 获取创建交易的回执
174
+ const createTxHash = ethers.keccak256(createTxSigned);
175
+ const receipt = await provider.getTransactionReceipt(createTxHash);
176
+ if (receipt && receipt.status === 1) {
177
+ // 从事件日志中解析代币地址
178
+ const tm2Interface = new ethers.Interface(TM2Abi);
179
+ for (const log of receipt.logs) {
180
+ try {
181
+ const parsed = tm2Interface.parseLog({
182
+ topics: log.topics,
183
+ data: log.data
184
+ });
185
+ if (parsed && parsed.name === 'TokenCreate') {
186
+ tokenAddress = parsed.args.token;
187
+ console.log(`✅ 成功解析代币地址: ${tokenAddress}`);
188
+ return tokenAddress;
189
+ }
190
+ }
191
+ catch (e) {
192
+ // 忽略无法解析的日志
193
+ }
194
+ }
195
+ // 如果没有找到 TokenCreate 事件,尝试 TokenCreated
196
+ const tokenCreatedTopic = ethers.id('TokenCreated(address,address,uint256,uint256,uint256,uint256)');
197
+ for (const log of receipt.logs) {
198
+ if (log.topics[0] === tokenCreatedTopic) {
199
+ tokenAddress = ethers.getAddress('0x' + log.topics[1].slice(26));
200
+ console.log(`✅ 成功解析代币地址: ${tokenAddress}`);
201
+ return tokenAddress;
202
+ }
203
+ }
204
+ break;
205
+ }
206
+ }
207
+ catch (error) {
208
+ // 继续等待
209
+ }
210
+ }
211
+ if (tokenAddress === ZERO_ADDRESS) {
212
+ console.warn('⚠️ 未能从事件中解析代币地址,返回 0x0');
213
+ }
214
+ return tokenAddress;
149
215
  }
150
216
  export async function batchBuyWithBundleMerkle(params) {
151
217
  const { privateKeys, buyAmounts, tokenAddress, config } = params;
@@ -11,6 +11,7 @@ export type FourSignConfig = {
11
11
  chainId?: number;
12
12
  prefer21000ForNative?: boolean;
13
13
  checkBnbForErc20NoHop?: boolean;
14
+ customSubmitFn?: (signedTxs: string[]) => Promise<string>;
14
15
  };
15
16
  export type FourBundleMerkleConfig = {
16
17
  apiKey: string;
@@ -66,6 +67,8 @@ export type MerkleSignedResult = {
66
67
  metadata?: {
67
68
  [key: string]: any;
68
69
  };
70
+ /** Bundle UUID(内部提交时返回) */
71
+ bundleUuid?: string;
69
72
  };
70
73
  /** ✅ 创建代币 + 购买参数(Merkle 版本 - 完整) */
71
74
  export type FourCreateWithBundleBuyMerkleParams = {
@@ -38,6 +38,34 @@ function getErrorMessage(key, ...args) {
38
38
  return typeof message === 'function' ? message(...args) : message;
39
39
  }
40
40
  // ========================================
41
+ // 辅助函数
42
+ // ========================================
43
+ /**
44
+ * 等待交易上链(不使用 club48 客户端)
45
+ */
46
+ async function waitForBundleWithProvider(provider, firstTxSigned, bundleUuid) {
47
+ const maxAttempts = 60; // 最多等待 60 秒
48
+ for (let i = 0; i < maxAttempts; i++) {
49
+ await new Promise(resolve => setTimeout(resolve, 1000));
50
+ try {
51
+ const txHash = ethers.keccak256(firstTxSigned);
52
+ const receipt = await provider.getTransactionReceipt(txHash);
53
+ if (receipt) {
54
+ if (receipt.status === 1) {
55
+ return { status: 'INCLUDED', includedBlock: receipt.blockNumber };
56
+ }
57
+ else {
58
+ return { status: 'FAILED' };
59
+ }
60
+ }
61
+ }
62
+ catch (error) {
63
+ // 继续等待
64
+ }
65
+ }
66
+ return { status: 'PENDING' };
67
+ }
68
+ // ========================================
41
69
  // 辅助函数:估算 gas 并应用配置
42
70
  // ========================================
43
71
  /**
@@ -87,10 +115,14 @@ export async function createTokenWithBundleBuy(params) {
87
115
  }
88
116
  const provider = new JsonRpcProvider(config.rpcUrl);
89
117
  const devWallet = new Wallet(privateKeys[0], provider);
90
- const club48 = new Club48Client({
91
- endpoint: 'https://puissant-bsc.48.club',
92
- explorerEndpoint: config.club48ExplorerEndpoint
93
- });
118
+ // 只在没有 customSubmitFn 时创建 club48 客户端
119
+ let club48 = null;
120
+ if (!config.customSubmitFn) {
121
+ club48 = new Club48Client({
122
+ endpoint: 'https://puissant-bsc.48.club',
123
+ explorerEndpoint: config.club48ExplorerEndpoint
124
+ });
125
+ }
94
126
  const fourClient = new FourClient({ baseUrl: config.fourApiUrl });
95
127
  // 1. 登录 four.meme
96
128
  const nonce = await fourClient.generateNonce({
@@ -144,7 +176,16 @@ export async function createTokenWithBundleBuy(params) {
144
176
  });
145
177
  // 4. 构建交易
146
178
  const tmAddr = ADDRESSES.BSC.TokenManagerV2Proxy;
147
- const gasPrice = await club48.getMinGasPrice();
179
+ // 获取 gas price
180
+ let gasPrice;
181
+ if (club48) {
182
+ gasPrice = await club48.getMinGasPrice();
183
+ }
184
+ else {
185
+ // 使用 provider 获取 gas price 并增加 20%
186
+ const currentGasPrice = (await provider.getFeeData()).gasPrice || 3000000000n;
187
+ gasPrice = (currentGasPrice * 120n) / 100n;
188
+ }
148
189
  const signedTxs = [];
149
190
  const nextNonceMap = new Map();
150
191
  const getNextNonce = async (w) => {
@@ -265,6 +306,9 @@ export async function createTokenWithBundleBuy(params) {
265
306
  }
266
307
  else {
267
308
  // ✅ 直接提交到 48.club(仅服务器端可用)
309
+ if (!club48) {
310
+ throw new Error('❌ club48 客户端未初始化');
311
+ }
268
312
  bundleUuid = await club48.sendBundle({
269
313
  txs: signedTxs,
270
314
  maxBlockNumber: (await provider.getBlockNumber()) + (config.bundleBlockOffset ?? 100),
@@ -276,7 +320,14 @@ export async function createTokenWithBundleBuy(params) {
276
320
  });
277
321
  }
278
322
  // 6. 等待完成
279
- const status = await club48.waitForBundle(bundleUuid);
323
+ let status;
324
+ if (club48) {
325
+ status = await club48.waitForBundle(bundleUuid);
326
+ }
327
+ else {
328
+ // 使用 provider 等待交易上链
329
+ status = await waitForBundleWithProvider(provider, signedTxs[0], bundleUuid);
330
+ }
280
331
  // 7. 解析 TokenCreate 事件获取创建的代币地址
281
332
  let createdTokenAddress;
282
333
  if (status.status === 'INCLUDED' || status.status === 'PENDING') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "four-flap-meme-sdk",
3
- "version": "1.2.69",
3
+ "version": "1.2.71",
4
4
  "description": "SDK for Flap bonding curve and four.meme TokenManager",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",