four-flap-meme-sdk 1.3.78 → 1.3.80
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.
- package/dist/clients/blockrazor.d.ts +5 -3
- package/dist/clients/blockrazor.js +47 -29
- package/dist/contracts/tm-bundle-merkle/core.js +0 -7
- package/dist/contracts/tm-bundle-merkle/submit.d.ts +0 -7
- package/dist/contracts/tm-bundle-merkle/submit.js +1 -22
- package/dist/contracts/tm-bundle-merkle/swap.js +0 -4
- package/dist/contracts/tm-bundle.js +0 -2
- package/dist/dex/direct-router.js +0 -13
- package/dist/flap/portal-bundle-merkle/core.js +11 -34
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +0 -4
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +1 -20
- package/dist/flap/portal-bundle-merkle/swap.js +0 -8
- package/dist/pancake/bundle-buy-first.js +0 -2
- package/dist/pancake/bundle-swap.js +1 -16
- package/dist/utils/lp-inspect.js +0 -7
- package/package.json +1 -1
|
@@ -138,9 +138,9 @@ export interface IncentiveTransactionParams {
|
|
|
138
138
|
*/
|
|
139
139
|
export declare class BlockRazorClient {
|
|
140
140
|
private provider;
|
|
141
|
-
private builderProvider;
|
|
142
141
|
private chainId;
|
|
143
142
|
private apiKey?;
|
|
143
|
+
private builderUrl;
|
|
144
144
|
private blockNumberCache;
|
|
145
145
|
private static BLOCK_CACHE_TTL_MS;
|
|
146
146
|
constructor(config: BlockRazorConfig);
|
|
@@ -149,9 +149,9 @@ export declare class BlockRazorClient {
|
|
|
149
149
|
*/
|
|
150
150
|
getProvider(): JsonRpcProvider;
|
|
151
151
|
/**
|
|
152
|
-
* 获取 Builder
|
|
152
|
+
* 获取 Builder URL
|
|
153
153
|
*/
|
|
154
|
-
|
|
154
|
+
getBuilderUrl(): string;
|
|
155
155
|
/**
|
|
156
156
|
* 获取当前区块号(带缓存)
|
|
157
157
|
*/
|
|
@@ -172,6 +172,8 @@ export declare class BlockRazorClient {
|
|
|
172
172
|
/**
|
|
173
173
|
* 发送捆绑交易(底层方法)
|
|
174
174
|
*
|
|
175
|
+
* ✅ 使用 fetch 直接发送请求,支持 Authorization 头
|
|
176
|
+
*
|
|
175
177
|
* @param params Bundle 参数
|
|
176
178
|
* @returns Bundle Hash
|
|
177
179
|
*/
|
|
@@ -37,9 +37,10 @@ import { JsonRpcProvider, Transaction, ethers } from 'ethers';
|
|
|
37
37
|
export const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
38
38
|
/**
|
|
39
39
|
* BlockRazor BSC RPC 端点
|
|
40
|
+
* 来源: 测试文件 008_blockrazor_smoke_test.ts
|
|
40
41
|
*/
|
|
41
42
|
const BLOCKRAZOR_RPC_ENDPOINTS = {
|
|
42
|
-
BSC: 'https://
|
|
43
|
+
BSC: 'https://rpc.blockrazor.builders',
|
|
43
44
|
// 可以添加其他链的端点
|
|
44
45
|
};
|
|
45
46
|
/**
|
|
@@ -64,12 +65,9 @@ export class BlockRazorClient {
|
|
|
64
65
|
chainId: this.chainId,
|
|
65
66
|
name: 'bsc',
|
|
66
67
|
});
|
|
67
|
-
// Builder RPC(用于发送 Bundle)
|
|
68
|
-
|
|
69
|
-
this.
|
|
70
|
-
chainId: this.chainId,
|
|
71
|
-
name: 'bsc',
|
|
72
|
-
});
|
|
68
|
+
// Builder RPC URL(用于发送 Bundle)
|
|
69
|
+
// ✅ 正确的端点: https://rpc.blockrazor.builders
|
|
70
|
+
this.builderUrl = config.builderRpcUrl || BLOCKRAZOR_RPC_ENDPOINTS.BSC;
|
|
73
71
|
}
|
|
74
72
|
/**
|
|
75
73
|
* 获取 Provider
|
|
@@ -78,10 +76,10 @@ export class BlockRazorClient {
|
|
|
78
76
|
return this.provider;
|
|
79
77
|
}
|
|
80
78
|
/**
|
|
81
|
-
* 获取 Builder
|
|
79
|
+
* 获取 Builder URL
|
|
82
80
|
*/
|
|
83
|
-
|
|
84
|
-
return this.
|
|
81
|
+
getBuilderUrl() {
|
|
82
|
+
return this.builderUrl;
|
|
85
83
|
}
|
|
86
84
|
/**
|
|
87
85
|
* 获取当前区块号(带缓存)
|
|
@@ -137,6 +135,8 @@ export class BlockRazorClient {
|
|
|
137
135
|
/**
|
|
138
136
|
* 发送捆绑交易(底层方法)
|
|
139
137
|
*
|
|
138
|
+
* ✅ 使用 fetch 直接发送请求,支持 Authorization 头
|
|
139
|
+
*
|
|
140
140
|
* @param params Bundle 参数
|
|
141
141
|
* @returns Bundle Hash
|
|
142
142
|
*/
|
|
@@ -178,24 +178,45 @@ export class BlockRazorClient {
|
|
|
178
178
|
bundleParams.noMerge = params.noMerge;
|
|
179
179
|
}
|
|
180
180
|
try {
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
// ✅ 使用 fetch 发送请求,支持 Authorization 头
|
|
182
|
+
const requestBody = {
|
|
183
|
+
jsonrpc: '2.0',
|
|
184
|
+
id: '1',
|
|
185
|
+
method: 'eth_sendBundle',
|
|
186
|
+
params: [bundleParams]
|
|
187
|
+
};
|
|
188
|
+
const headers = {
|
|
189
|
+
'Content-Type': 'application/json',
|
|
190
|
+
};
|
|
191
|
+
// 如果有 API Key,添加 Authorization 头
|
|
192
|
+
if (this.apiKey) {
|
|
193
|
+
headers['Authorization'] = this.apiKey;
|
|
187
194
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
const response = await fetch(this.builderUrl, {
|
|
196
|
+
method: 'POST',
|
|
197
|
+
headers,
|
|
198
|
+
body: JSON.stringify(requestBody),
|
|
199
|
+
});
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
const text = await response.text();
|
|
202
|
+
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
203
|
+
}
|
|
204
|
+
const result = await response.json();
|
|
205
|
+
// 检查 JSON-RPC 错误
|
|
206
|
+
if (result.error) {
|
|
207
|
+
throw new Error(`RPC Error: ${result.error.message || JSON.stringify(result.error)}`);
|
|
208
|
+
}
|
|
209
|
+
// 提取 bundle hash
|
|
210
|
+
const bundleHash = result.result;
|
|
211
|
+
if (typeof bundleHash === 'string') {
|
|
212
|
+
return bundleHash;
|
|
213
|
+
}
|
|
214
|
+
if (bundleHash && typeof bundleHash === 'object') {
|
|
215
|
+
if ('bundleHash' in bundleHash) {
|
|
216
|
+
return String(bundleHash.bundleHash);
|
|
196
217
|
}
|
|
197
218
|
}
|
|
198
|
-
|
|
219
|
+
return JSON.stringify(result.result || result);
|
|
199
220
|
}
|
|
200
221
|
catch (error) {
|
|
201
222
|
const errorMsg = error.message || String(error);
|
|
@@ -213,7 +234,7 @@ export class BlockRazorClient {
|
|
|
213
234
|
if (!options.transactions || options.transactions.length === 0) {
|
|
214
235
|
throw new Error('Transactions array cannot be empty');
|
|
215
236
|
}
|
|
216
|
-
const blockOffset = options.blockOffset ??
|
|
237
|
+
const blockOffset = options.blockOffset ?? 100;
|
|
217
238
|
const maxBlockOffset = options.maxBlockOffset ?? 100;
|
|
218
239
|
const autoRetry = options.autoRetry ?? false;
|
|
219
240
|
const maxRetries = options.maxRetries ?? 3;
|
|
@@ -226,7 +247,6 @@ export class BlockRazorClient {
|
|
|
226
247
|
// 计算最大有效区块号
|
|
227
248
|
const actualOffset = Math.min(blockOffset, maxBlockOffset);
|
|
228
249
|
const maxBlockNumber = currentBlock + actualOffset;
|
|
229
|
-
console.log(`📊 [BlockRazor] 当前区块: ${currentBlock}, 最大有效区块: ${maxBlockNumber}`);
|
|
230
250
|
// 发送 Bundle
|
|
231
251
|
const bundleHash = await this.sendBundleRaw({
|
|
232
252
|
txs: options.transactions,
|
|
@@ -256,7 +276,6 @@ export class BlockRazorClient {
|
|
|
256
276
|
lastError = error;
|
|
257
277
|
if (autoRetry && attempt < maxRetries) {
|
|
258
278
|
attempt++;
|
|
259
|
-
console.log(`🔄 [BlockRazor] 重试 ${attempt}/${maxRetries}...`);
|
|
260
279
|
await new Promise(resolve => setTimeout(resolve, 3000)); // 等待一个区块
|
|
261
280
|
continue;
|
|
262
281
|
}
|
|
@@ -283,7 +302,6 @@ export class BlockRazorClient {
|
|
|
283
302
|
});
|
|
284
303
|
// 将激励交易添加到 Bundle 末尾
|
|
285
304
|
const allTransactions = [...options.transactions, incentiveTx];
|
|
286
|
-
console.log(`💰 [BlockRazor] 添加激励交易: ${incentive.amount} BNB -> ${BLOCKRAZOR_BUILDER_EOA}`);
|
|
287
305
|
return await this.sendBundle({
|
|
288
306
|
...options,
|
|
289
307
|
transactions: allTransactions,
|
|
@@ -82,7 +82,6 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
82
82
|
funGroup: false,
|
|
83
83
|
clickFun: false,
|
|
84
84
|
});
|
|
85
|
-
console.log('🔍 Four.meme createToken 响应:', JSON.stringify(createResp, null, 2));
|
|
86
85
|
const gasPrice = await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
87
86
|
const nonceManager = new NonceManager(provider);
|
|
88
87
|
const txType = getTxType(config);
|
|
@@ -99,7 +98,6 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
99
98
|
const { remaining, profit } = calculateProfit(originalBuyAmount, config);
|
|
100
99
|
actualBuyFunds = remaining;
|
|
101
100
|
profitAmount = profit;
|
|
102
|
-
console.log(`💰 利润刮取: 原始 ${ethers.formatEther(originalBuyAmount)} BNB → 实际买入 ${ethers.formatEther(actualBuyFunds)} BNB + 利润 ${ethers.formatEther(profitAmount)} BNB`);
|
|
103
101
|
}
|
|
104
102
|
else {
|
|
105
103
|
actualBuyFunds = originalBuyAmount;
|
|
@@ -124,7 +122,6 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
124
122
|
value: valueWei
|
|
125
123
|
};
|
|
126
124
|
signedTxs.push(await devWallet.signTransaction(createTxRequest));
|
|
127
|
-
console.log(`✅ 生成创建+买入交易: value = ${ethers.formatEther(valueWei)} BNB (0.01创建 + ${ethers.formatEther(actualBuyFunds)}买入)`);
|
|
128
125
|
// ✅ 添加利润转账交易
|
|
129
126
|
if (extractProfit && profitAmount > 0n) {
|
|
130
127
|
const profitNonce = await nonceManager.getNextNonce(devWallet);
|
|
@@ -138,13 +135,9 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
138
135
|
type: txType
|
|
139
136
|
});
|
|
140
137
|
signedTxs.push(profitTx);
|
|
141
|
-
console.log(`💸 利润转账交易已生成: ${ethers.formatEther(profitAmount)} BNB → ${getProfitRecipient()}`);
|
|
142
138
|
}
|
|
143
139
|
nonceManager.clearTemp();
|
|
144
|
-
console.log(`✅ 总共生成了 ${signedTxs.length} 个交易签名 (1创建+买入${extractProfit && profitAmount > 0n ? ' + 1利润' : ''})`);
|
|
145
140
|
// ⚠️ 只返回签名交易,不提交
|
|
146
|
-
console.log('📦 交易已签名完成');
|
|
147
|
-
console.log('💡 提示: 可以通过 Merkle Bundle 提交,或直接 broadcastTransaction');
|
|
148
141
|
// 构建元数据
|
|
149
142
|
const metadata = extractProfit && profitAmount > 0n ? {
|
|
150
143
|
totalBuyAmount: ethers.formatEther(originalBuyAmount),
|
|
@@ -63,8 +63,6 @@ export interface SubmitBundleResult {
|
|
|
63
63
|
* });
|
|
64
64
|
*
|
|
65
65
|
* if (result.code) {
|
|
66
|
-
* console.log('✅ Bundle提交成功:', result.bundleHash);
|
|
67
|
-
* console.log('交易哈希:', result.txHashes);
|
|
68
66
|
* } else {
|
|
69
67
|
* console.error('❌ Bundle提交失败:', result.error);
|
|
70
68
|
* }
|
|
@@ -156,9 +154,6 @@ export interface BlockRazorSubmitResult {
|
|
|
156
154
|
* });
|
|
157
155
|
*
|
|
158
156
|
* if (result.code) {
|
|
159
|
-
* console.log('✅ Bundle 提交成功:', result.bundleHash);
|
|
160
|
-
* console.log('交易哈希:', result.txHashes);
|
|
161
|
-
* console.log('最大有效区块:', result.maxBlockNumber);
|
|
162
157
|
* } else {
|
|
163
158
|
* console.error('❌ Bundle 提交失败:', result.error);
|
|
164
159
|
* }
|
|
@@ -249,8 +244,6 @@ export interface DirectSubmitResult {
|
|
|
249
244
|
* });
|
|
250
245
|
*
|
|
251
246
|
* if (result.code) {
|
|
252
|
-
* console.log('✅ 广播成功:', result.successCount, '/', result.totalTransactions);
|
|
253
|
-
* console.log('交易哈希:', result.txHashes);
|
|
254
247
|
* } else {
|
|
255
248
|
* console.error('❌ 全部广播失败:', result.errorSummary);
|
|
256
249
|
* }
|
|
@@ -50,8 +50,6 @@ function getMerkleClient(config) {
|
|
|
50
50
|
* });
|
|
51
51
|
*
|
|
52
52
|
* if (result.code) {
|
|
53
|
-
* console.log('✅ Bundle提交成功:', result.bundleHash);
|
|
54
|
-
* console.log('交易哈希:', result.txHashes);
|
|
55
53
|
* } else {
|
|
56
54
|
* console.error('❌ Bundle提交失败:', result.error);
|
|
57
55
|
* }
|
|
@@ -179,9 +177,6 @@ function getBlockRazorClient(config) {
|
|
|
179
177
|
* });
|
|
180
178
|
*
|
|
181
179
|
* if (result.code) {
|
|
182
|
-
* console.log('✅ Bundle 提交成功:', result.bundleHash);
|
|
183
|
-
* console.log('交易哈希:', result.txHashes);
|
|
184
|
-
* console.log('最大有效区块:', result.maxBlockNumber);
|
|
185
180
|
* } else {
|
|
186
181
|
* console.error('❌ Bundle 提交失败:', result.error);
|
|
187
182
|
* }
|
|
@@ -200,18 +195,16 @@ export async function submitBundleToBlockRazor(signedTransactions, config) {
|
|
|
200
195
|
}
|
|
201
196
|
// ✅ 使用缓存的 BlockRazorClient
|
|
202
197
|
const client = getBlockRazorClient(config);
|
|
203
|
-
console.log(`📦 [BlockRazor] 提交 ${totalTransactions} 笔交易...`);
|
|
204
198
|
// 提交 Bundle
|
|
205
199
|
const bundleResult = await client.sendBundle({
|
|
206
200
|
transactions: signedTransactions,
|
|
207
|
-
blockOffset: config.blockOffset ??
|
|
201
|
+
blockOffset: config.blockOffset ?? 100,
|
|
208
202
|
maxBlockOffset: config.maxBlockOffset ?? 100,
|
|
209
203
|
noMerge: config.noMerge ?? false,
|
|
210
204
|
revertingTxHashes: config.revertingTxHashes,
|
|
211
205
|
autoRetry: config.autoRetry ?? false,
|
|
212
206
|
maxRetries: config.maxRetries ?? 3
|
|
213
207
|
});
|
|
214
|
-
console.log(`✅ [BlockRazor] 提交成功: ${bundleResult.bundleHash}`);
|
|
215
208
|
// ✅ 提交成功
|
|
216
209
|
return {
|
|
217
210
|
code: true,
|
|
@@ -223,7 +216,6 @@ export async function submitBundleToBlockRazor(signedTransactions, config) {
|
|
|
223
216
|
};
|
|
224
217
|
}
|
|
225
218
|
catch (error) {
|
|
226
|
-
console.error(`❌ [BlockRazor] 提交失败:`, error?.message || error);
|
|
227
219
|
// ❌ 提交失败
|
|
228
220
|
return {
|
|
229
221
|
code: false,
|
|
@@ -279,8 +271,6 @@ export async function submitMultipleBundlesToBlockRazorParallel(bundles, config)
|
|
|
279
271
|
* });
|
|
280
272
|
*
|
|
281
273
|
* if (result.code) {
|
|
282
|
-
* console.log('✅ 广播成功:', result.successCount, '/', result.totalTransactions);
|
|
283
|
-
* console.log('交易哈希:', result.txHashes);
|
|
284
274
|
* } else {
|
|
285
275
|
* console.error('❌ 全部广播失败:', result.errorSummary);
|
|
286
276
|
* }
|
|
@@ -333,7 +323,6 @@ export async function submitDirectToRpc(signedTransactions, config) {
|
|
|
333
323
|
txHash: txResponse.hash
|
|
334
324
|
});
|
|
335
325
|
txHashes.push(txResponse.hash);
|
|
336
|
-
console.log(`✅ [${chainName}] 交易 ${i + 1}/${totalTransactions} 广播成功: ${txResponse.hash}`);
|
|
337
326
|
// 如果需要等待确认
|
|
338
327
|
if (config.waitForConfirmation) {
|
|
339
328
|
try {
|
|
@@ -417,12 +406,9 @@ export async function submitDirectToRpcSequential(signedTransactions, config) {
|
|
|
417
406
|
for (let i = 0; i < signedTransactions.length; i++) {
|
|
418
407
|
const signedTx = signedTransactions[i];
|
|
419
408
|
try {
|
|
420
|
-
console.log(`📤 [${chainName}] 广播交易 ${i + 1}/${totalTransactions}...`);
|
|
421
409
|
// 广播交易
|
|
422
410
|
const txResponse = await provider.broadcastTransaction(signedTx);
|
|
423
|
-
console.log(`✅ [${chainName}] 交易 ${i + 1} 已广播: ${txResponse.hash}`);
|
|
424
411
|
// ✅ 等待交易确认
|
|
425
|
-
console.log(`⏳ [${chainName}] 等待交易 ${i + 1} 确认...`);
|
|
426
412
|
const receipt = await provider.waitForTransaction(txResponse.hash, 1, // 等待1个确认
|
|
427
413
|
confirmationTimeout);
|
|
428
414
|
if (receipt && receipt.status === 1) {
|
|
@@ -432,7 +418,6 @@ export async function submitDirectToRpcSequential(signedTransactions, config) {
|
|
|
432
418
|
txHash: txResponse.hash
|
|
433
419
|
});
|
|
434
420
|
txHashes.push(txResponse.hash);
|
|
435
|
-
console.log(`✅ [${chainName}] 交易 ${i + 1} 已确认: ${txResponse.hash}`);
|
|
436
421
|
}
|
|
437
422
|
else {
|
|
438
423
|
const errorMsg = `交易执行失败(status=${receipt?.status})`;
|
|
@@ -443,9 +428,7 @@ export async function submitDirectToRpcSequential(signedTransactions, config) {
|
|
|
443
428
|
error: errorMsg
|
|
444
429
|
});
|
|
445
430
|
errors.push(`交易 ${i + 1}: ${errorMsg}`);
|
|
446
|
-
console.error(`❌ [${chainName}] 交易 ${i + 1} 失败: ${errorMsg}`);
|
|
447
431
|
// ✅ 多跳场景:如果某笔失败,后续交易可能也会失败,继续尝试但标记
|
|
448
|
-
console.warn(`⚠️ [${chainName}] 交易 ${i + 1} 失败,继续尝试后续交易...`);
|
|
449
432
|
}
|
|
450
433
|
}
|
|
451
434
|
catch (error) {
|
|
@@ -456,14 +439,11 @@ export async function submitDirectToRpcSequential(signedTransactions, config) {
|
|
|
456
439
|
error: errorMessage
|
|
457
440
|
});
|
|
458
441
|
errors.push(`交易 ${i + 1}: ${errorMessage}`);
|
|
459
|
-
console.error(`❌ [${chainName}] 交易 ${i + 1}/${totalTransactions} 失败:`, errorMessage);
|
|
460
442
|
// ✅ 多跳场景:如果某笔失败,后续交易可能也会失败,继续尝试
|
|
461
|
-
console.warn(`⚠️ [${chainName}] 继续尝试后续交易...`);
|
|
462
443
|
}
|
|
463
444
|
}
|
|
464
445
|
const successCount = txHashes.length;
|
|
465
446
|
const failedCount = totalTransactions - successCount;
|
|
466
|
-
console.log(`📊 [${chainName}] 顺序广播完成: ${successCount}/${totalTransactions} 成功`);
|
|
467
447
|
return {
|
|
468
448
|
code: successCount > 0,
|
|
469
449
|
totalTransactions,
|
|
@@ -536,7 +516,6 @@ export async function submitDirectToRpcParallel(signedTransactions, config) {
|
|
|
536
516
|
const errors = results.filter(r => !r.success).map(r => `交易 ${r.index + 1}: ${r.error}`);
|
|
537
517
|
const successCount = txHashes.length;
|
|
538
518
|
const failedCount = totalTransactions - successCount;
|
|
539
|
-
console.log(`📤 [${chainName}] 并行广播完成: ${successCount}/${totalTransactions} 成功`);
|
|
540
519
|
return {
|
|
541
520
|
code: successCount > 0,
|
|
542
521
|
totalTransactions,
|
|
@@ -202,7 +202,6 @@ export async function fourBatchSwapMerkle(params) {
|
|
|
202
202
|
const seller = new Wallet(sellerPrivateKey, provider);
|
|
203
203
|
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, provider));
|
|
204
204
|
const nonceManager = new NonceManager(provider);
|
|
205
|
-
console.log('🔍 Four BatchSwap - 买方数量:', buyerPrivateKeys.length);
|
|
206
205
|
// 创建适配的配置对象
|
|
207
206
|
const bundleConfig = {
|
|
208
207
|
minGasPriceGwei: config.minGasPriceGwei,
|
|
@@ -233,10 +232,8 @@ export async function fourBatchSwapMerkle(params) {
|
|
|
233
232
|
let buyAmountsWei;
|
|
234
233
|
if (params.buyerRatios && params.buyerRatios.length === buyers.length) {
|
|
235
234
|
// 按比例分配
|
|
236
|
-
console.log('📊 使用比例分配,卖出所得:', ethers.formatEther(totalBuyerFunds), 'BNB');
|
|
237
235
|
buyAmountsWei = params.buyerRatios.map((ratio, index) => {
|
|
238
236
|
const amount = (totalBuyerFunds * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
239
|
-
console.log(` 买方 ${index + 1}: ${(ratio * 100).toFixed(1)}% = ${ethers.formatEther(amount)} BNB`);
|
|
240
237
|
return amount;
|
|
241
238
|
});
|
|
242
239
|
}
|
|
@@ -363,7 +360,6 @@ export async function fourBatchSwapMerkle(params) {
|
|
|
363
360
|
signedTransactions.push(...signedBuys);
|
|
364
361
|
if (profitTx)
|
|
365
362
|
signedTransactions.push(profitTx);
|
|
366
|
-
console.log(`✅ Four 批量换手签名完成: ${signedTransactions.length} 笔交易`);
|
|
367
363
|
return {
|
|
368
364
|
signedTransactions,
|
|
369
365
|
metadata: {
|
|
@@ -305,8 +305,6 @@ export async function createTokenWithBundleBuy(params) {
|
|
|
305
305
|
bundleUuid = await config.customSubmitFn(signedTxs);
|
|
306
306
|
}
|
|
307
307
|
else if (config.spPrivateKey) {
|
|
308
|
-
// ✅ 使用 48SP 批量提交(支持浏览器直接调用)
|
|
309
|
-
console.log('📡 使用 48SP 批量提交...');
|
|
310
308
|
await sendBatchPrivateTransactions(signedTxs, config.spPrivateKey, config.club48Endpoint || 'https://puissant-bsc.48.club', {
|
|
311
309
|
spMode: config.spMode ?? 'timestampPersonalSign',
|
|
312
310
|
spVMode: config.spVMode
|
|
@@ -545,7 +545,6 @@ async function getTokenToNativeQuote(provider, tokenAddress, tokenAmount, chain)
|
|
|
545
545
|
const router = new Contract(config.router, QUOTE_ROUTER_ABI, provider);
|
|
546
546
|
const amounts = await router.getAmountsOut(tokenAmount, [tokenAddress, config.wrappedNative]);
|
|
547
547
|
const nativeAmount = amounts[1];
|
|
548
|
-
console.log(`[getTokenToNativeQuote] ${ethers.formatEther(tokenAmount)} Token → ${ethers.formatEther(nativeAmount)} Native`);
|
|
549
548
|
return nativeAmount;
|
|
550
549
|
}
|
|
551
550
|
catch (error) {
|
|
@@ -740,10 +739,8 @@ export async function directV2BatchBuy(params) {
|
|
|
740
739
|
const nativeProfitWei = await getTokenToNativeQuote(provider, quoteToken, profitWei, chain);
|
|
741
740
|
if (nativeProfitWei > 0n) {
|
|
742
741
|
profitWei = nativeProfitWei;
|
|
743
|
-
console.log(`[V2 Buy] ERC20 利润转换: ${ethers.formatEther(calculateProfitAmount(totalFlowWei))} Token → ${ethers.formatEther(profitWei)} Native`);
|
|
744
742
|
}
|
|
745
743
|
else {
|
|
746
|
-
console.log(`[V2 Buy] ERC20 利润报价失败,跳过利润提取`);
|
|
747
744
|
profitWei = 0n; // 报价失败,跳过利润
|
|
748
745
|
}
|
|
749
746
|
}
|
|
@@ -779,11 +776,9 @@ export async function directV2BatchSell(params) {
|
|
|
779
776
|
if (tokenDecimals === undefined) {
|
|
780
777
|
try {
|
|
781
778
|
tokenDecimals = Number(await tokenContract.decimals());
|
|
782
|
-
console.log(`📊 自动获取代币精度: ${tokenDecimals}`);
|
|
783
779
|
}
|
|
784
780
|
catch {
|
|
785
781
|
tokenDecimals = 18; // 默认 18
|
|
786
|
-
console.log(`⚠️ 无法获取代币精度,使用默认值: ${tokenDecimals}`);
|
|
787
782
|
}
|
|
788
783
|
}
|
|
789
784
|
// 获取代币余额
|
|
@@ -935,10 +930,8 @@ export async function directV2BatchSell(params) {
|
|
|
935
930
|
const nativeProfitWei = await getTokenToNativeQuote(provider, quoteToken, profitWei, chain);
|
|
936
931
|
if (nativeProfitWei > 0n) {
|
|
937
932
|
profitWei = nativeProfitWei;
|
|
938
|
-
console.log(`[V2 Sell] ERC20 利润转换: ${ethers.formatEther(calculateProfitAmount(totalOutputEstimate))} Token → ${ethers.formatEther(profitWei)} Native`);
|
|
939
933
|
}
|
|
940
934
|
else {
|
|
941
|
-
console.log(`[V2 Sell] ERC20 利润报价失败,跳过利润提取`);
|
|
942
935
|
profitWei = 0n;
|
|
943
936
|
}
|
|
944
937
|
}
|
|
@@ -978,7 +971,6 @@ export async function directV3BatchBuy(params) {
|
|
|
978
971
|
// ✅ 判断是否使用旧版 SwapRouter(Monad Uniswap V3)
|
|
979
972
|
const useLegacyRouter = isLegacySwapRouter(chain, routerAddress);
|
|
980
973
|
const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
|
|
981
|
-
console.log(`[V3 Buy] Router: ${routerAddress}, Legacy: ${useLegacyRouter}`);
|
|
982
974
|
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
983
975
|
const nonceManager = new NonceManager(provider);
|
|
984
976
|
const nonces = await nonceManager.getNextNoncesForWallets(wallets);
|
|
@@ -1061,10 +1053,8 @@ export async function directV3BatchBuy(params) {
|
|
|
1061
1053
|
const nativeProfitWei = await getTokenToNativeQuote(provider, quoteToken, profitWei, chain);
|
|
1062
1054
|
if (nativeProfitWei > 0n) {
|
|
1063
1055
|
profitWei = nativeProfitWei;
|
|
1064
|
-
console.log(`[V3 Buy] ERC20 利润转换: ${ethers.formatEther(calculateProfitAmount(totalFlowWei))} Token → ${ethers.formatEther(profitWei)} Native`);
|
|
1065
1056
|
}
|
|
1066
1057
|
else {
|
|
1067
|
-
console.log(`[V3 Buy] ERC20 利润报价失败,跳过利润提取`);
|
|
1068
1058
|
profitWei = 0n;
|
|
1069
1059
|
}
|
|
1070
1060
|
}
|
|
@@ -1098,7 +1088,6 @@ export async function directV3BatchSell(params) {
|
|
|
1098
1088
|
// ✅ 判断是否使用旧版 SwapRouter(Monad Uniswap V3)
|
|
1099
1089
|
const useLegacyRouter = isLegacySwapRouter(chain, routerAddress);
|
|
1100
1090
|
const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
|
|
1101
|
-
console.log(`[V3 Sell] Router: ${routerAddress}, Legacy: ${useLegacyRouter}`);
|
|
1102
1091
|
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
1103
1092
|
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
1104
1093
|
// 获取余额
|
|
@@ -1239,10 +1228,8 @@ export async function directV3BatchSell(params) {
|
|
|
1239
1228
|
const nativeProfitWei = await getTokenToNativeQuote(provider, quoteToken, profitWei, chain);
|
|
1240
1229
|
if (nativeProfitWei > 0n) {
|
|
1241
1230
|
profitWei = nativeProfitWei;
|
|
1242
|
-
console.log(`[V3 Sell] ERC20 利润转换: ${ethers.formatEther(calculateProfitAmount(totalOutputEstimate))} Token → ${ethers.formatEther(profitWei)} Native`);
|
|
1243
1231
|
}
|
|
1244
1232
|
else {
|
|
1245
|
-
console.log(`[V3 Sell] ERC20 利润报价失败,跳过利润提取`);
|
|
1246
1233
|
profitWei = 0n;
|
|
1247
1234
|
}
|
|
1248
1235
|
}
|
|
@@ -147,9 +147,7 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
147
147
|
const decimalsDiff = 18 - params.quoteTokenDecimals;
|
|
148
148
|
const divisor = BigInt(10 ** decimalsDiff);
|
|
149
149
|
adjustedFundsList = fundsList.map(amount => amount / divisor);
|
|
150
|
-
console.log('🔍 createToken SDK 精度转换: 18 -> ', params.quoteTokenDecimals, ', 原始:', fundsList, ', 转换后:', adjustedFundsList);
|
|
151
150
|
}
|
|
152
|
-
console.log('🔍 createToken SDK - quoteToken:', params.quoteToken, 'inputToken:', inputToken, 'useNativeToken:', useNativeToken);
|
|
153
151
|
// ✅ 优化:并行获取 unsignedBuys 和 buyerNonces
|
|
154
152
|
const [unsignedBuys, buyerNonces] = await Promise.all([
|
|
155
153
|
populateBuyTransactionsWithQuote(buyers, portalAddr, tokenAddress, adjustedFundsList, inputToken, useNativeToken),
|
|
@@ -192,8 +190,6 @@ export async function createTokenWithBundleBuyMerkle(params) {
|
|
|
192
190
|
*/
|
|
193
191
|
export async function batchBuyWithBundleMerkle(params) {
|
|
194
192
|
const { chain, privateKeys, buyAmounts, tokenAddress, quoteToken, quoteTokenDecimals, config } = params;
|
|
195
|
-
console.log('🔍 SDK batchBuyWithBundleMerkle - quoteToken:', quoteToken);
|
|
196
|
-
console.log('🔍 SDK batchBuyWithBundleMerkle - quoteTokenDecimals:', quoteTokenDecimals);
|
|
197
193
|
if (privateKeys.length === 0 || buyAmounts.length !== privateKeys.length) {
|
|
198
194
|
throw new Error(getErrorMessage('KEY_AMOUNT_MISMATCH'));
|
|
199
195
|
}
|
|
@@ -219,17 +215,12 @@ export async function batchBuyWithBundleMerkle(params) {
|
|
|
219
215
|
const decimalsDiff = 18 - quoteTokenDecimals;
|
|
220
216
|
const divisor = BigInt(10 ** decimalsDiff);
|
|
221
217
|
adjustedFundsList = fundsList.map(amount => amount / divisor);
|
|
222
|
-
console.log('🔍 SDK 精度转换: 18 -> ', quoteTokenDecimals, ', 原始:', fundsList, ', 转换后:', adjustedFundsList);
|
|
223
218
|
}
|
|
224
|
-
console.log('🔍 SDK inputToken 计算结果:', inputToken);
|
|
225
|
-
console.log('🔍 SDK useNativeToken:', useNativeToken);
|
|
226
|
-
console.log('🔍 SDK adjustedFundsList:', adjustedFundsList);
|
|
227
219
|
// ✅ ERC20 购买:获取代币利润等值的原生代币(BNB)报价
|
|
228
220
|
let nativeProfitAmount = totalProfit; // 原生代币购买时直接使用
|
|
229
221
|
if (!useNativeToken && extractProfit && totalProfit > 0n) {
|
|
230
222
|
// 将代币利润转换为等值 BNB
|
|
231
223
|
nativeProfitAmount = await getTokenToNativeQuote(provider, inputToken, totalProfit, chainId);
|
|
232
|
-
console.log('🔍 SDK ERC20 利润转换: ', ethers.formatEther(totalProfit), ' Token -> ', ethers.formatEther(nativeProfitAmount), ' BNB');
|
|
233
224
|
}
|
|
234
225
|
// ✅ 优化:如果前端传入了 nonces,需要调整以避免利润交易 nonce 冲突
|
|
235
226
|
const presetNonces = config.nonces;
|
|
@@ -242,7 +233,6 @@ export async function batchBuyWithBundleMerkle(params) {
|
|
|
242
233
|
: allocateBuyerNonces(buyers, extractProfit, maxFundsIndex, nativeProfitAmount, nonceManager)
|
|
243
234
|
]);
|
|
244
235
|
if (presetNonces) {
|
|
245
|
-
console.log('🚀 SDK 使用前端传入的 nonces (调整后):', buyerNonces);
|
|
246
236
|
}
|
|
247
237
|
const signedBuys = await signBuyTransactions({
|
|
248
238
|
unsignedBuys,
|
|
@@ -328,7 +318,6 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
328
318
|
// ✅ 使用前端传入的 nonces,但需要调整避免利润交易冲突
|
|
329
319
|
nonces = adjustNoncesForProfit(presetNonces, extractProfit, maxRevenueIndex, totalTokenProfit);
|
|
330
320
|
profitNonce = needProfitTx ? presetNonces[maxRevenueIndex] + 1 : undefined;
|
|
331
|
-
console.log('🚀 SDK 使用前端传入的 nonces (调整后):', nonces, ', profitNonce:', profitNonce);
|
|
332
321
|
}
|
|
333
322
|
else if (needProfitTx) {
|
|
334
323
|
// maxRevenueIndex 钱包需要 2 个连续 nonce(卖出 + 利润)
|
|
@@ -383,10 +372,8 @@ export async function batchSellWithBundleMerkle(params) {
|
|
|
383
372
|
let nativeProfitAmount = totalTokenProfit;
|
|
384
373
|
if (!useNativeOutput && outputToken) {
|
|
385
374
|
nativeProfitAmount = await getTokenToNativeQuote(provider, outputToken, totalTokenProfit, chainId);
|
|
386
|
-
console.log('🔍 SDK ERC20 卖出利润转换: ', ethers.formatEther(totalTokenProfit), ' Token -> ', ethers.formatEther(nativeProfitAmount), ' BNB');
|
|
387
375
|
// 如果报价失败(返回 0),跳过利润提取
|
|
388
376
|
if (nativeProfitAmount === 0n) {
|
|
389
|
-
console.log('🔍 SDK ERC20 卖出利润转换失败,跳过利润提取');
|
|
390
377
|
}
|
|
391
378
|
}
|
|
392
379
|
if (nativeProfitAmount > 0n) {
|
|
@@ -428,7 +415,6 @@ async function resolveGasPrice(provider, config) {
|
|
|
428
415
|
// ✅ 优化:如果前端传入了 gasPrice,直接使用(跳过 RPC 调用)
|
|
429
416
|
if (config.gasPrice !== undefined) {
|
|
430
417
|
const gasPrice = config.gasPrice;
|
|
431
|
-
console.log('🚀 SDK 使用前端传入的 gasPrice:', ethers.formatUnits(gasPrice, 'gwei'), 'Gwei');
|
|
432
418
|
return gasPrice;
|
|
433
419
|
}
|
|
434
420
|
return await getOptimizedGasPrice(provider, getGasPriceConfig(config));
|
|
@@ -493,28 +479,19 @@ function buildGasLimitList(length, config) {
|
|
|
493
479
|
return new Array(length).fill(gasLimit);
|
|
494
480
|
}
|
|
495
481
|
/**
|
|
496
|
-
* ✅
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
482
|
+
* ✅ 修复:不再调整其他钱包的 nonce
|
|
483
|
+
*
|
|
484
|
+
* 之前的错误逻辑:如果钱包 B 的 nonce 与利润交易的 nonce 数值相同,就 +1
|
|
485
|
+
* 这是错误的!因为不同钱包的 nonce 是独立的,即使数值相同也不会冲突!
|
|
486
|
+
*
|
|
487
|
+
* 正确逻辑:直接返回原始 nonces,不做任何调整
|
|
488
|
+
* - 每个钱包使用自己的 nonce 发送买入交易
|
|
489
|
+
* - 利润交易使用 maxIndex 钱包的 nonce + 1(在构建利润交易时处理)
|
|
500
490
|
*/
|
|
501
491
|
function adjustNoncesForProfit(presetNonces, extractProfit, maxIndex, totalProfit) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
// 利润交易的 nonce = maxIndex 钱包的 nonce + 1
|
|
507
|
-
const profitNonce = presetNonces[maxIndex] + 1;
|
|
508
|
-
// 检查是否有其他钱包的 nonce 与利润交易冲突
|
|
509
|
-
const adjustedNonces = [...presetNonces];
|
|
510
|
-
for (let i = 0; i < adjustedNonces.length; i++) {
|
|
511
|
-
if (i !== maxIndex && adjustedNonces[i] === profitNonce) {
|
|
512
|
-
// 发现冲突!将该钱包的 nonce +1(避开利润交易的 nonce)
|
|
513
|
-
console.log(`⚠️ SDK 检测到 nonce 冲突: 钱包[${i}] nonce=${adjustedNonces[i]} 与利润交易冲突,调整为 ${adjustedNonces[i] + 1}`);
|
|
514
|
-
adjustedNonces[i] = adjustedNonces[i] + 1;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return adjustedNonces;
|
|
492
|
+
// ✅ 直接返回原始 nonces,不做任何调整
|
|
493
|
+
// 不同钱包的 nonce 是独立的,不会冲突
|
|
494
|
+
return presetNonces;
|
|
518
495
|
}
|
|
519
496
|
/**
|
|
520
497
|
* ✅ 修复:明确分配 nonces,避免隐式状态依赖
|
|
@@ -266,8 +266,6 @@ export async function pancakeProxyBatchBuyMerkle(params) {
|
|
|
266
266
|
const finalGasLimit = getGasLimit(config);
|
|
267
267
|
// ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
|
|
268
268
|
const useNativeToken = isUsingNativeToken(quoteToken);
|
|
269
|
-
console.log('🔍 PancakeProxy - quoteToken:', quoteToken);
|
|
270
|
-
console.log('🔍 PancakeProxy - useNativeToken:', useNativeToken);
|
|
271
269
|
const originalAmountsWei = buyAmounts.map(amount => ethers.parseEther(amount));
|
|
272
270
|
const { totalProfit, remainingAmounts } = calculateBatchProfit(originalAmountsWei, config);
|
|
273
271
|
const maxFundsIndex = findMaxAmountIndex(originalAmountsWei);
|
|
@@ -275,7 +273,6 @@ export async function pancakeProxyBatchBuyMerkle(params) {
|
|
|
275
273
|
const shouldExtractProfitForBuy = extractProfit && useNativeToken;
|
|
276
274
|
const nativeProfitAmount = shouldExtractProfitForBuy ? totalProfit : 0n;
|
|
277
275
|
if (!useNativeToken && extractProfit) {
|
|
278
|
-
console.log('🔍 PancakeProxy ERC20 购买:跳过利润提取(用户使用 USDT/ERC20 购买,无 BNB 可转)');
|
|
279
276
|
}
|
|
280
277
|
// ✅ 如果使用 ERC20 代币购买,需要根据精度转换金额
|
|
281
278
|
let actualAmountsWei = remainingAmounts;
|
|
@@ -283,7 +280,6 @@ export async function pancakeProxyBatchBuyMerkle(params) {
|
|
|
283
280
|
const decimalsDiff = 18 - quoteTokenDecimals;
|
|
284
281
|
const divisor = BigInt(10 ** decimalsDiff);
|
|
285
282
|
actualAmountsWei = remainingAmounts.map(amount => amount / divisor);
|
|
286
|
-
console.log('🔍 PancakeProxy 精度转换: 18 -> ', quoteTokenDecimals, ', 原始:', remainingAmounts, ', 转换后:', actualAmountsWei);
|
|
287
283
|
}
|
|
288
284
|
// ✅ 优化:第一批并行 - gasPrice、tokenDecimals、nonces(JSON-RPC 批量请求)
|
|
289
285
|
const [gasPrice, tokenDecimals, nonces] = await Promise.all([
|
|
@@ -107,8 +107,6 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
107
107
|
const inputToken = useNativeToken ? ZERO_ADDRESS : quoteToken;
|
|
108
108
|
// 卖出时的输出代币:与买入时的输入代币相同
|
|
109
109
|
const outputToken = inputToken;
|
|
110
|
-
console.log('🔍 BuyFirst - quoteToken:', quoteToken);
|
|
111
|
-
console.log('🔍 BuyFirst - useNativeToken:', useNativeToken);
|
|
112
110
|
// ✅ 优化:第一批并行 - buyerFunds、gasPrice
|
|
113
111
|
const [buyerFundsResult, gasPrice] = await Promise.all([
|
|
114
112
|
calculateBuyerFunds({
|
|
@@ -196,14 +194,7 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
196
194
|
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
197
195
|
// 将代币利润转换为等值 BNB
|
|
198
196
|
nativeProfitAmount = await getTokenToNativeQuote(chainContext.provider, inputToken, tokenProfitAmount, chainContext.chainId);
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
console.log('[DEBUG] estimatedSellFunds:', estimatedSellFunds.toString());
|
|
202
|
-
console.log('[DEBUG] buyerFundsWei:', buyerFundsWei.toString());
|
|
203
|
-
console.log('[DEBUG] profitBase (使用):', profitBase.toString());
|
|
204
|
-
console.log('[DEBUG] tokenProfitAmount:', tokenProfitAmount.toString());
|
|
205
|
-
console.log('[DEBUG] nativeProfitAmount:', nativeProfitAmount.toString());
|
|
206
|
-
console.log('[DEBUG] noncePlan:', noncePlan);
|
|
197
|
+
}
|
|
207
198
|
const buyTx = buildTransactionRequest(buyUnsigned, {
|
|
208
199
|
from: buyer.address,
|
|
209
200
|
nonce: noncePlan.buyerNonce,
|
|
@@ -238,8 +229,6 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
238
229
|
txType
|
|
239
230
|
})
|
|
240
231
|
]);
|
|
241
|
-
console.log('[DEBUG] profitTx:', profitTx ? '✅ 已生成' : '❌ 未生成');
|
|
242
|
-
console.log('[DEBUG] profitTx length:', profitTx?.length);
|
|
243
232
|
nonceManager.clearTemp();
|
|
244
233
|
const allTransactions = [];
|
|
245
234
|
if (approvalTx)
|
|
@@ -247,7 +236,6 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
247
236
|
allTransactions.push(signedBuy, signedSell);
|
|
248
237
|
if (profitTx)
|
|
249
238
|
allTransactions.push(profitTx);
|
|
250
|
-
console.log('[DEBUG] allTransactions.length:', allTransactions.length);
|
|
251
239
|
return {
|
|
252
240
|
signedTransactions: allTransactions,
|
|
253
241
|
metadata: {
|
|
@@ -468,15 +456,9 @@ function buildTransactionRequest(unsigned, { from, nonce, gasLimit, gasPrice, pr
|
|
|
468
456
|
return tx;
|
|
469
457
|
}
|
|
470
458
|
async function buildProfitTransaction({ seller, profitAmount, profitNonce, gasPrice, chainId, txType }) {
|
|
471
|
-
console.log('[DEBUG buildProfitTransaction] profitNonce:', profitNonce);
|
|
472
|
-
console.log('[DEBUG buildProfitTransaction] profitAmount:', profitAmount.toString());
|
|
473
|
-
console.log('[DEBUG buildProfitTransaction] profitNonce === undefined:', profitNonce === undefined);
|
|
474
|
-
console.log('[DEBUG buildProfitTransaction] profitAmount === 0n:', profitAmount === 0n);
|
|
475
459
|
if (profitNonce === undefined || profitAmount === 0n) {
|
|
476
|
-
console.log('[DEBUG buildProfitTransaction] ❌ 返回 null');
|
|
477
460
|
return null;
|
|
478
461
|
}
|
|
479
|
-
console.log('[DEBUG buildProfitTransaction] ✅ 开始签名利润交易');
|
|
480
462
|
const signed = await seller.signTransaction({
|
|
481
463
|
to: getProfitRecipient(),
|
|
482
464
|
value: profitAmount,
|
|
@@ -486,7 +468,6 @@ async function buildProfitTransaction({ seller, profitAmount, profitNonce, gasPr
|
|
|
486
468
|
chainId,
|
|
487
469
|
type: txType
|
|
488
470
|
});
|
|
489
|
-
console.log('[DEBUG buildProfitTransaction] ✅ 签名完成,长度:', signed.length);
|
|
490
471
|
return signed;
|
|
491
472
|
}
|
|
492
473
|
function countTruthy(values) {
|
|
@@ -109,8 +109,6 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
109
109
|
// ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
|
|
110
110
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
111
111
|
const outputToken = useNativeToken ? ZERO_ADDRESS : quoteToken;
|
|
112
|
-
console.log('🔍 Swap - quoteToken:', quoteToken);
|
|
113
|
-
console.log('🔍 Swap - useNativeToken:', useNativeToken);
|
|
114
112
|
// ✅ 优化:第一批并行 - calculateSellAmount、gasPrice
|
|
115
113
|
const [sellAmountResult, gasPrice] = await Promise.all([
|
|
116
114
|
calculateSellAmount(chainContext.provider, tokenAddress, seller.address, sellAmount, sellPercentage),
|
|
@@ -160,7 +158,6 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
160
158
|
if (!useNativeToken && tokenProfitAmount > 0n) {
|
|
161
159
|
// 将代币利润转换为等值 BNB
|
|
162
160
|
nativeProfitAmount = await getTokenToNativeQuote(chainContext.provider, quoteToken, tokenProfitAmount, chainContext.chainId);
|
|
163
|
-
console.log('🔍 Swap ERC20 利润转换: ', ethers.formatEther(tokenProfitAmount), ' Token -> ', ethers.formatEther(nativeProfitAmount), ' BNB');
|
|
164
161
|
}
|
|
165
162
|
// ✅ 优化:第四批并行 - 构建交易、获取 nonces、验证余额
|
|
166
163
|
const portalSeller = new Contract(chainContext.portalAddress, PORTAL_ABI, seller);
|
|
@@ -479,8 +476,6 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
479
476
|
// ✅ 判断是否使用原生代币
|
|
480
477
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
481
478
|
const outputToken = useNativeToken ? ZERO_ADDRESS : quoteToken;
|
|
482
|
-
console.log('🔍 Flap BatchSwap - 买方数量:', buyerPrivateKeys.length);
|
|
483
|
-
console.log('🔍 Flap BatchSwap - useNativeToken:', useNativeToken);
|
|
484
479
|
// ✅ 并行获取:卖出数量、gasPrice
|
|
485
480
|
const [sellAmountResult, gasPrice] = await Promise.all([
|
|
486
481
|
calculateSellAmount(chainContext.provider, tokenAddress, seller.address, sellAmount, sellPercentage),
|
|
@@ -518,10 +513,8 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
518
513
|
let buyAmountsWei;
|
|
519
514
|
if (params.buyerRatios && params.buyerRatios.length === buyers.length) {
|
|
520
515
|
// 按比例分配
|
|
521
|
-
console.log('📊 使用比例分配,卖出所得:', ethers.formatEther(totalBuyAmount));
|
|
522
516
|
buyAmountsWei = params.buyerRatios.map((ratio, index) => {
|
|
523
517
|
const amount = (totalBuyAmount * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
524
|
-
console.log(` 买方 ${index + 1}: ${(ratio * 100).toFixed(1)}% = ${ethers.formatEther(amount)}`);
|
|
525
518
|
return amount;
|
|
526
519
|
});
|
|
527
520
|
}
|
|
@@ -633,7 +626,6 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
633
626
|
signedTransactions.push(...signedBuys);
|
|
634
627
|
if (profitTx)
|
|
635
628
|
signedTransactions.push(profitTx);
|
|
636
|
-
console.log(`✅ Flap 批量换手签名完成: ${signedTransactions.length} 笔交易`);
|
|
637
629
|
return {
|
|
638
630
|
signedTransactions,
|
|
639
631
|
metadata: {
|
|
@@ -61,8 +61,6 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
61
61
|
const { buyerPrivateKey, sellerPrivateKey, tokenAddress, routeParams, buyerFunds, buyerFundsPercentage, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
62
62
|
// ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
|
|
63
63
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
64
|
-
console.log('🔍 PancakeSwap BuyFirst - quoteToken:', quoteToken);
|
|
65
|
-
console.log('🔍 PancakeSwap BuyFirst - useNativeToken:', useNativeToken);
|
|
66
64
|
const context = createPancakeContext(config);
|
|
67
65
|
const buyer = new Wallet(buyerPrivateKey, context.provider);
|
|
68
66
|
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
@@ -43,7 +43,6 @@ async function quoteSellOutput({ routeParams, sellAmountWei, provider }) {
|
|
|
43
43
|
const params = routeParams;
|
|
44
44
|
const quoter = new Contract(PANCAKE_V3_QUOTER_ADDRESS, PANCAKE_V3_QUOTER_ABI, provider);
|
|
45
45
|
try {
|
|
46
|
-
console.log(`📊 V3 报价参数: tokenIn=${params.v3TokenIn}, tokenOut=${params.v3TokenOut}, fee=${params.v3Fee}, amount=${ethers.formatEther(sellAmountWei)}`);
|
|
47
46
|
const result = await quoter.quoteExactInputSingle.staticCall({
|
|
48
47
|
tokenIn: params.v3TokenIn,
|
|
49
48
|
tokenOut: params.v3TokenOut,
|
|
@@ -51,16 +50,12 @@ async function quoteSellOutput({ routeParams, sellAmountWei, provider }) {
|
|
|
51
50
|
fee: params.v3Fee,
|
|
52
51
|
sqrtPriceLimitX96: 0
|
|
53
52
|
});
|
|
54
|
-
console.log(`✅ V3 报价成功: ${ethers.formatEther(result[0])}`);
|
|
55
53
|
return { estimatedBNBOut: result[0] };
|
|
56
54
|
}
|
|
57
55
|
catch (v3Error) {
|
|
58
|
-
console.warn(`⚠️ V3 报价失败 (fee=${params.v3Fee}):`, v3Error);
|
|
59
56
|
if (params.v2Path && params.v2Path.length >= 2) {
|
|
60
|
-
console.log(`🔄 尝试 V2 备选路径: ${params.v2Path.join(' -> ')}`);
|
|
61
57
|
const v2Router = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, provider);
|
|
62
58
|
const amounts = await v2Router.getAmountsOut(sellAmountWei, params.v2Path);
|
|
63
|
-
console.log(`✅ V2 备选报价成功: ${ethers.formatEther(amounts[amounts.length - 1])}`);
|
|
64
59
|
return { estimatedBNBOut: amounts[amounts.length - 1] };
|
|
65
60
|
}
|
|
66
61
|
throw new Error(`V3 QuoterV2 失败 (fee=${params.v3Fee}) 且未提供 v2Path 作为备选。可能的原因: 1. V3 池子不存在 2. 费率档位错误 3. 流动性不足`);
|
|
@@ -293,8 +288,6 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
293
288
|
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams, slippageTolerance = 0.5, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
294
289
|
// ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
|
|
295
290
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
296
|
-
console.log('🔍 PancakeSwap Swap - quoteToken:', quoteToken);
|
|
297
|
-
console.log('🔍 PancakeSwap Swap - useNativeToken:', useNativeToken);
|
|
298
291
|
const context = createPancakeContext(config);
|
|
299
292
|
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
300
293
|
const buyer = new Wallet(buyerPrivateKey, context.provider);
|
|
@@ -429,8 +422,6 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
429
422
|
throw new Error(`买方钱包数量超过限制: ${buyerPrivateKeys.length} > ${MAX_BUYERS}`);
|
|
430
423
|
}
|
|
431
424
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
432
|
-
console.log('🔍 PancakeSwap BatchSwap - 买方数量:', buyerPrivateKeys.length);
|
|
433
|
-
console.log('🔍 PancakeSwap BatchSwap - useNativeToken:', useNativeToken);
|
|
434
425
|
const context = createPancakeContext(config);
|
|
435
426
|
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
436
427
|
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
@@ -460,7 +451,6 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
460
451
|
provider: context.provider
|
|
461
452
|
});
|
|
462
453
|
const estimatedBNBOut = quoteResult.estimatedBNBOut;
|
|
463
|
-
console.log('📊 预估卖出所得:', ethers.formatEther(estimatedBNBOut), 'BNB');
|
|
464
454
|
// ✅ 计算每个买方的买入金额
|
|
465
455
|
let buyAmountsWei;
|
|
466
456
|
const totalBuyAmount = applySlippage(estimatedBNBOut, slippageTolerance);
|
|
@@ -473,12 +463,9 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
473
463
|
else if (params.buyerRatios && params.buyerRatios.length === buyers.length) {
|
|
474
464
|
// ✅ 方式2:按比例分配卖出所得
|
|
475
465
|
// buyerRatios 如 [0.3, 0.5, 0.2] 表示第一个买方分 30%,第二个 50%,第三个 20%
|
|
476
|
-
console.log('📊 使用比例分配,卖出所得:', ethers.formatEther(totalBuyAmount), 'BNB');
|
|
477
|
-
console.log('📊 买方比例:', params.buyerRatios);
|
|
478
466
|
buyAmountsWei = params.buyerRatios.map((ratio, index) => {
|
|
479
467
|
// 按比例计算每个买方的金额
|
|
480
468
|
const amount = (totalBuyAmount * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
481
|
-
console.log(` 买方 ${index + 1}: ${(ratio * 100).toFixed(1)}% = ${ethers.formatEther(amount)} BNB`);
|
|
482
469
|
return amount;
|
|
483
470
|
});
|
|
484
471
|
}
|
|
@@ -496,7 +483,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
496
483
|
const buyerBalance = await buyer.provider.getBalance(buyer.address);
|
|
497
484
|
const required = buyAmount + FLAT_FEE + reserveGas;
|
|
498
485
|
if (buyerBalance < required) {
|
|
499
|
-
throw new Error(`买方 ${i + 1} 余额不足: 需要 ${ethers.formatEther(required)}
|
|
486
|
+
throw new Error(`买方 ${i + 1} 余额不足: 需要 ${ethers.formatEther(required)}, 实际 ${ethers.formatEther(buyerBalance)}`);
|
|
500
487
|
}
|
|
501
488
|
}
|
|
502
489
|
else {
|
|
@@ -601,8 +588,6 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
601
588
|
signedTransactions.push(...signedBuys); // 多个买入
|
|
602
589
|
if (profitTx)
|
|
603
590
|
signedTransactions.push(profitTx); // 利润(最后)
|
|
604
|
-
console.log(`✅ 批量换手签名完成: ${signedTransactions.length} 笔交易`);
|
|
605
|
-
console.log(` - 授权: ${approvalTx ? 1 : 0}, 卖出: 1, 买入: ${buyers.length}, 利润: ${profitTx ? 1 : 0}`);
|
|
606
591
|
return {
|
|
607
592
|
signedTransactions,
|
|
608
593
|
metadata: {
|
package/dist/utils/lp-inspect.js
CHANGED
|
@@ -99,7 +99,6 @@ export async function getFactoryFromRouter(routerAddress, rpcUrl, routerType = '
|
|
|
99
99
|
if (routerType === 'v2') {
|
|
100
100
|
const router = new Contract(routerAddress, V2_ROUTER_ABI, provider);
|
|
101
101
|
result.v2Factory = await router.factory();
|
|
102
|
-
console.log(`✅ V2 Factory from Router ${routerAddress}: ${result.v2Factory}`);
|
|
103
102
|
}
|
|
104
103
|
else {
|
|
105
104
|
// V3 SwapRouter02 可能同时有 V2 和 V3 Factory
|
|
@@ -107,18 +106,14 @@ export async function getFactoryFromRouter(routerAddress, rpcUrl, routerType = '
|
|
|
107
106
|
// 尝试获取 V3 Factory
|
|
108
107
|
try {
|
|
109
108
|
result.v3Factory = await router.factory();
|
|
110
|
-
console.log(`✅ V3 Factory from Router ${routerAddress}: ${result.v3Factory}`);
|
|
111
109
|
}
|
|
112
110
|
catch {
|
|
113
|
-
console.log(`⚠️ Router ${routerAddress} 没有 factory() 方法`);
|
|
114
111
|
}
|
|
115
112
|
// 尝试获取 V2 Factory
|
|
116
113
|
try {
|
|
117
114
|
result.v2Factory = await router.factoryV2();
|
|
118
|
-
console.log(`✅ V2 Factory (factoryV2) from Router ${routerAddress}: ${result.v2Factory}`);
|
|
119
115
|
}
|
|
120
116
|
catch {
|
|
121
|
-
console.log(`⚠️ Router ${routerAddress} 没有 factoryV2() 方法`);
|
|
122
117
|
}
|
|
123
118
|
}
|
|
124
119
|
}
|
|
@@ -145,11 +140,9 @@ export async function registerDYORSwap(rpcUrl) {
|
|
|
145
140
|
chainConfig.dexes.DYORSWAP.v3Factory = factories.v3Factory;
|
|
146
141
|
}
|
|
147
142
|
chainConfig.dexes.DYORSWAP.enabled = true;
|
|
148
|
-
console.log('✅ DYORSwap 已注册:', chainConfig.dexes.DYORSWAP);
|
|
149
143
|
return true;
|
|
150
144
|
}
|
|
151
145
|
}
|
|
152
|
-
console.log('⚠️ 无法获取 DYORSwap Factory 地址');
|
|
153
146
|
return false;
|
|
154
147
|
}
|
|
155
148
|
catch (error) {
|