four-flap-meme-sdk 1.3.91 → 1.3.93
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.js +0 -1
- package/dist/contracts/tm-bundle-merkle/config.d.ts +5 -0
- package/dist/contracts/tm-bundle-merkle/config.js +10 -0
- package/dist/contracts/tm-bundle-merkle/core.js +92 -24
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +103 -54
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +0 -1
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +36 -6
- package/dist/contracts/tm-bundle-merkle/swap.d.ts +0 -3
- package/dist/contracts/tm-bundle-merkle/swap.js +59 -6
- package/dist/flap/portal-bundle-merkle/config.d.ts +8 -0
- package/dist/flap/portal-bundle-merkle/config.js +17 -0
- package/dist/flap/portal-bundle-merkle/core.js +120 -68
- package/dist/flap/portal-bundle-merkle/encryption.d.ts +16 -0
- package/dist/flap/portal-bundle-merkle/encryption.js +146 -0
- package/dist/flap/portal-bundle-merkle/pancake-proxy.js +136 -78
- package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +0 -2
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +49 -30
- package/dist/flap/portal-bundle-merkle/swap.d.ts +0 -2
- package/dist/flap/portal-bundle-merkle/swap.js +75 -47
- package/dist/flap/portal-bundle-merkle/types.d.ts +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/pancake/bundle-buy-first.d.ts +1 -1
- package/dist/pancake/bundle-buy-first.js +49 -17
- package/dist/pancake/bundle-swap.d.ts +1 -4
- package/dist/pancake/bundle-swap.js +98 -33
- package/dist/utils/erc20.d.ts +108 -2
- package/dist/utils/erc20.js +65 -17
- package/package.json +4 -39
- package/dist/sol/constants.d.ts +0 -126
- package/dist/sol/constants.js +0 -145
- package/dist/sol/dex/index.d.ts +0 -8
- package/dist/sol/dex/index.js +0 -12
- package/dist/sol/dex/meteora/client.d.ts +0 -76
- package/dist/sol/dex/meteora/client.js +0 -219
- package/dist/sol/dex/meteora/damm-v1-bundle.d.ts +0 -61
- package/dist/sol/dex/meteora/damm-v1-bundle.js +0 -112
- package/dist/sol/dex/meteora/damm-v1.d.ts +0 -118
- package/dist/sol/dex/meteora/damm-v1.js +0 -315
- package/dist/sol/dex/meteora/damm-v2-bundle.d.ts +0 -82
- package/dist/sol/dex/meteora/damm-v2-bundle.js +0 -242
- package/dist/sol/dex/meteora/damm-v2.d.ts +0 -172
- package/dist/sol/dex/meteora/damm-v2.js +0 -632
- package/dist/sol/dex/meteora/dbc-bundle.d.ts +0 -123
- package/dist/sol/dex/meteora/dbc-bundle.js +0 -304
- package/dist/sol/dex/meteora/dbc.d.ts +0 -192
- package/dist/sol/dex/meteora/dbc.js +0 -619
- package/dist/sol/dex/meteora/dlmm-bundle.d.ts +0 -39
- package/dist/sol/dex/meteora/dlmm-bundle.js +0 -189
- package/dist/sol/dex/meteora/dlmm.d.ts +0 -146
- package/dist/sol/dex/meteora/dlmm.js +0 -593
- package/dist/sol/dex/meteora/index.d.ts +0 -25
- package/dist/sol/dex/meteora/index.js +0 -65
- package/dist/sol/dex/meteora/types.d.ts +0 -787
- package/dist/sol/dex/meteora/types.js +0 -110
- package/dist/sol/dex/orca/index.d.ts +0 -10
- package/dist/sol/dex/orca/index.js +0 -16
- package/dist/sol/dex/orca/orca-bundle.d.ts +0 -41
- package/dist/sol/dex/orca/orca-bundle.js +0 -173
- package/dist/sol/dex/orca/orca.d.ts +0 -65
- package/dist/sol/dex/orca/orca.js +0 -474
- package/dist/sol/dex/orca/types.d.ts +0 -263
- package/dist/sol/dex/orca/types.js +0 -38
- package/dist/sol/dex/orca/wavebreak-bundle.d.ts +0 -34
- package/dist/sol/dex/orca/wavebreak-bundle.js +0 -198
- package/dist/sol/dex/orca/wavebreak-types.d.ts +0 -227
- package/dist/sol/dex/orca/wavebreak-types.js +0 -23
- package/dist/sol/dex/orca/wavebreak.d.ts +0 -78
- package/dist/sol/dex/orca/wavebreak.js +0 -497
- package/dist/sol/dex/pump/index.d.ts +0 -9
- package/dist/sol/dex/pump/index.js +0 -14
- package/dist/sol/dex/pump/pump-bundle.d.ts +0 -92
- package/dist/sol/dex/pump/pump-bundle.js +0 -383
- package/dist/sol/dex/pump/pump-swap-bundle.d.ts +0 -103
- package/dist/sol/dex/pump/pump-swap-bundle.js +0 -380
- package/dist/sol/dex/pump/pump-swap.d.ts +0 -46
- package/dist/sol/dex/pump/pump-swap.js +0 -199
- package/dist/sol/dex/pump/pump.d.ts +0 -35
- package/dist/sol/dex/pump/pump.js +0 -352
- package/dist/sol/dex/pump/types.d.ts +0 -215
- package/dist/sol/dex/pump/types.js +0 -5
- package/dist/sol/dex/raydium/index.d.ts +0 -8
- package/dist/sol/dex/raydium/index.js +0 -12
- package/dist/sol/dex/raydium/launchlab.d.ts +0 -68
- package/dist/sol/dex/raydium/launchlab.js +0 -210
- package/dist/sol/dex/raydium/raydium-bundle.d.ts +0 -64
- package/dist/sol/dex/raydium/raydium-bundle.js +0 -324
- package/dist/sol/dex/raydium/raydium.d.ts +0 -40
- package/dist/sol/dex/raydium/raydium.js +0 -366
- package/dist/sol/dex/raydium/types.d.ts +0 -240
- package/dist/sol/dex/raydium/types.js +0 -5
- package/dist/sol/index.d.ts +0 -10
- package/dist/sol/index.js +0 -16
- package/dist/sol/jito/bundle.d.ts +0 -90
- package/dist/sol/jito/bundle.js +0 -263
- package/dist/sol/jito/index.d.ts +0 -7
- package/dist/sol/jito/index.js +0 -7
- package/dist/sol/jito/tip.d.ts +0 -51
- package/dist/sol/jito/tip.js +0 -83
- package/dist/sol/jito/types.d.ts +0 -100
- package/dist/sol/jito/types.js +0 -5
- package/dist/sol/token/create-complete.d.ts +0 -115
- package/dist/sol/token/create-complete.js +0 -235
- package/dist/sol/token/create-token.d.ts +0 -57
- package/dist/sol/token/create-token.js +0 -230
- package/dist/sol/token/index.d.ts +0 -9
- package/dist/sol/token/index.js +0 -14
- package/dist/sol/token/metadata-upload.d.ts +0 -86
- package/dist/sol/token/metadata-upload.js +0 -173
- package/dist/sol/token/metadata.d.ts +0 -92
- package/dist/sol/token/metadata.js +0 -274
- package/dist/sol/token/types.d.ts +0 -153
- package/dist/sol/token/types.js +0 -5
- package/dist/sol/types.d.ts +0 -176
- package/dist/sol/types.js +0 -7
- package/dist/sol/utils/balance.d.ts +0 -160
- package/dist/sol/utils/balance.js +0 -638
- package/dist/sol/utils/connection.d.ts +0 -78
- package/dist/sol/utils/connection.js +0 -168
- package/dist/sol/utils/index.d.ts +0 -9
- package/dist/sol/utils/index.js +0 -9
- package/dist/sol/utils/lp-inspect.d.ts +0 -129
- package/dist/sol/utils/lp-inspect.js +0 -529
- package/dist/sol/utils/transfer.d.ts +0 -125
- package/dist/sol/utils/transfer.js +0 -220
- package/dist/sol/utils/wallet.d.ts +0 -107
- package/dist/sol/utils/wallet.js +0 -210
|
@@ -8,8 +8,8 @@ export interface PancakeSwapSignConfig {
|
|
|
8
8
|
txType?: 0 | 2;
|
|
9
9
|
chainId?: number;
|
|
10
10
|
reserveGasBNB?: number;
|
|
11
|
-
slippageTolerance?: number;
|
|
12
11
|
skipApprovalCheck?: boolean;
|
|
12
|
+
bribeAmount?: number;
|
|
13
13
|
}
|
|
14
14
|
export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
|
|
15
15
|
export interface PancakeSwapConfig extends CommonBundleConfig {
|
|
@@ -46,7 +46,6 @@ export interface PancakeBundleSwapSignParams {
|
|
|
46
46
|
buyerPrivateKey: string;
|
|
47
47
|
tokenAddress: string;
|
|
48
48
|
routeParams: RouteParams;
|
|
49
|
-
slippageTolerance?: number;
|
|
50
49
|
config: PancakeSwapSignConfig;
|
|
51
50
|
quoteToken?: string;
|
|
52
51
|
quoteTokenDecimals?: number;
|
|
@@ -58,7 +57,6 @@ export interface PancakeBundleSwapParams {
|
|
|
58
57
|
buyerPrivateKey: string;
|
|
59
58
|
tokenAddress: string;
|
|
60
59
|
routeParams: RouteParams;
|
|
61
|
-
slippageTolerance?: number;
|
|
62
60
|
config: PancakeSwapConfig;
|
|
63
61
|
}
|
|
64
62
|
/** ✅ Pancake Swap 结果(简化版) */
|
|
@@ -90,7 +88,6 @@ export interface PancakeBatchSwapSignParams {
|
|
|
90
88
|
buyerRatios?: number[];
|
|
91
89
|
tokenAddress: string;
|
|
92
90
|
routeParams: RouteParams;
|
|
93
|
-
slippageTolerance?: number;
|
|
94
91
|
config: PancakeSwapSignConfig;
|
|
95
92
|
quoteToken?: string;
|
|
96
93
|
quoteTokenDecimals?: number;
|
|
@@ -99,9 +99,10 @@ async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB,
|
|
|
99
99
|
);
|
|
100
100
|
return { sellUnsigned, buyUnsigned };
|
|
101
101
|
}
|
|
102
|
-
async function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB,
|
|
102
|
+
async function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB, useNativeToken = true, quoteToken, quoteTokenDecimals = 18, provider }) {
|
|
103
103
|
const reserveGas = ethers.parseEther((reserveGasBNB ?? 0.0005).toString());
|
|
104
|
-
|
|
104
|
+
// ✅ 已移除滑点保护:直接使用报价金额
|
|
105
|
+
const buyAmountBNB = quotedBNBOut;
|
|
105
106
|
// ✅ 根据是否使用原生代币获取不同的余额
|
|
106
107
|
let buyerBalance;
|
|
107
108
|
if (useNativeToken) {
|
|
@@ -128,31 +129,46 @@ async function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB, slippa
|
|
|
128
129
|
return { buyerBalance, reserveGas, requiredBalance, buyAmountBNB, useNativeToken };
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
async function planNonces({ seller, buyer, sameAddress, approvalExists, profitNeeded, nonceManager }) {
|
|
132
|
+
/**
|
|
133
|
+
* ✅ 规划 nonce
|
|
134
|
+
* 交易顺序:贿赂 → 授权 → 卖出 → 买入 → 利润
|
|
135
|
+
* 贿赂和利润由卖方发送
|
|
136
|
+
*/
|
|
137
|
+
async function planNonces({ seller, buyer, sameAddress, approvalExists, profitNeeded, needBribeTx, nonceManager }) {
|
|
139
138
|
if (sameAddress) {
|
|
140
|
-
|
|
139
|
+
// 同一地址:贿赂(可选) + 授权(可选) + 卖出 + 买入 + 利润(可选)
|
|
140
|
+
const txCount = countTruthy([needBribeTx, approvalExists, true, true, profitNeeded]);
|
|
141
141
|
const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
|
|
142
142
|
let idx = 0;
|
|
143
|
+
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
143
144
|
if (approvalExists)
|
|
144
145
|
idx++;
|
|
145
|
-
const buyerNonce = nonces[idx++];
|
|
146
146
|
const sellerNonce = nonces[idx++];
|
|
147
|
+
const buyerNonce = nonces[idx++];
|
|
147
148
|
const profitNonce = profitNeeded ? nonces[idx] : undefined;
|
|
148
|
-
return { sellerNonce, buyerNonce, profitNonce };
|
|
149
|
+
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
149
150
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
151
|
+
if (needBribeTx || approvalExists || profitNeeded) {
|
|
152
|
+
// 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
|
|
153
|
+
const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, profitNeeded]);
|
|
154
|
+
// ✅ 并行获取 seller 和 buyer 的 nonce
|
|
155
|
+
const [sellerNonces, buyerNonce] = await Promise.all([
|
|
156
|
+
nonceManager.getNextNonceBatch(seller, sellerTxCount),
|
|
157
|
+
nonceManager.getNextNonce(buyer)
|
|
158
|
+
]);
|
|
159
|
+
let idx = 0;
|
|
160
|
+
const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
|
|
161
|
+
if (approvalExists)
|
|
162
|
+
idx++;
|
|
163
|
+
const sellerNonce = sellerNonces[idx++];
|
|
164
|
+
const profitNonce = profitNeeded ? sellerNonces[idx] : undefined;
|
|
165
|
+
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
154
166
|
}
|
|
155
|
-
|
|
167
|
+
// ✅ 并行获取 seller 和 buyer 的 nonce
|
|
168
|
+
const [sellerNonce, buyerNonce] = await Promise.all([
|
|
169
|
+
nonceManager.getNextNonce(seller),
|
|
170
|
+
nonceManager.getNextNonce(buyer)
|
|
171
|
+
]);
|
|
156
172
|
return { sellerNonce, buyerNonce };
|
|
157
173
|
}
|
|
158
174
|
async function buildProfitTransaction({ wallet, profitAmount, profitNonce, gasPrice, chainId, txType }) {
|
|
@@ -224,6 +240,9 @@ import { ethers, Contract, Wallet } from 'ethers';
|
|
|
224
240
|
import { calculateSellAmount } from '../utils/swap-helpers.js';
|
|
225
241
|
import { NonceManager } from '../utils/bundle-helpers.js';
|
|
226
242
|
import { ADDRESSES, PROFIT_CONFIG } from '../utils/constants.js';
|
|
243
|
+
// ✅ BlockRazor Builder EOA 地址(用于贿赂)
|
|
244
|
+
// 参考文档: https://blockrazor.gitbook.io/blockrazor/bsc/block-builder/send-bundle
|
|
245
|
+
const BLOCKRAZOR_BUILDER_EOA = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20';
|
|
227
246
|
/**
|
|
228
247
|
* 获取 Gas Limit
|
|
229
248
|
*/
|
|
@@ -285,7 +304,7 @@ const ERC20_BALANCE_OF_ABI = ['function balanceOf(address) view returns (uint256
|
|
|
285
304
|
* ✅ 支持 quoteToken:传入 USDT 等地址时,卖出得到该代币,买入使用该代币
|
|
286
305
|
*/
|
|
287
306
|
export async function pancakeBundleSwapMerkle(params) {
|
|
288
|
-
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams,
|
|
307
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
289
308
|
// ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
|
|
290
309
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
291
310
|
const context = createPancakeContext(config);
|
|
@@ -319,7 +338,6 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
319
338
|
buyer,
|
|
320
339
|
quotedBNBOut: quoteResult.estimatedBNBOut,
|
|
321
340
|
reserveGasBNB: config.reserveGasBNB,
|
|
322
|
-
slippageTolerance,
|
|
323
341
|
useNativeToken,
|
|
324
342
|
quoteToken,
|
|
325
343
|
quoteTokenDecimals,
|
|
@@ -335,6 +353,11 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
335
353
|
useNativeToken
|
|
336
354
|
});
|
|
337
355
|
const profitAmount = calculateProfitAmount(quoteResult.estimatedBNBOut);
|
|
356
|
+
// ✅ 获取贿赂金额
|
|
357
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
358
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
359
|
+
: 0n;
|
|
360
|
+
const needBribeTx = bribeAmount > 0n;
|
|
338
361
|
// ✅ 使用共享的 NonceManager 规划 nonce(授权已消耗一个 nonce)
|
|
339
362
|
const noncePlan = await planNonces({
|
|
340
363
|
seller,
|
|
@@ -342,8 +365,22 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
342
365
|
sameAddress,
|
|
343
366
|
approvalExists: !!approvalTx,
|
|
344
367
|
profitNeeded: profitAmount > 0n,
|
|
368
|
+
needBribeTx, // ✅ 新增
|
|
345
369
|
nonceManager
|
|
346
370
|
});
|
|
371
|
+
// ✅ 贿赂交易放在首位(由卖方发送,与利润交易同一钱包)
|
|
372
|
+
let bribeTx = null;
|
|
373
|
+
if (needBribeTx && noncePlan.bribeNonce !== undefined) {
|
|
374
|
+
bribeTx = await seller.signTransaction({
|
|
375
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
376
|
+
value: bribeAmount,
|
|
377
|
+
nonce: noncePlan.bribeNonce,
|
|
378
|
+
gasPrice,
|
|
379
|
+
gasLimit: 21000n,
|
|
380
|
+
chainId: context.chainId,
|
|
381
|
+
type: txType
|
|
382
|
+
});
|
|
383
|
+
}
|
|
347
384
|
const signedSell = await seller.signTransaction({
|
|
348
385
|
...swapUnsigned.sellUnsigned,
|
|
349
386
|
from: seller.address,
|
|
@@ -362,8 +399,9 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
362
399
|
chainId: context.chainId,
|
|
363
400
|
type: txType
|
|
364
401
|
});
|
|
402
|
+
// ✅ 利润交易放在末尾(由卖方发送,与贿赂交易同一钱包)
|
|
365
403
|
const profitTx = await buildProfitTransaction({
|
|
366
|
-
wallet:
|
|
404
|
+
wallet: seller, // ✅ 改为卖方发送(与贿赂交易一致)
|
|
367
405
|
profitAmount,
|
|
368
406
|
profitNonce: noncePlan.profitNonce,
|
|
369
407
|
gasPrice,
|
|
@@ -383,7 +421,10 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
383
421
|
provider: context.provider,
|
|
384
422
|
buyerAddress: buyer.address
|
|
385
423
|
});
|
|
424
|
+
// ✅ 组装顺序:贿赂 → 授权 → 卖出 → 买入 → 利润
|
|
386
425
|
const signedTransactions = [];
|
|
426
|
+
if (bribeTx)
|
|
427
|
+
signedTransactions.push(bribeTx);
|
|
387
428
|
if (approvalTx)
|
|
388
429
|
signedTransactions.push(approvalTx);
|
|
389
430
|
signedTransactions.push(signedSell, signedBuy);
|
|
@@ -412,7 +453,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
412
453
|
* 交易顺序:[授权(可选)] → [卖出] → [买入1, 买入2, ..., 买入N] → [利润]
|
|
413
454
|
*/
|
|
414
455
|
export async function pancakeBatchSwapMerkle(params) {
|
|
415
|
-
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams,
|
|
456
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
416
457
|
// ✅ 校验买方数量(最多 24 个)
|
|
417
458
|
const MAX_BUYERS = 24;
|
|
418
459
|
if (buyerPrivateKeys.length === 0) {
|
|
@@ -451,9 +492,9 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
451
492
|
provider: context.provider
|
|
452
493
|
});
|
|
453
494
|
const estimatedBNBOut = quoteResult.estimatedBNBOut;
|
|
454
|
-
// ✅
|
|
495
|
+
// ✅ 计算每个买方的买入金额(已移除滑点保护:直接使用报价金额)
|
|
455
496
|
let buyAmountsWei;
|
|
456
|
-
const totalBuyAmount =
|
|
497
|
+
const totalBuyAmount = estimatedBNBOut;
|
|
457
498
|
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
458
499
|
// 方式1:使用指定的买入金额(USDT)
|
|
459
500
|
buyAmountsWei = buyerAmounts.map(amt => useNativeToken
|
|
@@ -531,19 +572,41 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
531
572
|
return await proxyBuyer.swapV3MultiHop.populateTransaction(v3LpAddresses, exactTokenOut, buyAmount, 0n, buyer.address, { value: buyValue });
|
|
532
573
|
}
|
|
533
574
|
}));
|
|
534
|
-
// ✅
|
|
535
|
-
|
|
575
|
+
// ✅ 获取贿赂金额
|
|
576
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
577
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
578
|
+
: 0n;
|
|
579
|
+
// ✅ 规划 Nonce(贿赂和利润都由卖方发送)
|
|
580
|
+
// 卖方: [贿赂(可选)] → [授权(可选)] → [卖出] → [利润(可选)]
|
|
581
|
+
let bribeNonce;
|
|
582
|
+
let sellNonceOffset = 0;
|
|
583
|
+
if (bribeAmount > 0n) {
|
|
584
|
+
bribeNonce = await nonceManager.getNextNonce(seller);
|
|
585
|
+
sellNonceOffset = 1; // 卖出交易 nonce 需要偏移
|
|
586
|
+
}
|
|
536
587
|
const sellNonce = await nonceManager.getNextNonce(seller);
|
|
537
588
|
// ✅ 并行获取所有买方的 nonce
|
|
538
589
|
const buyerNonces = await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
539
|
-
//
|
|
590
|
+
// ✅ 贿赂交易放在首位(由卖方发送)
|
|
591
|
+
let bribeTx = null;
|
|
592
|
+
if (bribeAmount > 0n && bribeNonce !== undefined) {
|
|
593
|
+
bribeTx = await seller.signTransaction({
|
|
594
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
595
|
+
value: bribeAmount,
|
|
596
|
+
nonce: bribeNonce,
|
|
597
|
+
gasPrice,
|
|
598
|
+
gasLimit: 21000n,
|
|
599
|
+
chainId: context.chainId,
|
|
600
|
+
type: txType
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
// 利润交易放在末尾(由卖方发送,与贿赂交易一致)
|
|
540
604
|
const profitAmount = calculateProfitAmount(estimatedBNBOut);
|
|
541
605
|
let profitTx = null;
|
|
542
606
|
if (profitAmount > 0n) {
|
|
543
|
-
//
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
profitTx = await profitPayer.signTransaction({
|
|
607
|
+
// ✅ 利润由卖方发送(与贿赂交易同一钱包)
|
|
608
|
+
const profitNonce = await nonceManager.getNextNonce(seller);
|
|
609
|
+
profitTx = await seller.signTransaction({
|
|
547
610
|
to: PROFIT_CONFIG.RECIPIENT,
|
|
548
611
|
value: profitAmount,
|
|
549
612
|
nonce: profitNonce,
|
|
@@ -580,14 +643,16 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
580
643
|
signedSellPromise,
|
|
581
644
|
...signedBuyPromises
|
|
582
645
|
]);
|
|
583
|
-
// 4.
|
|
646
|
+
// 4. 按顺序组装交易数组:贿赂 → 授权 → 卖出 → 买入 → 利润
|
|
584
647
|
const signedTransactions = [];
|
|
648
|
+
if (bribeTx)
|
|
649
|
+
signedTransactions.push(bribeTx); // 贿赂(首位)
|
|
585
650
|
if (approvalTx)
|
|
586
651
|
signedTransactions.push(approvalTx); // 授权(如果有)
|
|
587
652
|
signedTransactions.push(signedSell); // 卖出
|
|
588
653
|
signedTransactions.push(...signedBuys); // 多个买入
|
|
589
654
|
if (profitTx)
|
|
590
|
-
signedTransactions.push(profitTx); //
|
|
655
|
+
signedTransactions.push(profitTx); // 利润(末尾)
|
|
591
656
|
return {
|
|
592
657
|
signedTransactions,
|
|
593
658
|
metadata: {
|
package/dist/utils/erc20.d.ts
CHANGED
|
@@ -167,6 +167,14 @@ export type ApproveTokenBatchParams = {
|
|
|
167
167
|
privateKeys: string[];
|
|
168
168
|
tokenAddress: string;
|
|
169
169
|
amounts: (bigint | 'max')[];
|
|
170
|
+
/** 是否只签名不提交(默认 false,直接发送交易) */
|
|
171
|
+
signOnly?: boolean;
|
|
172
|
+
/** Gas Price(可选,单位 Gwei,signOnly=true 时生效) */
|
|
173
|
+
gasPriceGwei?: number;
|
|
174
|
+
/** Gas Limit(可选,默认 100000,signOnly=true 时生效) */
|
|
175
|
+
gasLimit?: number;
|
|
176
|
+
/** 链ID(可选,默认56=BSC,signOnly=true 时生效) */
|
|
177
|
+
chainId?: number;
|
|
170
178
|
};
|
|
171
179
|
export type ApproveTokenBatchRawParams = {
|
|
172
180
|
rpcUrl: string;
|
|
@@ -174,7 +182,16 @@ export type ApproveTokenBatchRawParams = {
|
|
|
174
182
|
tokenAddress: string;
|
|
175
183
|
spenderAddress: string;
|
|
176
184
|
amounts: (bigint | 'max')[];
|
|
185
|
+
/** 是否只签名不提交(默认 false,直接发送交易) */
|
|
186
|
+
signOnly?: boolean;
|
|
187
|
+
/** Gas Price(可选,单位 Gwei,signOnly=true 时生效) */
|
|
188
|
+
gasPriceGwei?: number;
|
|
189
|
+
/** Gas Limit(可选,默认 100000,signOnly=true 时生效) */
|
|
190
|
+
gasLimit?: number;
|
|
191
|
+
/** 链ID(可选,默认56=BSC,signOnly=true 时生效) */
|
|
192
|
+
chainId?: number;
|
|
177
193
|
};
|
|
194
|
+
/** signOnly=false 时的返回结果(直接发送交易) */
|
|
178
195
|
export type ApproveTokenBatchResult = {
|
|
179
196
|
success: boolean;
|
|
180
197
|
approvedCount: number;
|
|
@@ -187,18 +204,107 @@ export type ApproveTokenBatchResult = {
|
|
|
187
204
|
error?: string;
|
|
188
205
|
}>;
|
|
189
206
|
};
|
|
207
|
+
/** signOnly=true 时的返回结果(仅签名) */
|
|
208
|
+
export type ApproveTokenBatchSignResult = {
|
|
209
|
+
/** 需要授权的签名交易数组(已过滤掉已授权的钱包) */
|
|
210
|
+
signedTransactions: string[];
|
|
211
|
+
/** 详细结果 */
|
|
212
|
+
results: Array<{
|
|
213
|
+
owner: string;
|
|
214
|
+
/** 是否已授权(无需发送交易) */
|
|
215
|
+
alreadyApproved: boolean;
|
|
216
|
+
currentAllowance: bigint;
|
|
217
|
+
requiredAllowance: bigint;
|
|
218
|
+
/** 签名交易(仅当 alreadyApproved=false 时有值) */
|
|
219
|
+
signedTx?: string;
|
|
220
|
+
}>;
|
|
221
|
+
/** 需要授权的钱包数量 */
|
|
222
|
+
needApproveCount: number;
|
|
223
|
+
/** 已授权的钱包数量 */
|
|
224
|
+
alreadyApprovedCount: number;
|
|
225
|
+
};
|
|
190
226
|
/**
|
|
191
227
|
* ✅ 智能路由:批量授权 ERC20 代币(自动选择 spender,自动检查,只在不足时才发送交易)
|
|
192
228
|
*
|
|
193
229
|
* @param params - 批量授权参数
|
|
230
|
+
* @param params.signOnly - 是否只签名不提交(默认 false)
|
|
231
|
+
* - false: 直接发送交易并等待确认,返回 ApproveTokenBatchResult
|
|
232
|
+
* - true: 只签名不提交,返回 ApproveTokenBatchSignResult,前端可通过 submitBundleToBlockRazor 提交
|
|
194
233
|
* @returns 批量授权结果
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```typescript
|
|
237
|
+
* // 模式1:直接发送交易(默认)
|
|
238
|
+
* const result = await approveTokenBatch({
|
|
239
|
+
* chain: 'BSC',
|
|
240
|
+
* platform: 'four',
|
|
241
|
+
* rpcUrl: 'https://bsc-dataseed.binance.org',
|
|
242
|
+
* privateKeys: [key1, key2],
|
|
243
|
+
* tokenAddress: '0x...',
|
|
244
|
+
* amounts: ['max', 'max']
|
|
245
|
+
* });
|
|
246
|
+
*
|
|
247
|
+
* // 模式2:只签名,前端提交
|
|
248
|
+
* const signResult = await approveTokenBatch({
|
|
249
|
+
* chain: 'BSC',
|
|
250
|
+
* platform: 'four',
|
|
251
|
+
* rpcUrl: 'https://bsc-dataseed.binance.org',
|
|
252
|
+
* privateKeys: [key1, key2],
|
|
253
|
+
* tokenAddress: '0x...',
|
|
254
|
+
* amounts: ['max', 'max'],
|
|
255
|
+
* signOnly: true
|
|
256
|
+
* });
|
|
257
|
+
* // 提交到 BlockRazor
|
|
258
|
+
* if (signResult.signedTransactions.length > 0) {
|
|
259
|
+
* await submitBundleToBlockRazor(signResult.signedTransactions, { blockOffset: 10 });
|
|
260
|
+
* }
|
|
261
|
+
* ```
|
|
195
262
|
*/
|
|
196
|
-
export declare function approveTokenBatch(params: ApproveTokenBatchParams
|
|
263
|
+
export declare function approveTokenBatch(params: ApproveTokenBatchParams & {
|
|
264
|
+
signOnly: true;
|
|
265
|
+
}): Promise<ApproveTokenBatchSignResult>;
|
|
266
|
+
export declare function approveTokenBatch(params: ApproveTokenBatchParams & {
|
|
267
|
+
signOnly?: false;
|
|
268
|
+
}): Promise<ApproveTokenBatchResult>;
|
|
197
269
|
/**
|
|
198
270
|
* ✅ 底层方法:批量授权 ERC20 代币(手动指定 spender)
|
|
199
271
|
* 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
|
|
200
272
|
*
|
|
201
273
|
* @param params - 批量授权参数
|
|
274
|
+
* @param params.signOnly - 是否只签名不提交(默认 false)
|
|
275
|
+
* - false: 直接发送交易并等待确认,返回 ApproveTokenBatchResult
|
|
276
|
+
* - true: 只签名不提交,返回 ApproveTokenBatchSignResult,前端可通过 submitBundleToBlockRazor 提交
|
|
202
277
|
* @returns 批量授权结果
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```typescript
|
|
281
|
+
* // 模式1:直接发送交易(默认)
|
|
282
|
+
* const result = await approveTokenBatchRaw({
|
|
283
|
+
* rpcUrl: 'https://bsc-dataseed.binance.org',
|
|
284
|
+
* privateKeys: [key1, key2],
|
|
285
|
+
* tokenAddress: '0x...',
|
|
286
|
+
* spenderAddress: '0x...',
|
|
287
|
+
* amounts: ['max', 'max']
|
|
288
|
+
* });
|
|
289
|
+
*
|
|
290
|
+
* // 模式2:只签名,前端提交
|
|
291
|
+
* const signResult = await approveTokenBatchRaw({
|
|
292
|
+
* rpcUrl: 'https://bsc-dataseed.binance.org',
|
|
293
|
+
* privateKeys: [key1, key2],
|
|
294
|
+
* tokenAddress: '0x...',
|
|
295
|
+
* spenderAddress: '0x...',
|
|
296
|
+
* amounts: ['max', 'max'],
|
|
297
|
+
* signOnly: true
|
|
298
|
+
* });
|
|
299
|
+
* // 提交到 BlockRazor
|
|
300
|
+
* if (signResult.signedTransactions.length > 0) {
|
|
301
|
+
* await submitBundleToBlockRazor(signResult.signedTransactions, { blockOffset: 10 });
|
|
302
|
+
* }
|
|
303
|
+
* ```
|
|
203
304
|
*/
|
|
204
|
-
export declare function approveTokenBatchRaw(params: ApproveTokenBatchRawParams
|
|
305
|
+
export declare function approveTokenBatchRaw(params: ApproveTokenBatchRawParams & {
|
|
306
|
+
signOnly: true;
|
|
307
|
+
}): Promise<ApproveTokenBatchSignResult>;
|
|
308
|
+
export declare function approveTokenBatchRaw(params: ApproveTokenBatchRawParams & {
|
|
309
|
+
signOnly?: false;
|
|
310
|
+
}): Promise<ApproveTokenBatchResult>;
|
package/dist/utils/erc20.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Contract, Wallet, JsonRpcProvider } from 'ethers';
|
|
1
|
+
import { Contract, Wallet, JsonRpcProvider, Interface, parseUnits } from 'ethers';
|
|
2
2
|
import { ADDRESSES } from './constants.js';
|
|
3
3
|
const ERC20_ABI = [
|
|
4
4
|
{ "constant": false, "inputs": [{ "name": "spender", "type": "address" }, { "name": "value", "type": "uint256" }], "name": "approve", "outputs": [{ "name": "", "type": "bool" }], "type": "function" },
|
|
@@ -482,26 +482,13 @@ export async function checkAllowanceBatchRaw(rpcUrl, tokenAddress, ownerAddresse
|
|
|
482
482
|
await validateContractAddress(provider, normalizedSpender, 'Spender');
|
|
483
483
|
return batchCheckAllowances(provider, normalizedToken, normalizedOwners, normalizedSpender);
|
|
484
484
|
}
|
|
485
|
-
/**
|
|
486
|
-
* ✅ 智能路由:批量授权 ERC20 代币(自动选择 spender,自动检查,只在不足时才发送交易)
|
|
487
|
-
*
|
|
488
|
-
* @param params - 批量授权参数
|
|
489
|
-
* @returns 批量授权结果
|
|
490
|
-
*/
|
|
491
485
|
export async function approveTokenBatch(params) {
|
|
492
|
-
const { chain, platform, rpcUrl, privateKeys, tokenAddress, amounts } = params;
|
|
486
|
+
const { chain, platform, rpcUrl, privateKeys, tokenAddress, amounts, signOnly, gasPriceGwei, gasLimit, chainId } = params;
|
|
493
487
|
const spenderAddress = resolveSpenderAddress(chain, platform);
|
|
494
|
-
return approveTokenBatchRaw({ rpcUrl, privateKeys, tokenAddress, spenderAddress, amounts });
|
|
488
|
+
return approveTokenBatchRaw({ rpcUrl, privateKeys, tokenAddress, spenderAddress, amounts, signOnly, gasPriceGwei, gasLimit, chainId });
|
|
495
489
|
}
|
|
496
|
-
/**
|
|
497
|
-
* ✅ 底层方法:批量授权 ERC20 代币(手动指定 spender)
|
|
498
|
-
* 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
|
|
499
|
-
*
|
|
500
|
-
* @param params - 批量授权参数
|
|
501
|
-
* @returns 批量授权结果
|
|
502
|
-
*/
|
|
503
490
|
export async function approveTokenBatchRaw(params) {
|
|
504
|
-
const { rpcUrl, privateKeys, tokenAddress, spenderAddress, amounts } = params;
|
|
491
|
+
const { rpcUrl, privateKeys, tokenAddress, spenderAddress, amounts, signOnly, gasPriceGwei, gasLimit, chainId = 56 } = params;
|
|
505
492
|
if (privateKeys.length === 0 || amounts.length !== privateKeys.length) {
|
|
506
493
|
throw new Error('❌ 私钥数量和授权数量必须匹配');
|
|
507
494
|
}
|
|
@@ -518,6 +505,67 @@ export async function approveTokenBatchRaw(params) {
|
|
|
518
505
|
const requiredAmounts = amounts.map(amount => amount === 'max'
|
|
519
506
|
? BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
|
|
520
507
|
: amount);
|
|
508
|
+
// ==================== signOnly=true:只签名不提交 ====================
|
|
509
|
+
if (signOnly) {
|
|
510
|
+
// ✅ 并行获取:当前授权额度 + nonces + gasPrice
|
|
511
|
+
const [currentAllowances, nonces, fetchedGasPrice] = await Promise.all([
|
|
512
|
+
batchCheckAllowances(provider, normalizedToken, ownerAddresses, normalizedSpender),
|
|
513
|
+
Promise.all(wallets.map(w => provider.getTransactionCount(w.address, 'latest'))),
|
|
514
|
+
gasPriceGwei ? Promise.resolve(parseUnits(gasPriceGwei.toString(), 'gwei')) : provider.getFeeData().then(fee => fee.gasPrice || parseUnits('3', 'gwei'))
|
|
515
|
+
]);
|
|
516
|
+
const finalGasPrice = fetchedGasPrice;
|
|
517
|
+
const finalGasLimit = BigInt(gasLimit || 100000);
|
|
518
|
+
// ✅ ERC20 approve 函数的 ABI 编码
|
|
519
|
+
const erc20Interface = new Interface(ERC20_ABI);
|
|
520
|
+
// ✅ 并行签名所有需要授权的交易
|
|
521
|
+
const signPromises = wallets.map(async (wallet, i) => {
|
|
522
|
+
const ownerAddress = ownerAddresses[i];
|
|
523
|
+
const currentAllowance = currentAllowances[i];
|
|
524
|
+
const requiredAmount = requiredAmounts[i];
|
|
525
|
+
// 如果已经授权足够,跳过
|
|
526
|
+
if (currentAllowance >= requiredAmount) {
|
|
527
|
+
return {
|
|
528
|
+
owner: ownerAddress,
|
|
529
|
+
alreadyApproved: true,
|
|
530
|
+
currentAllowance,
|
|
531
|
+
requiredAllowance: requiredAmount,
|
|
532
|
+
signedTx: undefined
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
// 构建并签名交易
|
|
536
|
+
const txData = erc20Interface.encodeFunctionData('approve', [normalizedSpender, requiredAmount]);
|
|
537
|
+
const signedTx = await wallet.signTransaction({
|
|
538
|
+
to: normalizedToken,
|
|
539
|
+
data: txData,
|
|
540
|
+
nonce: nonces[i],
|
|
541
|
+
gasLimit: finalGasLimit,
|
|
542
|
+
gasPrice: finalGasPrice,
|
|
543
|
+
chainId,
|
|
544
|
+
type: 0 // Legacy 交易
|
|
545
|
+
});
|
|
546
|
+
return {
|
|
547
|
+
owner: ownerAddress,
|
|
548
|
+
alreadyApproved: false,
|
|
549
|
+
currentAllowance,
|
|
550
|
+
requiredAllowance: requiredAmount,
|
|
551
|
+
signedTx
|
|
552
|
+
};
|
|
553
|
+
});
|
|
554
|
+
const results = await Promise.all(signPromises);
|
|
555
|
+
// ✅ 提取所有签名交易(过滤掉已授权的)
|
|
556
|
+
const signedTransactions = results
|
|
557
|
+
.filter(r => !r.alreadyApproved && r.signedTx)
|
|
558
|
+
.map(r => r.signedTx);
|
|
559
|
+
const alreadyApprovedCount = results.filter(r => r.alreadyApproved).length;
|
|
560
|
+
const needApproveCount = results.filter(r => !r.alreadyApproved).length;
|
|
561
|
+
return {
|
|
562
|
+
signedTransactions,
|
|
563
|
+
results,
|
|
564
|
+
needApproveCount,
|
|
565
|
+
alreadyApprovedCount
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
// ==================== signOnly=false(默认):直接发送交易 ====================
|
|
521
569
|
// ✅ 优化:并行检查所有钱包的授权额度
|
|
522
570
|
const currentAllowances = await batchCheckAllowances(provider, normalizedToken, ownerAddresses, normalizedSpender);
|
|
523
571
|
// ✅ 优化:并行发送所有需要授权的交易
|
package/package.json
CHANGED
|
@@ -1,24 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "four-flap-meme-sdk",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "SDK for Flap bonding curve
|
|
3
|
+
"version": "1.3.93",
|
|
4
|
+
"description": "SDK for Flap bonding curve and four.meme TokenManager",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
12
|
-
},
|
|
13
|
-
"./sol": {
|
|
14
|
-
"types": "./dist/sol/index.d.ts",
|
|
15
|
-
"import": "./dist/sol/index.js"
|
|
16
|
-
},
|
|
17
|
-
"./sol/*": {
|
|
18
|
-
"types": "./dist/sol/*.d.ts",
|
|
19
|
-
"import": "./dist/sol/*.js"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
8
|
"files": [
|
|
23
9
|
"dist",
|
|
24
10
|
"src/abis/*.json"
|
|
@@ -43,32 +29,11 @@
|
|
|
43
29
|
"node": ">=18"
|
|
44
30
|
},
|
|
45
31
|
"dependencies": {
|
|
46
|
-
"@metaplex-foundation/mpl-token-metadata": "^3.4.0",
|
|
47
|
-
"@metaplex-foundation/umi": "^1.4.1",
|
|
48
|
-
"@metaplex-foundation/umi-bundle-defaults": "^1.4.1",
|
|
49
|
-
"@metaplex-foundation/umi-web3js-adapters": "^1.4.1",
|
|
50
|
-
"@meteora-ag/cp-amm-sdk": "^1.2.6",
|
|
51
|
-
"@meteora-ag/dlmm": "^1.9.0",
|
|
52
|
-
"@meteora-ag/dynamic-amm-sdk": "^1.4.1",
|
|
53
|
-
"@meteora-ag/dynamic-bonding-curve-sdk": "^1.4.9",
|
|
54
|
-
"@orca-so/common-sdk": "^0.7.0",
|
|
55
|
-
"@orca-so/whirlpools-sdk": "^0.17.0",
|
|
56
|
-
"@pump-fun/pump-sdk": "^1.23.0",
|
|
57
|
-
"@pump-fun/pump-swap-sdk": "^1.11.0",
|
|
58
|
-
"@raydium-io/raydium-sdk-v2": "^0.2.30-alpha",
|
|
59
|
-
"@solana/spl-token": "^0.4.14",
|
|
60
|
-
"@solana/web3.js": "^1.98.4",
|
|
61
32
|
"axios": "^1.12.2",
|
|
62
33
|
"ethers": "^6.11.0",
|
|
63
|
-
"pinata": "^1.10.1"
|
|
64
|
-
"tweetnacl": "^1.0.3"
|
|
65
|
-
},
|
|
66
|
-
"optionalDependencies": {
|
|
67
|
-
"@orca-so/wavebreak": "^1.1.7"
|
|
34
|
+
"pinata": "^1.10.1"
|
|
68
35
|
},
|
|
69
36
|
"devDependencies": {
|
|
70
|
-
"@types/bn.js": "^5.2.0",
|
|
71
|
-
"@types/bs58": "^4.0.4",
|
|
72
37
|
"typescript": "^5.6.3"
|
|
73
38
|
}
|
|
74
|
-
}
|
|
39
|
+
}
|