four-flap-meme-sdk 1.4.30 → 1.4.32
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/contracts/tm-bundle-merkle/swap-buy-first.d.ts +0 -2
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +7 -48
- package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +0 -1
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +16 -58
- package/dist/pancake/bundle-buy-first.js +0 -2
- package/dist/pancake/bundle-swap.js +19 -6
- package/package.json +1 -1
|
@@ -15,7 +15,6 @@ export interface FourBuyFirstConfig extends CommonBundleConfig {
|
|
|
15
15
|
reserveGasBNB?: number;
|
|
16
16
|
waitForConfirmation?: boolean;
|
|
17
17
|
waitTimeoutMs?: number;
|
|
18
|
-
skipApprovalCheck?: boolean;
|
|
19
18
|
}
|
|
20
19
|
export interface FourBundleBuyFirstSignParams {
|
|
21
20
|
buyerPrivateKey: string;
|
|
@@ -41,7 +40,6 @@ export type FourBuyFirstResult = {
|
|
|
41
40
|
sellerAddress: string;
|
|
42
41
|
buyAmount: string;
|
|
43
42
|
sellAmount: string;
|
|
44
|
-
hasApproval?: boolean;
|
|
45
43
|
profitAmount?: string;
|
|
46
44
|
};
|
|
47
45
|
};
|
|
@@ -52,18 +52,16 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
52
52
|
if (buyerFundsWei <= 0n) {
|
|
53
53
|
throw new Error('buyerFunds 需要大于 0');
|
|
54
54
|
}
|
|
55
|
-
// ✅ 优化:第二批并行 - buyQuote、sellerTokenBal、decimals
|
|
55
|
+
// ✅ 优化:第二批并行 - buyQuote、sellerTokenBal、decimals
|
|
56
56
|
const helper3 = new Contract(ADDRESSES.BSC.TokenManagerHelper3, HELPER3_ABI, provider);
|
|
57
57
|
const erc20 = new Contract(tokenAddress, [
|
|
58
58
|
'function balanceOf(address) view returns (uint256)',
|
|
59
|
-
'function decimals() view returns (uint8)'
|
|
60
|
-
'function allowance(address,address) view returns (uint256)'
|
|
59
|
+
'function decimals() view returns (uint8)'
|
|
61
60
|
], provider);
|
|
62
|
-
const [buyQuote, sellerTokenBal, decimals
|
|
61
|
+
const [buyQuote, sellerTokenBal, decimals] = await Promise.all([
|
|
63
62
|
helper3.tryBuy(tokenAddress, 0n, buyerFundsWei),
|
|
64
63
|
erc20.balanceOf(seller.address),
|
|
65
|
-
erc20.decimals()
|
|
66
|
-
erc20.allowance(seller.address, TM_ADDRESS)
|
|
64
|
+
erc20.decimals()
|
|
67
65
|
]);
|
|
68
66
|
const estimatedTokenAmount = buyQuote.estimatedAmount ?? buyQuote[2];
|
|
69
67
|
if (!estimatedTokenAmount || estimatedTokenAmount <= 0n) {
|
|
@@ -74,14 +72,9 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
74
72
|
if (!sameAddress && sellerTokenBal < sellAmountWei) {
|
|
75
73
|
throw new Error(`卖方代币余额不足: 需要 ${ethers.formatUnits(sellAmountWei, decimals)},实际 ${ethers.formatUnits(sellerTokenBal, decimals)}`);
|
|
76
74
|
}
|
|
77
|
-
// ✅ 检查是否需要授权
|
|
78
|
-
const APPROVAL_THRESHOLD = ethers.parseUnits('1000000000', decimals);
|
|
79
|
-
const needApproval = currentAllowance < APPROVAL_THRESHOLD;
|
|
80
75
|
// ✅ 优化:第三批并行 - trySell、构建交易、获取 nonces
|
|
81
76
|
const tmBuyer = new Contract(TM_ADDRESS, TM_ABI, buyer);
|
|
82
77
|
const tmSeller = new Contract(TM_ADDRESS, TM_ABI, seller);
|
|
83
|
-
// ✅ 已移除滑点保护:minBuyAmount 固定为 0
|
|
84
|
-
const minBuyAmount = 0n;
|
|
85
78
|
// 预先规划 nonces
|
|
86
79
|
const extractProfit = true;
|
|
87
80
|
const profitRateBps = PROFIT_CONFIG.RATE_BPS_CAPITAL; // 万分之三
|
|
@@ -93,8 +86,6 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
93
86
|
let sellerNonceCount = 1; // 卖出交易
|
|
94
87
|
if (needBribeTx)
|
|
95
88
|
sellerNonceCount++; // 贿赂交易(由卖方发送)
|
|
96
|
-
if (needApproval)
|
|
97
|
-
sellerNonceCount++; // 授权交易
|
|
98
89
|
if (extractProfit)
|
|
99
90
|
sellerNonceCount++; // 利润交易
|
|
100
91
|
// ✅ 优化:使用批量 nonce 获取(单次网络往返)
|
|
@@ -110,31 +101,22 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
110
101
|
}));
|
|
111
102
|
const [sellResult, buyUnsigned, sellUnsigned, noncesResult] = await Promise.all([
|
|
112
103
|
trySell('BSC', config.rpcUrl, tokenAddress, sellAmountWei),
|
|
113
|
-
tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyerFundsWei,
|
|
114
|
-
tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmountWei, 0n),
|
|
104
|
+
tmBuyer.buyTokenAMAP.populateTransaction(0n, tokenAddress, buyer.address, buyerFundsWei, 0n, { value: buyerFundsWei }),
|
|
105
|
+
tmSeller.sellToken.populateTransaction(0n, tokenAddress, sellAmountWei, 0n),
|
|
115
106
|
getNoncesPromise
|
|
116
107
|
]);
|
|
117
108
|
const { buyerNonces, sellerNonces } = noncesResult;
|
|
118
109
|
const estimatedSellFunds = sellResult.funds;
|
|
119
|
-
// ✅ 已移除滑点保护:minSellFunds 固定为 0
|
|
120
|
-
const minSellFunds = 0n;
|
|
121
110
|
const profitAmount = extractProfit ? (estimatedSellFunds * BigInt(profitRateBps)) / 10000n : 0n;
|
|
122
|
-
// 更新卖出交易的 minSellFunds
|
|
123
|
-
sellUnsigned.data = tmSeller.interface.encodeFunctionData('sellToken', [
|
|
124
|
-
0n, tokenAddress, sellAmountWei, minSellFunds
|
|
125
|
-
]);
|
|
126
111
|
// 分配 nonces
|
|
127
112
|
let buyerNonce;
|
|
128
113
|
let sellerNonce;
|
|
129
114
|
let bribeNonce;
|
|
130
|
-
let approvalNonce;
|
|
131
115
|
let profitNonce;
|
|
132
116
|
if (sameAddress) {
|
|
133
117
|
let idx = 0;
|
|
134
118
|
if (needBribeTx)
|
|
135
119
|
bribeNonce = sellerNonces[idx++];
|
|
136
|
-
if (needApproval)
|
|
137
|
-
approvalNonce = sellerNonces[idx++];
|
|
138
120
|
buyerNonce = sellerNonces[idx++];
|
|
139
121
|
sellerNonce = sellerNonces[idx++];
|
|
140
122
|
if (extractProfit)
|
|
@@ -145,8 +127,6 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
145
127
|
let idx = 0;
|
|
146
128
|
if (needBribeTx)
|
|
147
129
|
bribeNonce = sellerNonces[idx++];
|
|
148
|
-
if (needApproval)
|
|
149
|
-
approvalNonce = sellerNonces[idx++];
|
|
150
130
|
sellerNonce = sellerNonces[idx++];
|
|
151
131
|
if (extractProfit)
|
|
152
132
|
profitNonce = sellerNonces[idx];
|
|
@@ -166,22 +146,6 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
166
146
|
type: txType
|
|
167
147
|
}));
|
|
168
148
|
}
|
|
169
|
-
// 授权交易
|
|
170
|
-
let approvalTx = null;
|
|
171
|
-
if (needApproval && approvalNonce !== undefined) {
|
|
172
|
-
const approveInterface = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
|
|
173
|
-
const approveData = approveInterface.encodeFunctionData('approve', [TM_ADDRESS, ethers.MaxUint256]);
|
|
174
|
-
signPromises.push(seller.signTransaction({
|
|
175
|
-
to: tokenAddress,
|
|
176
|
-
data: approveData,
|
|
177
|
-
value: 0n,
|
|
178
|
-
nonce: approvalNonce,
|
|
179
|
-
gasLimit: 80000n,
|
|
180
|
-
gasPrice,
|
|
181
|
-
chainId: chainIdNum,
|
|
182
|
-
type: txType
|
|
183
|
-
}));
|
|
184
|
-
}
|
|
185
149
|
// 买入和卖出交易
|
|
186
150
|
signPromises.push(buyer.signTransaction({
|
|
187
151
|
...buyUnsigned,
|
|
@@ -222,19 +186,15 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
222
186
|
let idx = 0;
|
|
223
187
|
if (needBribeTx)
|
|
224
188
|
bribeTx = signedTxs[idx++];
|
|
225
|
-
if (needApproval)
|
|
226
|
-
approvalTx = signedTxs[idx++];
|
|
227
189
|
const signedBuy = signedTxs[idx++];
|
|
228
190
|
const signedSell = signedTxs[idx++];
|
|
229
191
|
if (extractProfit && profitAmount > 0n)
|
|
230
192
|
profitTx = signedTxs[idx];
|
|
231
193
|
nonceManager.clearTemp();
|
|
232
|
-
// ✅ 组装交易列表:贿赂 →
|
|
194
|
+
// ✅ 组装交易列表:贿赂 → 买入 → 卖出 → 利润
|
|
233
195
|
const allTransactions = [];
|
|
234
196
|
if (bribeTx)
|
|
235
197
|
allTransactions.push(bribeTx);
|
|
236
|
-
if (approvalTx)
|
|
237
|
-
allTransactions.push(approvalTx);
|
|
238
198
|
allTransactions.push(signedBuy, signedSell);
|
|
239
199
|
if (profitTx)
|
|
240
200
|
allTransactions.push(profitTx);
|
|
@@ -245,7 +205,6 @@ export async function fourBundleBuyFirstMerkle(params) {
|
|
|
245
205
|
sellerAddress: seller.address,
|
|
246
206
|
buyAmount: ethers.formatEther(buyerFundsWei),
|
|
247
207
|
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
248
|
-
hasApproval: !!approvalTx,
|
|
249
208
|
profitAmount: extractProfit ? ethers.formatEther(profitAmount) : undefined
|
|
250
209
|
}
|
|
251
210
|
};
|
|
@@ -18,7 +18,6 @@ export interface FlapBuyFirstConfig extends CommonBundleConfig {
|
|
|
18
18
|
skipQuoteOnError?: boolean;
|
|
19
19
|
waitForConfirmation?: boolean;
|
|
20
20
|
waitTimeoutMs?: number;
|
|
21
|
-
skipApprovalCheck?: boolean;
|
|
22
21
|
}
|
|
23
22
|
export interface FlapBundleBuyFirstSignParams {
|
|
24
23
|
chain: FlapChain;
|
|
@@ -7,10 +7,8 @@ import { ethers, Contract, Wallet } from 'ethers';
|
|
|
7
7
|
import { NonceManager, getOptimizedGasPrice } from '../../utils/bundle-helpers.js';
|
|
8
8
|
import { FLAP_PORTAL_ADDRESSES } from '../constants.js';
|
|
9
9
|
import { PROFIT_CONFIG, ADDRESSES, ZERO_ADDRESS } from '../../utils/constants.js';
|
|
10
|
-
import { ERC20_BALANCE_ABI,
|
|
10
|
+
import { ERC20_BALANCE_ABI, V2_ROUTER_QUOTE_ABI } from '../../abis/common.js';
|
|
11
11
|
import { getGasPriceConfig, getTxType, getProfitRecipient, getBribeAmount, BLOCKRAZOR_BUILDER_EOA, PORTAL_ABI } from './config.js';
|
|
12
|
-
// ✅ ABI 别名
|
|
13
|
-
const APPROVE_INTERFACE = new ethers.Interface(['function approve(address,uint256) returns (bool)']);
|
|
14
12
|
// ==================== 链常量 ====================
|
|
15
13
|
const BSC_PANCAKE_V2_ROUTER = ADDRESSES.BSC.PancakeV2Router;
|
|
16
14
|
const BSC_WBNB = ADDRESSES.BSC.WBNB;
|
|
@@ -105,7 +103,7 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
105
103
|
]);
|
|
106
104
|
const { buyerFundsWei, buyerBalance } = buyerFundsResult;
|
|
107
105
|
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
108
|
-
// ✅
|
|
106
|
+
// ✅ 获取报价
|
|
109
107
|
const quoteResult = await quoteBuyerOutput({
|
|
110
108
|
portalAddress: chainContext.portalAddress,
|
|
111
109
|
tokenAddress,
|
|
@@ -144,29 +142,15 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
144
142
|
}),
|
|
145
143
|
estimateSellFunds(portalSeller, tokenAddress, sellAmountWei, outputToken)
|
|
146
144
|
]);
|
|
147
|
-
// ✅ 修复:先构建授权交易(会消耗 nonce),再规划其他 nonce
|
|
148
|
-
const approvalTx = await buildApprovalTransaction({
|
|
149
|
-
tokenAddress,
|
|
150
|
-
provider: chainContext.provider,
|
|
151
|
-
seller,
|
|
152
|
-
decimals: sellerInfo.decimals,
|
|
153
|
-
portalAddress: chainContext.portalAddress,
|
|
154
|
-
chainId: chainContext.chainId,
|
|
155
|
-
config,
|
|
156
|
-
nonceManager,
|
|
157
|
-
gasPrice,
|
|
158
|
-
txType
|
|
159
|
-
});
|
|
160
145
|
// ✅ 获取贿赂金额
|
|
161
146
|
const bribeAmount = getBribeAmount(config);
|
|
162
147
|
const needBribeTx = bribeAmount > 0n;
|
|
163
|
-
// ✅
|
|
148
|
+
// ✅ 规划 nonce(已移除授权交易)
|
|
164
149
|
const noncePlan = await planNonces({
|
|
165
150
|
buyer,
|
|
166
151
|
seller,
|
|
167
|
-
approvalExists: approvalTx !== null, // ✅ 使用实际值
|
|
168
152
|
extractProfit: true,
|
|
169
|
-
needBribeTx,
|
|
153
|
+
needBribeTx,
|
|
170
154
|
sameAddress,
|
|
171
155
|
nonceManager
|
|
172
156
|
});
|
|
@@ -228,12 +212,10 @@ export async function flapBundleBuyFirstMerkle(params) {
|
|
|
228
212
|
txType
|
|
229
213
|
});
|
|
230
214
|
nonceManager.clearTemp();
|
|
231
|
-
// ✅ 组装顺序:贿赂 →
|
|
215
|
+
// ✅ 组装顺序:贿赂 → 买入 → 卖出 → 利润
|
|
232
216
|
const allTransactions = [];
|
|
233
217
|
if (bribeTx)
|
|
234
218
|
allTransactions.push(bribeTx);
|
|
235
|
-
if (approvalTx)
|
|
236
|
-
allTransactions.push(approvalTx);
|
|
237
219
|
allTransactions.push(signedBuy, signedSell);
|
|
238
220
|
if (profitTx)
|
|
239
221
|
allTransactions.push(profitTx);
|
|
@@ -321,12 +303,11 @@ async function quoteBuyerOutput({ portalAddress, tokenAddress, buyerFundsWei, pr
|
|
|
321
303
|
throw new Error(`买入报价失败: ${error}`);
|
|
322
304
|
}
|
|
323
305
|
}
|
|
324
|
-
// ✅ 已移除滑点保护:minOutToken 固定为 0
|
|
325
306
|
const sellAmountWei = quotedToken;
|
|
326
307
|
if (sellAmountWei <= 0n) {
|
|
327
308
|
throw new Error('卖方卖出数量为 0:报价失败');
|
|
328
309
|
}
|
|
329
|
-
return { quotedToken,
|
|
310
|
+
return { quotedToken, sellAmountWei };
|
|
330
311
|
}
|
|
331
312
|
async function ensureSellerBalance({ tokenAddress, provider, seller, sellAmountWei, skipBalanceCheck }) {
|
|
332
313
|
const erc20 = new Contract(tokenAddress, ERC20_BALANCE_ABI, provider);
|
|
@@ -339,25 +320,6 @@ async function ensureSellerBalance({ tokenAddress, provider, seller, sellAmountW
|
|
|
339
320
|
}
|
|
340
321
|
return { decimals };
|
|
341
322
|
}
|
|
342
|
-
async function buildApprovalTransaction({ tokenAddress, provider, seller, decimals, portalAddress, chainId, config, nonceManager, gasPrice, txType }) {
|
|
343
|
-
const allowanceContract = new Contract(tokenAddress, ERC20_ALLOWANCE_ABI, provider);
|
|
344
|
-
const currentAllowance = await allowanceContract.allowance(seller.address, portalAddress);
|
|
345
|
-
const approvalThreshold = ethers.parseUnits('1000000000', decimals);
|
|
346
|
-
if (currentAllowance >= approvalThreshold) {
|
|
347
|
-
return null;
|
|
348
|
-
}
|
|
349
|
-
const approvalNonce = await nonceManager.getNextNonce(seller);
|
|
350
|
-
return await seller.signTransaction({
|
|
351
|
-
to: tokenAddress,
|
|
352
|
-
data: APPROVE_INTERFACE.encodeFunctionData('approve', [portalAddress, ethers.MaxUint256]),
|
|
353
|
-
value: 0n,
|
|
354
|
-
nonce: approvalNonce,
|
|
355
|
-
gasLimit: 80000n,
|
|
356
|
-
gasPrice,
|
|
357
|
-
chainId,
|
|
358
|
-
type: txType
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
323
|
async function estimateSellFunds(portal, tokenAddress, sellAmountWei, outputToken = ZERO_ADDRESS // ✅ 新增:输出代币地址
|
|
362
324
|
) {
|
|
363
325
|
try {
|
|
@@ -379,41 +341,37 @@ function calculateProfitAmount(expectedFunds) {
|
|
|
379
341
|
return (expectedFunds * BigInt(PROFIT_CONFIG.RATE_BPS_CAPITAL)) / 10000n;
|
|
380
342
|
}
|
|
381
343
|
/**
|
|
382
|
-
* ✅
|
|
383
|
-
* 交易顺序:贿赂 →
|
|
344
|
+
* ✅ 规划 nonce
|
|
345
|
+
* 交易顺序:贿赂 → 买入 → 卖出 → 利润
|
|
384
346
|
*/
|
|
385
|
-
async function planNonces({ buyer, seller,
|
|
347
|
+
async function planNonces({ buyer, seller, extractProfit, needBribeTx, sameAddress, nonceManager }) {
|
|
386
348
|
if (sameAddress) {
|
|
387
|
-
//
|
|
388
|
-
const txCount = countTruthy([needBribeTx,
|
|
349
|
+
// 同一地址:贿赂(可选) + 买入 + 卖出 + 利润(可选)
|
|
350
|
+
const txCount = countTruthy([needBribeTx, true, true, extractProfit]);
|
|
389
351
|
const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
|
|
390
352
|
let idx = 0;
|
|
391
353
|
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
392
|
-
if (approvalExists)
|
|
393
|
-
idx++;
|
|
394
354
|
const buyerNonce = nonces[idx++];
|
|
395
355
|
const sellerNonce = nonces[idx++];
|
|
396
356
|
const profitNonce = extractProfit ? nonces[idx] : undefined;
|
|
397
357
|
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
398
358
|
}
|
|
399
|
-
if (needBribeTx ||
|
|
400
|
-
// 卖方需要多个 nonce:贿赂(可选) +
|
|
401
|
-
const sellerTxCount = countTruthy([needBribeTx,
|
|
402
|
-
// ✅
|
|
359
|
+
if (needBribeTx || extractProfit) {
|
|
360
|
+
// 卖方需要多个 nonce:贿赂(可选) + 卖出 + 利润(可选)
|
|
361
|
+
const sellerTxCount = countTruthy([needBribeTx, true, extractProfit]);
|
|
362
|
+
// ✅ 并行获取 buyer 和 seller 的 nonce
|
|
403
363
|
const [sellerNonces, buyerNonces] = await Promise.all([
|
|
404
364
|
nonceManager.getNextNonceBatch(seller, sellerTxCount),
|
|
405
365
|
nonceManager.getNextNoncesForWallets([buyer])
|
|
406
366
|
]);
|
|
407
367
|
let idx = 0;
|
|
408
368
|
const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
|
|
409
|
-
if (approvalExists)
|
|
410
|
-
idx++;
|
|
411
369
|
const sellerNonce = sellerNonces[idx++];
|
|
412
370
|
const profitNonce = extractProfit ? sellerNonces[idx] : undefined;
|
|
413
371
|
const buyerNonce = buyerNonces[0];
|
|
414
372
|
return { buyerNonce, sellerNonce, bribeNonce, profitNonce };
|
|
415
373
|
}
|
|
416
|
-
// ✅
|
|
374
|
+
// ✅ 使用 getNextNoncesForWallets 批量获取
|
|
417
375
|
const nonces = await nonceManager.getNextNoncesForWallets([buyer, seller]);
|
|
418
376
|
return { buyerNonce: nonces[0], sellerNonce: nonces[1] };
|
|
419
377
|
}
|
|
@@ -93,7 +93,6 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
93
93
|
const txType = config.txType ?? 0;
|
|
94
94
|
const nonceManager = new NonceManager(context.provider);
|
|
95
95
|
// ✅ 修复:基于买入金额估算利润,而不是基于卖出预估(因为代币可能还没有流动性)
|
|
96
|
-
// 在先买后卖的场景中,卖出收益 ≈ 买入金额(忽略滑点和手续费)
|
|
97
96
|
const estimatedProfitFromSell = await estimateProfitAmount({
|
|
98
97
|
provider: context.provider,
|
|
99
98
|
tokenAddress,
|
|
@@ -299,7 +298,6 @@ async function quoteSellerNative({ provider, tokenAddress, sellAmountToken, rout
|
|
|
299
298
|
return await getTokenToNativeQuote(provider, tokenAddress, sellAmountToken, 'BSC', version, fee);
|
|
300
299
|
}
|
|
301
300
|
function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
|
|
302
|
-
// ✅ 已移除滑点保护:直接使用报价金额
|
|
303
301
|
const estimatedBuyerNeed = quotedNative;
|
|
304
302
|
const buyerNeedTotal = estimatedBuyerNeed + reserveGas;
|
|
305
303
|
if (buyerBalance < buyerNeedTotal) {
|
|
@@ -57,18 +57,31 @@ async function quoteSellOutput({ routeParams, sellAmountWei, provider }) {
|
|
|
57
57
|
}
|
|
58
58
|
async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken = true }) {
|
|
59
59
|
const deadline = getDeadline();
|
|
60
|
-
// ✅ ERC20 购买时,value
|
|
61
|
-
const buyValue = useNativeToken ? buyAmountBNB + FLAT_FEE :
|
|
60
|
+
// ✅ ERC20 购买时,value = 0(通过代币授权支付)
|
|
61
|
+
const buyValue = useNativeToken ? buyAmountBNB + FLAT_FEE : 0n;
|
|
62
62
|
if (routeParams.routeType === 'v2') {
|
|
63
63
|
const { v2Path } = routeParams;
|
|
64
64
|
const reversePath = [...v2Path].reverse();
|
|
65
65
|
// ✅ 使用官方 V2 Router
|
|
66
66
|
const v2RouterSeller = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, seller);
|
|
67
67
|
const v2RouterBuyer = new Contract(PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V2_ROUTER_ABI, buyer);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
let sellUnsigned;
|
|
69
|
+
let buyUnsigned;
|
|
70
|
+
if (useNativeToken) {
|
|
71
|
+
// ✅ 原生代币模式(BNB)
|
|
72
|
+
// 卖出:Token → WBNB → BNB
|
|
73
|
+
sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
74
|
+
// 买入:BNB → WBNB → Token
|
|
75
|
+
buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// ✅ ERC20 模式(如 USDT)
|
|
79
|
+
// 卖出:Token → USDT
|
|
80
|
+
sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
81
|
+
// 买入:USDT → Token
|
|
82
|
+
buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmountBNB, // USDT 数量
|
|
83
|
+
0n, reversePath, buyer.address, deadline);
|
|
84
|
+
}
|
|
72
85
|
return { sellUnsigned, buyUnsigned };
|
|
73
86
|
}
|
|
74
87
|
if (routeParams.routeType === 'v3-single') {
|