four-flap-meme-sdk 1.2.70 → 1.2.72
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,6 +2,7 @@ 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 { sendBatchPrivateTransactions } from '../../clients/club48.js';
|
|
5
6
|
import { getErrorMessage, getTxType, getGasPriceConfig, shouldExtractProfit, calculateProfit, getProfitRecipient } from './config.js';
|
|
6
7
|
import { batchCheckAllowances } from '../../utils/erc20.js';
|
|
7
8
|
import { trySell } from '../tm.js';
|
|
@@ -142,23 +143,34 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
142
143
|
console.log(`✅ 总共生成了 ${signedTxs.length} 个交易签名 (1创建 + ${buyerKeys.length}买入 + 利润)`);
|
|
143
144
|
// ✅ 内部提交到 48.club Bundle
|
|
144
145
|
console.log('📡 开始提交到 48.club Bundle...');
|
|
145
|
-
|
|
146
|
+
let bundleUuid;
|
|
146
147
|
if (config.customSubmitFn) {
|
|
147
|
-
//
|
|
148
|
-
|
|
148
|
+
// ✅ 方式 1:使用自定义提交函数(通过服务器代理)
|
|
149
|
+
bundleUuid = await config.customSubmitFn(signedTxs);
|
|
149
150
|
console.log(`✅ Bundle 提交成功!UUID: ${bundleUuid}`);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
};
|
|
151
|
+
}
|
|
152
|
+
else if (config.spPrivateKey) {
|
|
153
|
+
// ✅ 方式 2:使用 48SP 批量提交(支持浏览器直接调用)
|
|
154
|
+
console.log('📡 使用 48SP 批量提交...');
|
|
155
|
+
await sendBatchPrivateTransactions(signedTxs, config.spPrivateKey, config.club48Endpoint || 'https://puissant-bsc.48.club', {
|
|
156
|
+
spMode: config.spMode,
|
|
157
|
+
spVMode: config.spVMode
|
|
158
|
+
});
|
|
159
|
+
// 批量提交不返回 bundleUuid,使用第一笔交易的 hash 作为标识
|
|
160
|
+
bundleUuid = ethers.keccak256(signedTxs[0]);
|
|
161
|
+
console.log(`✅ 48SP 批量提交成功!`);
|
|
158
162
|
}
|
|
159
163
|
else {
|
|
160
|
-
throw new Error('❌ 浏览器环境必须提供 customSubmitFn
|
|
164
|
+
throw new Error('❌ 浏览器环境必须提供 customSubmitFn 或 spPrivateKey 来提交 Bundle(避免 CORS 限制)');
|
|
161
165
|
}
|
|
166
|
+
// 等待交易上链并解析代币地址
|
|
167
|
+
const tokenAddress = await waitAndParseTokenAddress(provider, signedTxs[0], bundleUuid);
|
|
168
|
+
return {
|
|
169
|
+
signedTransactions: signedTxs,
|
|
170
|
+
tokenAddress,
|
|
171
|
+
metadata,
|
|
172
|
+
bundleUuid
|
|
173
|
+
};
|
|
162
174
|
}
|
|
163
175
|
/**
|
|
164
176
|
* 等待交易上链并解析代币地址
|
|
@@ -12,6 +12,9 @@ export type FourSignConfig = {
|
|
|
12
12
|
prefer21000ForNative?: boolean;
|
|
13
13
|
checkBnbForErc20NoHop?: boolean;
|
|
14
14
|
customSubmitFn?: (signedTxs: string[]) => Promise<string>;
|
|
15
|
+
spPrivateKey?: string;
|
|
16
|
+
spMode?: 'none' | 'timestampPersonalSign' | 'concatTxHash' | 'rawTimestamp';
|
|
17
|
+
spVMode?: '27_28' | '0_1';
|
|
15
18
|
};
|
|
16
19
|
export type FourBundleMerkleConfig = {
|
|
17
20
|
apiKey: string;
|
|
@@ -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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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) => {
|
|
@@ -263,8 +304,21 @@ export async function createTokenWithBundleBuy(params) {
|
|
|
263
304
|
// ✅ 使用自定义提交函数(通过服务器代理)
|
|
264
305
|
bundleUuid = await config.customSubmitFn(signedTxs);
|
|
265
306
|
}
|
|
307
|
+
else if (config.spPrivateKey) {
|
|
308
|
+
// ✅ 使用 48SP 批量提交(支持浏览器直接调用)
|
|
309
|
+
console.log('📡 使用 48SP 批量提交...');
|
|
310
|
+
await sendBatchPrivateTransactions(signedTxs, config.spPrivateKey, config.club48Endpoint || 'https://puissant-bsc.48.club', {
|
|
311
|
+
spMode: config.spMode ?? 'timestampPersonalSign',
|
|
312
|
+
spVMode: config.spVMode
|
|
313
|
+
});
|
|
314
|
+
// 批量提交不返回 bundleUuid,使用第一笔交易的 hash 作为标识
|
|
315
|
+
bundleUuid = ethers.keccak256(signedTxs[0]);
|
|
316
|
+
}
|
|
266
317
|
else {
|
|
267
|
-
// ✅ 直接提交到 48.club(仅服务器端可用)
|
|
318
|
+
// ✅ 直接提交到 48.club Bundle(仅服务器端可用)
|
|
319
|
+
if (!club48) {
|
|
320
|
+
throw new Error('❌ 浏览器环境必须提供 customSubmitFn 或 spPrivateKey 来提交 Bundle(避免 CORS 限制)');
|
|
321
|
+
}
|
|
268
322
|
bundleUuid = await club48.sendBundle({
|
|
269
323
|
txs: signedTxs,
|
|
270
324
|
maxBlockNumber: (await provider.getBlockNumber()) + (config.bundleBlockOffset ?? 100),
|
|
@@ -276,7 +330,14 @@ export async function createTokenWithBundleBuy(params) {
|
|
|
276
330
|
});
|
|
277
331
|
}
|
|
278
332
|
// 6. 等待完成
|
|
279
|
-
|
|
333
|
+
let status;
|
|
334
|
+
if (club48) {
|
|
335
|
+
status = await club48.waitForBundle(bundleUuid);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
// 使用 provider 等待交易上链
|
|
339
|
+
status = await waitForBundleWithProvider(provider, signedTxs[0], bundleUuid);
|
|
340
|
+
}
|
|
280
341
|
// 7. 解析 TokenCreate 事件获取创建的代币地址
|
|
281
342
|
let createdTokenAddress;
|
|
282
343
|
if (status.status === 'INCLUDED' || status.status === 'PENDING') {
|