four-flap-meme-sdk 1.3.87 → 1.3.89
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/core.js +6 -3
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +30 -38
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +0 -1
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +4 -3
- package/dist/contracts/tm-bundle-merkle/swap.d.ts +0 -3
- package/dist/contracts/tm-bundle-merkle/swap.js +2 -2
- package/dist/flap/portal-bundle-merkle/core.js +2 -6
- 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 +55 -35
- package/dist/flap/portal-bundle-merkle/swap-buy-first.d.ts +0 -2
- package/dist/flap/portal-bundle-merkle/swap-buy-first.js +6 -11
- package/dist/flap/portal-bundle-merkle/swap.d.ts +0 -2
- package/dist/flap/portal-bundle-merkle/swap.js +10 -22
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/pancake/bundle-buy-first.d.ts +0 -1
- package/dist/pancake/bundle-buy-first.js +4 -9
- package/dist/pancake/bundle-swap.d.ts +0 -4
- package/dist/pancake/bundle-swap.js +7 -14
- 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 -136
- package/dist/sol/constants.js +0 -156
- 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 -75
- package/dist/sol/dex/meteora/client.js +0 -218
- 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 -167
- package/dist/sol/dex/orca/orca.d.ts +0 -65
- package/dist/sol/dex/orca/orca.js +0 -454
- 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 -189
- 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 -469
- 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 -69
- package/dist/sol/utils/connection.js +0 -156
- 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 -515
- 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
|
@@ -116,7 +116,7 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
116
116
|
]);
|
|
117
117
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
118
118
|
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
119
|
-
// ✅ 优化:第二批并行 - approval、quote
|
|
119
|
+
// ✅ 优化:第二批并行 - approval、quote(已移除滑点保护)
|
|
120
120
|
const [approvalTx, quote] = await Promise.all([
|
|
121
121
|
config.skipApprovalCheck
|
|
122
122
|
? Promise.resolve(null)
|
|
@@ -134,7 +134,6 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
134
134
|
tokenAddress,
|
|
135
135
|
sellAmountWei,
|
|
136
136
|
provider: chainContext.provider,
|
|
137
|
-
slippageBps: config.slippageBps,
|
|
138
137
|
skipQuoteOnError: config.skipQuoteOnError,
|
|
139
138
|
outputToken // ✅ 传递输出代币
|
|
140
139
|
})
|
|
@@ -144,7 +143,6 @@ export async function flapBundleSwapMerkle(params) {
|
|
|
144
143
|
buyer,
|
|
145
144
|
quotedNative: quote.quotedNative,
|
|
146
145
|
reserveGasEth: config.reserveGasETH,
|
|
147
|
-
slippageBps: config.slippageBps,
|
|
148
146
|
nativeToken: chainContext.nativeToken,
|
|
149
147
|
useNativeToken,
|
|
150
148
|
quoteToken,
|
|
@@ -301,32 +299,28 @@ async function buildApprovalTransaction({ tokenAddress, seller, provider, decima
|
|
|
301
299
|
type: txType
|
|
302
300
|
});
|
|
303
301
|
}
|
|
304
|
-
async function quoteSellOutput({ portalAddress, tokenAddress, sellAmountWei, provider,
|
|
302
|
+
async function quoteSellOutput({ portalAddress, tokenAddress, sellAmountWei, provider, skipQuoteOnError, outputToken = ZERO_ADDRESS // ✅ 默认使用原生代币
|
|
305
303
|
}) {
|
|
306
304
|
const portal = new Contract(portalAddress, PORTAL_ABI, provider);
|
|
307
|
-
const safeSlippage = Math.max(0, Math.min(5000, slippageBps ?? 100));
|
|
308
305
|
try {
|
|
309
306
|
const quotedNative = await portal.quoteExactInput.staticCall({
|
|
310
307
|
inputToken: tokenAddress,
|
|
311
308
|
outputToken, // ✅ 使用动态输出代币
|
|
312
309
|
inputAmount: sellAmountWei
|
|
313
310
|
});
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return { quotedNative, minOutNative };
|
|
311
|
+
// ✅ 已移除滑点保护:minOutNative 固定为 0
|
|
312
|
+
return { quotedNative, minOutNative: 0n };
|
|
317
313
|
}
|
|
318
314
|
catch (err) {
|
|
319
315
|
if (skipQuoteOnError ?? true) {
|
|
320
|
-
console.warn(`⚠️ 报价失败,使用 minOut = 0: ${err}`);
|
|
321
316
|
return { quotedNative: 0n, minOutNative: 0n };
|
|
322
317
|
}
|
|
323
318
|
throw new Error(`卖出报价失败: ${err}`);
|
|
324
319
|
}
|
|
325
320
|
}
|
|
326
321
|
const ERC20_BALANCE_OF_ABI = ['function balanceOf(address) view returns (uint256)'];
|
|
327
|
-
async function calculateBuyerNeed({ buyer, quotedNative, reserveGasEth,
|
|
322
|
+
async function calculateBuyerNeed({ buyer, quotedNative, reserveGasEth, nativeToken, useNativeToken = true, quoteToken, quoteTokenDecimals = 18, provider }) {
|
|
328
323
|
const reserveGas = ethers.parseEther((reserveGasEth || 0.0005).toString());
|
|
329
|
-
const safeSlippage = Math.max(0, Math.min(5000, slippageBps ?? 100));
|
|
330
324
|
// ✅ 根据是否使用原生代币获取不同的余额
|
|
331
325
|
let buyerBalance;
|
|
332
326
|
if (useNativeToken) {
|
|
@@ -337,11 +331,8 @@ async function calculateBuyerNeed({ buyer, quotedNative, reserveGasEth, slippage
|
|
|
337
331
|
const erc20 = new Contract(quoteToken, ERC20_BALANCE_OF_ABI, provider || buyer.provider);
|
|
338
332
|
buyerBalance = await erc20.balanceOf(buyer.address);
|
|
339
333
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const increase = BigInt(10000 + safeSlippage);
|
|
343
|
-
estimatedBuyerNeed = (quotedNative * increase) / 10000n;
|
|
344
|
-
}
|
|
334
|
+
// ✅ 已移除滑点保护:直接使用报价金额
|
|
335
|
+
const estimatedBuyerNeed = quotedNative;
|
|
345
336
|
// ✅ 原生代币需要预留 Gas,ERC20 不需要
|
|
346
337
|
const buyerNeedTotal = useNativeToken
|
|
347
338
|
? estimatedBuyerNeed + reserveGas
|
|
@@ -483,7 +474,7 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
483
474
|
]);
|
|
484
475
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
485
476
|
const priorityFee = gasPrice / 10n === 0n ? 1n : gasPrice / 10n;
|
|
486
|
-
// ✅
|
|
477
|
+
// ✅ 并行获取:授权、报价(已移除滑点保护)
|
|
487
478
|
const [approvalTx, quote] = await Promise.all([
|
|
488
479
|
config.skipApprovalCheck
|
|
489
480
|
? Promise.resolve(null)
|
|
@@ -501,15 +492,12 @@ export async function flapBatchSwapMerkle(params) {
|
|
|
501
492
|
tokenAddress,
|
|
502
493
|
sellAmountWei,
|
|
503
494
|
provider: chainContext.provider,
|
|
504
|
-
slippageBps: config.slippageBps,
|
|
505
495
|
skipQuoteOnError: config.skipQuoteOnError,
|
|
506
496
|
outputToken
|
|
507
497
|
})
|
|
508
498
|
]);
|
|
509
|
-
// ✅
|
|
510
|
-
const
|
|
511
|
-
const increase = BigInt(10000 + safeSlippage);
|
|
512
|
-
const totalBuyAmount = (quote.quotedNative * increase) / 10000n;
|
|
499
|
+
// ✅ 计算每个买方的买入金额(按比例分配,已移除滑点保护)
|
|
500
|
+
const totalBuyAmount = quote.quotedNative;
|
|
513
501
|
let buyAmountsWei;
|
|
514
502
|
if (params.buyerRatios && params.buyerRatios.length === buyers.length) {
|
|
515
503
|
// 按比例分配
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export * as Abis from './abis/index.js';
|
|
2
|
-
export * as Sol from './sol/index.js';
|
|
3
2
|
export { ADDRESSES, CHAIN } from './utils/constants.js';
|
|
4
3
|
export { isExclusiveOnChain, isExclusiveOffChain } from './utils/mpcExclusive.js';
|
|
5
|
-
export { ensureSellApprovalV1, checkSellApprovalV1, ensureSellApprovalV2, checkSellApprovalV2, ensureSellApproval, checkSellApproval, ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch, checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch, checkAllowanceRaw, approveTokenRaw, checkAllowanceBatchRaw, approveTokenBatchRaw, type EnsureAllowanceBatchItemResult, type ApproveTokenBatchParams, type ApproveTokenBatchRawParams, type ApproveTokenBatchResult } from './utils/erc20.js';
|
|
4
|
+
export { ensureSellApprovalV1, checkSellApprovalV1, ensureSellApprovalV2, checkSellApprovalV2, ensureSellApproval, checkSellApproval, ensureFlapSellApproval, checkFlapSellApproval, ensureFlapSellApprovalBatch, checkFlapSellApprovalBatch, checkAllowance, approveToken, checkAllowanceBatch, approveTokenBatch, checkAllowanceRaw, approveTokenRaw, checkAllowanceBatchRaw, approveTokenBatchRaw, type EnsureAllowanceBatchItemResult, type ApproveTokenBatchParams, type ApproveTokenBatchRawParams, type ApproveTokenBatchResult, type ApproveTokenBatchSignResult } from './utils/erc20.js';
|
|
6
5
|
export { parseFourError, type FourErrorCode } from './utils/errors.js';
|
|
7
6
|
export { getTokenManagerV1, getTokenManagerV2, getTokenManagerHelper3, getTokenManagerV1Writer, getTokenManagerV2Writer, getTokenManagerHelper3Writer, getTokenManagerAddress, type ChainName } from './utils/contract-factory.js';
|
|
8
7
|
export { FourClient, buildLoginMessage, type FourConfig, type GenerateNonceReq, type LoginReq, type CreateTokenReq, type CreateTokenResp } from './clients/four.js';
|
package/dist/index.js
CHANGED
|
@@ -98,8 +98,7 @@ export async function pancakeBundleBuyFirstMerkle(params) {
|
|
|
98
98
|
const buyerNeed = calculateBuyerNeed({
|
|
99
99
|
quotedNative,
|
|
100
100
|
buyerBalance: buyerFundsInfo.buyerBalance,
|
|
101
|
-
reserveGas: buyerFundsInfo.reserveGas
|
|
102
|
-
slippageBps: config.slippageBps
|
|
101
|
+
reserveGas: buyerFundsInfo.reserveGas
|
|
103
102
|
});
|
|
104
103
|
const finalGasLimit = getGasLimit(config);
|
|
105
104
|
const gasPrice = await getGasPrice(context.provider, config);
|
|
@@ -303,13 +302,9 @@ async function quoteSellerNative({ provider, tokenAddress, sellAmountToken }) {
|
|
|
303
302
|
return 0n;
|
|
304
303
|
}
|
|
305
304
|
}
|
|
306
|
-
function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const safeSlippage = Math.max(0, Math.min(5000, slippageBps ?? 100));
|
|
310
|
-
const increase = BigInt(10000 + safeSlippage);
|
|
311
|
-
estimatedBuyerNeed = (quotedNative * increase) / 10000n;
|
|
312
|
-
}
|
|
305
|
+
function calculateBuyerNeed({ quotedNative, buyerBalance, reserveGas }) {
|
|
306
|
+
// ✅ 已移除滑点保护:直接使用报价金额
|
|
307
|
+
const estimatedBuyerNeed = quotedNative;
|
|
313
308
|
const buyerNeedTotal = estimatedBuyerNeed + reserveGas;
|
|
314
309
|
if (buyerBalance < buyerNeedTotal) {
|
|
315
310
|
throw new Error(`买方余额不足:\n - 需要: ${ethers.formatEther(buyerNeedTotal)} BNB\n - 实际: ${ethers.formatEther(buyerBalance)} BNB`);
|
|
@@ -8,7 +8,6 @@ export interface PancakeSwapSignConfig {
|
|
|
8
8
|
txType?: 0 | 2;
|
|
9
9
|
chainId?: number;
|
|
10
10
|
reserveGasBNB?: number;
|
|
11
|
-
slippageTolerance?: number;
|
|
12
11
|
skipApprovalCheck?: boolean;
|
|
13
12
|
}
|
|
14
13
|
export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
|
|
@@ -46,7 +45,6 @@ export interface PancakeBundleSwapSignParams {
|
|
|
46
45
|
buyerPrivateKey: string;
|
|
47
46
|
tokenAddress: string;
|
|
48
47
|
routeParams: RouteParams;
|
|
49
|
-
slippageTolerance?: number;
|
|
50
48
|
config: PancakeSwapSignConfig;
|
|
51
49
|
quoteToken?: string;
|
|
52
50
|
quoteTokenDecimals?: number;
|
|
@@ -58,7 +56,6 @@ export interface PancakeBundleSwapParams {
|
|
|
58
56
|
buyerPrivateKey: string;
|
|
59
57
|
tokenAddress: string;
|
|
60
58
|
routeParams: RouteParams;
|
|
61
|
-
slippageTolerance?: number;
|
|
62
59
|
config: PancakeSwapConfig;
|
|
63
60
|
}
|
|
64
61
|
/** ✅ Pancake Swap 结果(简化版) */
|
|
@@ -90,7 +87,6 @@ export interface PancakeBatchSwapSignParams {
|
|
|
90
87
|
buyerRatios?: number[];
|
|
91
88
|
tokenAddress: string;
|
|
92
89
|
routeParams: RouteParams;
|
|
93
|
-
slippageTolerance?: number;
|
|
94
90
|
config: PancakeSwapSignConfig;
|
|
95
91
|
quoteToken?: string;
|
|
96
92
|
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,13 +129,6 @@ async function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB, slippa
|
|
|
128
129
|
return { buyerBalance, reserveGas, requiredBalance, buyAmountBNB, useNativeToken };
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
|
-
function applySlippage(amount, tolerancePercent = 0.5) {
|
|
132
|
-
if (amount === 0n) {
|
|
133
|
-
return 0n;
|
|
134
|
-
}
|
|
135
|
-
const bps = BigInt(Math.round(Math.max(0, tolerancePercent) * 100));
|
|
136
|
-
return (amount * (10000n + bps)) / 10000n;
|
|
137
|
-
}
|
|
138
132
|
async function planNonces({ seller, buyer, sameAddress, approvalExists, profitNeeded, nonceManager }) {
|
|
139
133
|
if (sameAddress) {
|
|
140
134
|
const txCount = countTruthy([approvalExists, true, true, profitNeeded]);
|
|
@@ -285,7 +279,7 @@ const ERC20_BALANCE_OF_ABI = ['function balanceOf(address) view returns (uint256
|
|
|
285
279
|
* ✅ 支持 quoteToken:传入 USDT 等地址时,卖出得到该代币,买入使用该代币
|
|
286
280
|
*/
|
|
287
281
|
export async function pancakeBundleSwapMerkle(params) {
|
|
288
|
-
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams,
|
|
282
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
289
283
|
// ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
|
|
290
284
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
291
285
|
const context = createPancakeContext(config);
|
|
@@ -319,7 +313,6 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
319
313
|
buyer,
|
|
320
314
|
quotedBNBOut: quoteResult.estimatedBNBOut,
|
|
321
315
|
reserveGasBNB: config.reserveGasBNB,
|
|
322
|
-
slippageTolerance,
|
|
323
316
|
useNativeToken,
|
|
324
317
|
quoteToken,
|
|
325
318
|
quoteTokenDecimals,
|
|
@@ -412,7 +405,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
412
405
|
* 交易顺序:[授权(可选)] → [卖出] → [买入1, 买入2, ..., 买入N] → [利润]
|
|
413
406
|
*/
|
|
414
407
|
export async function pancakeBatchSwapMerkle(params) {
|
|
415
|
-
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams,
|
|
408
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18 } = params;
|
|
416
409
|
// ✅ 校验买方数量(最多 24 个)
|
|
417
410
|
const MAX_BUYERS = 24;
|
|
418
411
|
if (buyerPrivateKeys.length === 0) {
|
|
@@ -451,9 +444,9 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
451
444
|
provider: context.provider
|
|
452
445
|
});
|
|
453
446
|
const estimatedBNBOut = quoteResult.estimatedBNBOut;
|
|
454
|
-
// ✅
|
|
447
|
+
// ✅ 计算每个买方的买入金额(已移除滑点保护:直接使用报价金额)
|
|
455
448
|
let buyAmountsWei;
|
|
456
|
-
const totalBuyAmount =
|
|
449
|
+
const totalBuyAmount = estimatedBNBOut;
|
|
457
450
|
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
458
451
|
// 方式1:使用指定的买入金额(USDT)
|
|
459
452
|
buyAmountsWei = buyerAmounts.map(amt => useNativeToken
|
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.89",
|
|
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
|
+
}
|