four-flap-meme-sdk 1.6.97 → 1.6.99
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/index.d.ts +3 -3
- package/dist/index.js +5 -3
- package/dist/pancake/bundle-swap.d.ts +40 -0
- package/dist/pancake/bundle-swap.js +138 -0
- package/dist/xlayer/eoa-bundle-swap.d.ts +95 -0
- package/dist/xlayer/eoa-bundle-swap.js +559 -0
- package/dist/xlayer/index.d.ts +1 -0
- package/dist/xlayer/index.js +4 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -40,8 +40,8 @@ export { privateSaleMerkle, batchPrivateSaleMerkle, type PrivateSaleSignConfig,
|
|
|
40
40
|
export { inspectTokenLP, getFactoryFromRouter, registerDYORSwap, registerDex, getChainConfig, type LPInfo, type LPPlatform, type InspectOptions, type DexConfig, type ChainDexConfig, } from './utils/lp-inspect.js';
|
|
41
41
|
export { disperseWithBundle, sweepWithBundle, type DisperseSignParams, type SweepSignParams, type SignedTransactionsResult, type DisperseParams, type SweepParams, type BundleSubmitResult } from './utils/airdrop-sweep.js';
|
|
42
42
|
export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle, type FourSwapSignConfig, type FourSwapConfig, type FourBundleSwapSignParams, type FourBundleSwapParams, type FourSwapResult, type FourBatchSwapSignParams, type FourBatchSwapResult, type FourQuickBatchSwapSignParams, type FourQuickBatchSwapResult } from './contracts/tm-bundle-merkle/swap.js';
|
|
43
|
-
export { flapBundleSwapMerkle, flapBatchSwapMerkle, flapQuickBatchSwapMerkle, type FlapSwapSignConfig, type FlapSwapConfig, type FlapBundleSwapSignParams, type FlapBundleSwapParams, type FlapSwapResult, type FlapBatchSwapSignParams, type FlapBatchSwapResult, type FlapQuickBatchSwapSignParams, type FlapQuickBatchSwapResult } from './flap/portal-bundle-merkle/swap.js';
|
|
44
|
-
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle, pancakeQuickBatchSwapMerkle, type PancakeSwapSignConfig, type PancakeBundleSwapSignParams, type PancakeSwapConfig, type PancakeBundleSwapParams, type PancakeSwapResult, type PancakeBatchSwapSignParams, type PancakeBatchSwapResult, type PancakeQuickBatchSwapParams, type PancakeQuickBatchSwapResult, type SwapRouteType, type V2RouteParams, type V3SingleRouteParams, type V3MultiRouteParams, type RouteParams } from './pancake/bundle-swap.js';
|
|
43
|
+
export { flapBundleSwapMerkle, flapBatchSwapMerkle, flapQuickBatchSwapMerkle, flapCrossSwapMerkle, type FlapSwapSignConfig, type FlapSwapConfig, type FlapBundleSwapSignParams, type FlapBundleSwapParams, type FlapSwapResult, type FlapBatchSwapSignParams, type FlapBatchSwapResult, type FlapQuickBatchSwapSignParams, type FlapQuickBatchSwapResult, type FlapCrossSwapParams, type FlapCrossSwapResult } from './flap/portal-bundle-merkle/swap.js';
|
|
44
|
+
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle, pancakeQuickBatchSwapMerkle, pancakeCrossSwapMerkle, type PancakeSwapSignConfig, type PancakeBundleSwapSignParams, type PancakeSwapConfig, type PancakeBundleSwapParams, type PancakeSwapResult, type PancakeBatchSwapSignParams, type PancakeBatchSwapResult, type PancakeQuickBatchSwapParams, type PancakeQuickBatchSwapResult, type PancakeCrossSwapParams, type PancakeCrossSwapResult, type SwapRouteType, type V2RouteParams, type V3SingleRouteParams, type V3MultiRouteParams, type RouteParams } from './pancake/bundle-swap.js';
|
|
45
45
|
export { fourBundleBuyFirstMerkle, type FourBuyFirstConfig, type FourBundleBuyFirstParams, type FourBuyFirstSignConfig, type FourBundleBuyFirstSignParams, type FourBuyFirstResult } from './contracts/tm-bundle-merkle/swap-buy-first.js';
|
|
46
46
|
export { flapBundleBuyFirstMerkle, type FlapBuyFirstSignConfig, type FlapBuyFirstConfig, type FlapBundleBuyFirstSignParams, type FlapBundleBuyFirstParams, type FlapBuyFirstResult } from './flap/portal-bundle-merkle/swap-buy-first.js';
|
|
47
47
|
export { pancakeBundleBuyFirstMerkle, type PancakeBuyFirstSignConfig, type PancakeBuyFirstConfig, type PancakeBundleBuyFirstSignParams, type PancakeBundleBuyFirstParams, type PancakeBuyFirstResult } from './pancake/bundle-buy-first.js';
|
|
@@ -54,4 +54,4 @@ submitDirectToRpcSequentialNoWait, // ✅ 顺序广播但不等待确认(用
|
|
|
54
54
|
submitDirectToRpcParallel, type DirectSubmitConfig, type DirectSubmitResult, type DirectTxResult } from './contracts/tm-bundle-merkle/submit.js';
|
|
55
55
|
export { directV2BatchBuy, directV2BatchSell, directV3BatchBuy, directV3BatchSell, getRouterAddress, DIRECT_ROUTERS, type DirectV2BuyParams, type DirectV2SellParams, type DirectV3BuyParams, type DirectV3SellParams, type DirectRouterResult, type DirectRouterSignConfig, type DexKey, type RouterVersion, } from './dex/index.js';
|
|
56
56
|
export * as XLayer from './xlayer/index.js';
|
|
57
|
-
export { bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleCreateBuySign as xlayerBundleCreateBuySign, bundleCreateToDexSign as xlayerBundleCreateToDexSign, bundleGraduateBuy as xlayerBundleGraduateBuy, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor, makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor, makeBuyFirstVolume as xlayerMakeBuyFirstVolume, BuyFirstVolumeExecutor as XLayerBuyFirstVolumeExecutor, AAPortalBuyFirstExecutor as XLayerAAPortalBuyFirstExecutor, AADexBuyFirstExecutor as XLayerAADexBuyFirstExecutor, AAVolumeBuyFirstExecutor as XLayerAAVolumeBuyFirstExecutor, createAAVolumeBuyFirstExecutor as xlayerCreateAAVolumeBuyFirstExecutor, type VolumeBuyFirstParams as XLayerVolumeBuyFirstParams, type VolumeBuyFirstResult as XLayerVolumeBuyFirstResult, type VolumeContinuousParams as XLayerVolumeContinuousParams, type VolumeContinuousResult as XLayerVolumeContinuousResult, type VolumeProgress as XLayerVolumeProgress, createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery, buildWashOps, type WashOpsParams, type WashOpsResult, type WashPoolType, type WashUserType, buildBundleBuyOps, buildBundleSellOps, buildBundleProfitOp, filterOutProfitOps, type BundleBuyOpsParams, type BundleSellOpsParams, type BundleProfitOpParams, type BundleOpsResult, type BundlePoolType, type BundleUserType, buildDexBatchBuyOps, buildDexBatchBuyOpsV3, type DexBatchBuyParams, type DexBatchBuyV3Params, type DexBatchBuyResult, buildDexBatchSellOps, buildDexBatchSellOpsV3, type DexBatchSellParams, type DexBatchSellV3Params, type DexBatchSellResult, buildDisperseFromSingleOwnerOpsWithProfit, buildTransfersWithProfit, createAAAccountManager as xlayerCreateAAAccountManager, predictSender as xlayerPredictSender, createWallet as xlayerCreateWallet, AAAccountManager as XLayerAAAccountManager, generateAAWallets as xlayerGenerateAAWallets, generateAAWalletsFromMnemonic as xlayerGenerateAAWalletsFromMnemonic, predictSendersFromPrivateKeys as xlayerPredictSendersFromPrivateKeys, createBundlerClient as xlayerCreateBundlerClient, BundlerClient as XLayerBundlerClient, createPortalQuery as xlayerCreatePortalQuery, PortalQuery as XLayerPortalQuery, encodeBuyCall as xlayerEncodeBuyCall, encodeSellCall as xlayerEncodeSellCall, encodeApproveCall as xlayerEncodeApproveCall, parseOkb as xlayerParseOkb, formatOkb as xlayerFormatOkb, XLAYER_CHAIN_ID, FLAP_PORTAL as XLAYER_FLAP_PORTAL, ENTRYPOINT_V06 as XLAYER_ENTRYPOINT, SIMPLE_ACCOUNT_FACTORY as XLAYER_FACTORY, PARTICLE_BUNDLER_URL as XLAYER_BUNDLER_URL, WOKB as XLAYER_WOKB, POTATOSWAP_V2_ROUTER as XLAYER_POTATOSWAP_ROUTER, type XLayerConfig, type BundleBuyParams as XLayerBundleBuyParams, type BundleSellParams as XLayerBundleSellParams, type BundleBuySellParams as XLayerBundleBuySellParams, type BundleSwapParams as XLayerBundleSwapParams, type BundleSwapSignParams as XLayerBundleSwapSignParams, type BundleBatchSwapParams as XLayerBundleBatchSwapParams, type BundleBatchSwapSignParams as XLayerBundleBatchSwapSignParams, type VolumeParams as XLayerVolumeParams, type BundleBuyResult as XLayerBundleBuyResult, type BundleSellResult as XLayerBundleSellResult, type BundleBuySellResult as XLayerBundleBuySellResult, type BundleCreateBuySignParams as XLayerBundleCreateBuySignParams, type BundleCreateBuySignResult as XLayerBundleCreateBuySignResult, type BundleCreateToDexSignParams as XLayerBundleCreateToDexSignParams, type BundleCreateToDexSignResult as XLayerBundleCreateToDexSignResult, type BundleGraduateBuyParams as XLayerBundleGraduateBuyParams, type BundleGraduateBuyResult as XLayerBundleGraduateBuyResult, type BundleSwapResult as XLayerBundleSwapResult, type BundleSwapSignResult as XLayerBundleSwapSignResult, type BundleBatchSwapResult as XLayerBundleBatchSwapResult, type BundleBatchSwapSignResult as XLayerBundleBatchSwapSignResult, type VolumeResult as XLayerVolumeResult, type BuyFirstParams as XLayerBuyFirstParams, type BuyFirstResult as XLayerBuyFirstResult, type BuyFirstVolumeParams as XLayerBuyFirstVolumeParams, type BuyFirstVolumeResult as XLayerBuyFirstVolumeResult, type HandleOpsResult as XLayerHandleOpsResult, type AAAccount as XLayerAAAccount, type UserOperation as XLayerUserOperation, type GeneratedAAWallet as XLayerGeneratedAAWallet, type GenerateAAWalletsParams as XLayerGenerateAAWalletsParams, type GenerateAAWalletsResult as XLayerGenerateAAWalletsResult, } from './xlayer/index.js';
|
|
57
|
+
export { bundleBuy as xlayerBundleBuy, bundleSell as xlayerBundleSell, bundleBuySell as xlayerBundleBuySell, bundleCreateBuySign as xlayerBundleCreateBuySign, bundleCreateToDexSign as xlayerBundleCreateToDexSign, bundleGraduateBuy as xlayerBundleGraduateBuy, bundleSwapSign as xlayerBundleSwapSign, bundleBatchSwapSign as xlayerBundleBatchSwapSign, createBundleExecutor as xlayerCreateBundleExecutor, BundleExecutor as XLayerBundleExecutor, makeVolume as xlayerMakeVolume, singleRoundVolume as xlayerSingleRoundVolume, createVolumeExecutor as xlayerCreateVolumeExecutor, VolumeExecutor as XLayerVolumeExecutor, makeBuyFirstVolume as xlayerMakeBuyFirstVolume, BuyFirstVolumeExecutor as XLayerBuyFirstVolumeExecutor, AAPortalBuyFirstExecutor as XLayerAAPortalBuyFirstExecutor, AADexBuyFirstExecutor as XLayerAADexBuyFirstExecutor, AAVolumeBuyFirstExecutor as XLayerAAVolumeBuyFirstExecutor, createAAVolumeBuyFirstExecutor as xlayerCreateAAVolumeBuyFirstExecutor, type VolumeBuyFirstParams as XLayerVolumeBuyFirstParams, type VolumeBuyFirstResult as XLayerVolumeBuyFirstResult, type VolumeContinuousParams as XLayerVolumeContinuousParams, type VolumeContinuousResult as XLayerVolumeContinuousResult, type VolumeProgress as XLayerVolumeProgress, createDexExecutor as xlayerCreateDexExecutor, createDexQuery as xlayerCreateDexQuery, quoteOkbToToken as xlayerQuoteOkbToToken, quoteTokenToOkb as xlayerQuoteTokenToOkb, DexExecutor as XLayerDexExecutor, DexQuery as XLayerDexQuery, buildWashOps, type WashOpsParams, type WashOpsResult, type WashPoolType, type WashUserType, buildBundleBuyOps, buildBundleSellOps, buildBundleProfitOp, filterOutProfitOps, type BundleBuyOpsParams, type BundleSellOpsParams, type BundleProfitOpParams, type BundleOpsResult, type BundlePoolType, type BundleUserType, buildDexBatchBuyOps, buildDexBatchBuyOpsV3, type DexBatchBuyParams, type DexBatchBuyV3Params, type DexBatchBuyResult, buildDexBatchSellOps, buildDexBatchSellOpsV3, type DexBatchSellParams, type DexBatchSellV3Params, type DexBatchSellResult, buildDisperseFromSingleOwnerOpsWithProfit, buildTransfersWithProfit, createAAAccountManager as xlayerCreateAAAccountManager, predictSender as xlayerPredictSender, createWallet as xlayerCreateWallet, AAAccountManager as XLayerAAAccountManager, generateAAWallets as xlayerGenerateAAWallets, generateAAWalletsFromMnemonic as xlayerGenerateAAWalletsFromMnemonic, predictSendersFromPrivateKeys as xlayerPredictSendersFromPrivateKeys, createBundlerClient as xlayerCreateBundlerClient, BundlerClient as XLayerBundlerClient, createPortalQuery as xlayerCreatePortalQuery, PortalQuery as XLayerPortalQuery, encodeBuyCall as xlayerEncodeBuyCall, encodeSellCall as xlayerEncodeSellCall, encodeApproveCall as xlayerEncodeApproveCall, parseOkb as xlayerParseOkb, formatOkb as xlayerFormatOkb, XLAYER_CHAIN_ID, FLAP_PORTAL as XLAYER_FLAP_PORTAL, ENTRYPOINT_V06 as XLAYER_ENTRYPOINT, SIMPLE_ACCOUNT_FACTORY as XLAYER_FACTORY, PARTICLE_BUNDLER_URL as XLAYER_BUNDLER_URL, WOKB as XLAYER_WOKB, POTATOSWAP_V2_ROUTER as XLAYER_POTATOSWAP_ROUTER, type XLayerConfig, type BundleBuyParams as XLayerBundleBuyParams, type BundleSellParams as XLayerBundleSellParams, type BundleBuySellParams as XLayerBundleBuySellParams, type BundleSwapParams as XLayerBundleSwapParams, type BundleSwapSignParams as XLayerBundleSwapSignParams, type BundleBatchSwapParams as XLayerBundleBatchSwapParams, type BundleBatchSwapSignParams as XLayerBundleBatchSwapSignParams, type VolumeParams as XLayerVolumeParams, type BundleBuyResult as XLayerBundleBuyResult, type BundleSellResult as XLayerBundleSellResult, type BundleBuySellResult as XLayerBundleBuySellResult, type BundleCreateBuySignParams as XLayerBundleCreateBuySignParams, type BundleCreateBuySignResult as XLayerBundleCreateBuySignResult, type BundleCreateToDexSignParams as XLayerBundleCreateToDexSignParams, type BundleCreateToDexSignResult as XLayerBundleCreateToDexSignResult, type BundleGraduateBuyParams as XLayerBundleGraduateBuyParams, type BundleGraduateBuyResult as XLayerBundleGraduateBuyResult, type BundleSwapResult as XLayerBundleSwapResult, type BundleSwapSignResult as XLayerBundleSwapSignResult, type BundleBatchSwapResult as XLayerBundleBatchSwapResult, type BundleBatchSwapSignResult as XLayerBundleBatchSwapSignResult, type VolumeResult as XLayerVolumeResult, xlayerQuickBatchSwapMerkle, xlayerCrossSwapMerkle, type XLayerEoaSwapConfig, type XLayerQuickBatchSwapParams, type XLayerQuickBatchSwapResult, type XLayerCrossSwapParams, type XLayerCrossSwapResult, type BuyFirstParams as XLayerBuyFirstParams, type BuyFirstResult as XLayerBuyFirstResult, type BuyFirstVolumeParams as XLayerBuyFirstVolumeParams, type BuyFirstVolumeResult as XLayerBuyFirstVolumeResult, type HandleOpsResult as XLayerHandleOpsResult, type AAAccount as XLayerAAAccount, type UserOperation as XLayerUserOperation, type GeneratedAAWallet as XLayerGeneratedAAWallet, type GenerateAAWalletsParams as XLayerGenerateAAWalletsParams, type GenerateAAWalletsResult as XLayerGenerateAAWalletsResult, } from './xlayer/index.js';
|
package/dist/index.js
CHANGED
|
@@ -66,9 +66,9 @@ export { disperseWithBundle, sweepWithBundle } from './utils/airdrop-sweep.js';
|
|
|
66
66
|
// Four内盘换手
|
|
67
67
|
export { fourBundleSwapMerkle, fourBatchSwapMerkle, fourQuickBatchSwapMerkle } from './contracts/tm-bundle-merkle/swap.js';
|
|
68
68
|
// Flap内盘换手
|
|
69
|
-
export { flapBundleSwapMerkle, flapBatchSwapMerkle, flapQuickBatchSwapMerkle } from './flap/portal-bundle-merkle/swap.js';
|
|
69
|
+
export { flapBundleSwapMerkle, flapBatchSwapMerkle, flapQuickBatchSwapMerkle, flapCrossSwapMerkle } from './flap/portal-bundle-merkle/swap.js';
|
|
70
70
|
// PancakeSwap V2/V3通用换手
|
|
71
|
-
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle, pancakeQuickBatchSwapMerkle } from './pancake/bundle-swap.js';
|
|
71
|
+
export { pancakeBundleSwapMerkle, pancakeBatchSwapMerkle, pancakeQuickBatchSwapMerkle, pancakeCrossSwapMerkle } from './pancake/bundle-swap.js';
|
|
72
72
|
// 先买后卖(Buy-First)入口
|
|
73
73
|
export { fourBundleBuyFirstMerkle } from './contracts/tm-bundle-merkle/swap-buy-first.js';
|
|
74
74
|
export { flapBundleBuyFirstMerkle } from './flap/portal-bundle-merkle/swap-buy-first.js';
|
|
@@ -133,4 +133,6 @@ createPortalQuery as xlayerCreatePortalQuery, PortalQuery as XLayerPortalQuery,
|
|
|
133
133
|
// 工具函数
|
|
134
134
|
parseOkb as xlayerParseOkb, formatOkb as xlayerFormatOkb,
|
|
135
135
|
// 常量
|
|
136
|
-
XLAYER_CHAIN_ID, FLAP_PORTAL as XLAYER_FLAP_PORTAL, ENTRYPOINT_V06 as XLAYER_ENTRYPOINT, SIMPLE_ACCOUNT_FACTORY as XLAYER_FACTORY, PARTICLE_BUNDLER_URL as XLAYER_BUNDLER_URL, WOKB as XLAYER_WOKB, POTATOSWAP_V2_ROUTER as XLAYER_POTATOSWAP_ROUTER,
|
|
136
|
+
XLAYER_CHAIN_ID, FLAP_PORTAL as XLAYER_FLAP_PORTAL, ENTRYPOINT_V06 as XLAYER_ENTRYPOINT, SIMPLE_ACCOUNT_FACTORY as XLAYER_FACTORY, PARTICLE_BUNDLER_URL as XLAYER_BUNDLER_URL, WOKB as XLAYER_WOKB, POTATOSWAP_V2_ROUTER as XLAYER_POTATOSWAP_ROUTER,
|
|
137
|
+
// ✅ XLayer EOA 模式换手(不影响 BSC 链)
|
|
138
|
+
xlayerQuickBatchSwapMerkle, xlayerCrossSwapMerkle, } from './xlayer/index.js';
|
|
@@ -170,3 +170,43 @@ export interface PancakeQuickBatchSwapResult {
|
|
|
170
170
|
* 限制:根据多跳数动态计算最大买方数量
|
|
171
171
|
*/
|
|
172
172
|
export declare function pancakeQuickBatchSwapMerkle(params: PancakeQuickBatchSwapParams): Promise<PancakeQuickBatchSwapResult>;
|
|
173
|
+
/** 交叉换手参数(PancakeSwap V2/V3) */
|
|
174
|
+
export interface PancakeCrossSwapParams {
|
|
175
|
+
/** 卖方私钥列表(按顺序轮流卖出) */
|
|
176
|
+
sellerPrivateKeys: string[];
|
|
177
|
+
/** 每个卖方的卖出数量(与 sellerPrivateKeys 长度一致) */
|
|
178
|
+
sellAmounts: string[];
|
|
179
|
+
/** 买方私钥列表(按顺序轮流分配,每笔平分) */
|
|
180
|
+
buyerPrivateKeys: string[];
|
|
181
|
+
tokenAddress: string;
|
|
182
|
+
routeParams: RouteParams;
|
|
183
|
+
config: PancakeSwapSignConfig;
|
|
184
|
+
quoteToken?: string;
|
|
185
|
+
quoteTokenDecimals?: number;
|
|
186
|
+
/** 每次卖出分配给多少个买方(默认使用全部买方平分) */
|
|
187
|
+
buyersPerSell?: number;
|
|
188
|
+
/** 转账多跳数(可选) */
|
|
189
|
+
disperseHopCount?: number;
|
|
190
|
+
}
|
|
191
|
+
/** 交叉换手结果(PancakeSwap V2/V3) */
|
|
192
|
+
export interface PancakeCrossSwapResult {
|
|
193
|
+
signedTransactions: string[];
|
|
194
|
+
/** 每轮的元数据列表 */
|
|
195
|
+
rounds: Array<{
|
|
196
|
+
sellerAddress: string;
|
|
197
|
+
buyerAddresses: string[];
|
|
198
|
+
sellAmount: string;
|
|
199
|
+
bundleHash?: string;
|
|
200
|
+
}>;
|
|
201
|
+
/** 汇总的中间钱包信息(转账/利润) */
|
|
202
|
+
disperseHopWallets?: GeneratedWallet[];
|
|
203
|
+
profitHopWallets?: GeneratedWallet[];
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* PancakeSwap 交叉换手(多卖多买,循环执行)
|
|
207
|
+
* - 每个卖方单独卖出,买入金额由卖出所得等分分配给当轮买方
|
|
208
|
+
* - 利润刮取、转账多跳逻辑继承 pancakeQuickBatchSwapMerkle
|
|
209
|
+
* - 结果按顺序拼接 signedTransactions(可交由前端/后端顺序广播)
|
|
210
|
+
* - ✅ 正确处理 nonce:同一钱包在多轮中使用时 nonce 自动递增
|
|
211
|
+
*/
|
|
212
|
+
export declare function pancakeCrossSwapMerkle(params: PancakeCrossSwapParams): Promise<PancakeCrossSwapResult>;
|
|
@@ -1477,3 +1477,141 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1477
1477
|
}
|
|
1478
1478
|
};
|
|
1479
1479
|
}
|
|
1480
|
+
/**
|
|
1481
|
+
* PancakeSwap 交叉换手(多卖多买,循环执行)
|
|
1482
|
+
* - 每个卖方单独卖出,买入金额由卖出所得等分分配给当轮买方
|
|
1483
|
+
* - 利润刮取、转账多跳逻辑继承 pancakeQuickBatchSwapMerkle
|
|
1484
|
+
* - 结果按顺序拼接 signedTransactions(可交由前端/后端顺序广播)
|
|
1485
|
+
* - ✅ 正确处理 nonce:同一钱包在多轮中使用时 nonce 自动递增
|
|
1486
|
+
*/
|
|
1487
|
+
export async function pancakeCrossSwapMerkle(params) {
|
|
1488
|
+
const { sellerPrivateKeys, sellAmounts, buyerPrivateKeys, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, buyersPerSell, disperseHopCount = 0 } = params;
|
|
1489
|
+
if (sellerPrivateKeys.length === 0) {
|
|
1490
|
+
throw new Error('至少需要一个卖方');
|
|
1491
|
+
}
|
|
1492
|
+
if (sellerPrivateKeys.length !== sellAmounts.length) {
|
|
1493
|
+
throw new Error(`sellAmounts 长度 (${sellAmounts.length}) 必须与卖方数量 (${sellerPrivateKeys.length}) 一致`);
|
|
1494
|
+
}
|
|
1495
|
+
if (buyerPrivateKeys.length === 0) {
|
|
1496
|
+
throw new Error('至少需要一个买方');
|
|
1497
|
+
}
|
|
1498
|
+
// ✅ 创建 Provider 和 NonceManager
|
|
1499
|
+
const context = createPancakeContext(config);
|
|
1500
|
+
const nonceManager = new NonceManager(context.provider);
|
|
1501
|
+
// ✅ 预先获取所有钱包的初始 nonce
|
|
1502
|
+
const allSellerWallets = sellerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
1503
|
+
const allBuyerWallets = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
1504
|
+
// 使用 Map 去重(同一私钥可能出现多次)
|
|
1505
|
+
const addressToNonce = new Map();
|
|
1506
|
+
// 获取所有卖方 nonce
|
|
1507
|
+
for (const wallet of allSellerWallets) {
|
|
1508
|
+
if (!addressToNonce.has(wallet.address)) {
|
|
1509
|
+
const nonce = await nonceManager.getNextNonce(wallet);
|
|
1510
|
+
addressToNonce.set(wallet.address, nonce);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
// 获取所有买方 nonce
|
|
1514
|
+
for (const wallet of allBuyerWallets) {
|
|
1515
|
+
if (!addressToNonce.has(wallet.address)) {
|
|
1516
|
+
const nonce = await nonceManager.getNextNonce(wallet);
|
|
1517
|
+
addressToNonce.set(wallet.address, nonce);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
console.log(`[pancakeCrossSwapMerkle] 初始化完成: ${sellerPrivateKeys.length} 卖方, ${buyerPrivateKeys.length} 买方`);
|
|
1521
|
+
const allSigned = [];
|
|
1522
|
+
const allDisperse = [];
|
|
1523
|
+
const allProfit = [];
|
|
1524
|
+
const rounds = [];
|
|
1525
|
+
// 轮流分配买方:每轮取 buyersPerSell 个(默认所有买方),不足则从头继续
|
|
1526
|
+
let buyerCursor = 0;
|
|
1527
|
+
const buyerBatchSize = Math.max(1, buyersPerSell ?? buyerPrivateKeys.length);
|
|
1528
|
+
// 判断是否使用原生代币
|
|
1529
|
+
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
1530
|
+
// 获取贿赂金额
|
|
1531
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
1532
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
1533
|
+
: 0n;
|
|
1534
|
+
const hasBribe = bribeAmount > 0n;
|
|
1535
|
+
for (let i = 0; i < sellerPrivateKeys.length; i++) {
|
|
1536
|
+
const sellerPk = sellerPrivateKeys[i];
|
|
1537
|
+
const sellAmount = sellAmounts[i];
|
|
1538
|
+
const sellerWallet = allSellerWallets[i];
|
|
1539
|
+
// 选取本轮买方私钥和钱包
|
|
1540
|
+
const roundBuyerPks = [];
|
|
1541
|
+
const roundBuyerWallets = [];
|
|
1542
|
+
for (let k = 0; k < buyerBatchSize; k++) {
|
|
1543
|
+
const idx = buyerCursor % buyerPrivateKeys.length;
|
|
1544
|
+
roundBuyerPks.push(buyerPrivateKeys[idx]);
|
|
1545
|
+
roundBuyerWallets.push(allBuyerWallets[idx]);
|
|
1546
|
+
buyerCursor++;
|
|
1547
|
+
}
|
|
1548
|
+
// ✅ 获取卖方当前 nonce
|
|
1549
|
+
const sellerNonce = addressToNonce.get(sellerWallet.address);
|
|
1550
|
+
// ✅ 获取并更新买方 nonces(每个买方本轮需要 1 个 nonce)
|
|
1551
|
+
const roundBuyerNonces = roundBuyerWallets.map(w => {
|
|
1552
|
+
const nonce = addressToNonce.get(w.address);
|
|
1553
|
+
addressToNonce.set(w.address, nonce + 1); // 每个买方用 1 个 nonce
|
|
1554
|
+
return nonce;
|
|
1555
|
+
});
|
|
1556
|
+
// 平分买入金额(使用比例模式,避免固定金额超出可分配金额)
|
|
1557
|
+
const ratio = 1 / roundBuyerPks.length;
|
|
1558
|
+
const buyerRatios = roundBuyerPks.map(() => ratio);
|
|
1559
|
+
// ✅ 构建 startNonces: [sellerNonce, buyer1Nonce, buyer2Nonce, ...]
|
|
1560
|
+
const startNonces = [sellerNonce, ...roundBuyerNonces];
|
|
1561
|
+
// ✅ 预先计算卖方 nonce 消耗(精确计算)
|
|
1562
|
+
// 卖方 nonce 消耗 = 贿赂(0/1) + 卖出(1) + 转账(N 或 N*2) + 利润(1)
|
|
1563
|
+
const hasProfit = true; // 默认有利润刮取
|
|
1564
|
+
let sellerNonceConsumed = 0;
|
|
1565
|
+
if (hasBribe)
|
|
1566
|
+
sellerNonceConsumed += 1; // 贿赂
|
|
1567
|
+
sellerNonceConsumed += 1; // 卖出
|
|
1568
|
+
if (disperseHopCount === 0) {
|
|
1569
|
+
sellerNonceConsumed += roundBuyerPks.length; // 直接转账
|
|
1570
|
+
}
|
|
1571
|
+
else if (useNativeToken) {
|
|
1572
|
+
sellerNonceConsumed += roundBuyerPks.length; // 原生多跳:seller→hop1
|
|
1573
|
+
}
|
|
1574
|
+
else {
|
|
1575
|
+
sellerNonceConsumed += roundBuyerPks.length * 2; // ERC20 多跳:BNB + ERC20 两批
|
|
1576
|
+
}
|
|
1577
|
+
if (hasProfit)
|
|
1578
|
+
sellerNonceConsumed += 1; // 利润
|
|
1579
|
+
// 调用单卖多买的签名函数
|
|
1580
|
+
const res = await pancakeQuickBatchSwapMerkle({
|
|
1581
|
+
sellerPrivateKey: sellerPk,
|
|
1582
|
+
sellAmount,
|
|
1583
|
+
buyerPrivateKeys: roundBuyerPks,
|
|
1584
|
+
buyerRatios,
|
|
1585
|
+
tokenAddress,
|
|
1586
|
+
routeParams,
|
|
1587
|
+
config,
|
|
1588
|
+
quoteToken,
|
|
1589
|
+
quoteTokenDecimals,
|
|
1590
|
+
disperseHopCount,
|
|
1591
|
+
startNonces // ✅ 传入预计算的 nonces
|
|
1592
|
+
});
|
|
1593
|
+
// ✅ 更新卖方 nonce(使用精确计算的值)
|
|
1594
|
+
addressToNonce.set(sellerWallet.address, sellerNonce + sellerNonceConsumed);
|
|
1595
|
+
console.log(`[pancakeCrossSwapMerkle] 轮次 ${i + 1}: 卖方=${sellerWallet.address.slice(0, 10)}..., 买方=${roundBuyerPks.length}, 交易=${res.signedTransactions.length}`);
|
|
1596
|
+
// 累积签名与中间钱包
|
|
1597
|
+
allSigned.push(...res.signedTransactions);
|
|
1598
|
+
if (res.disperseHopWallets)
|
|
1599
|
+
allDisperse.push(...res.disperseHopWallets);
|
|
1600
|
+
if (res.profitHopWallets)
|
|
1601
|
+
allProfit.push(...res.profitHopWallets);
|
|
1602
|
+
rounds.push({
|
|
1603
|
+
sellerAddress: res.metadata?.sellerAddress || '',
|
|
1604
|
+
buyerAddresses: res.metadata?.buyerAddresses || roundBuyerPks.map(() => ''),
|
|
1605
|
+
sellAmount,
|
|
1606
|
+
bundleHash: undefined
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
nonceManager.clearTemp();
|
|
1610
|
+
console.log(`[pancakeCrossSwapMerkle] 完成: ${rounds.length} 轮, ${allSigned.length} 笔交易`);
|
|
1611
|
+
return {
|
|
1612
|
+
signedTransactions: allSigned,
|
|
1613
|
+
rounds,
|
|
1614
|
+
disperseHopWallets: allDisperse.length > 0 ? allDisperse : undefined,
|
|
1615
|
+
profitHopWallets: allProfit.length > 0 ? allProfit : undefined
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XLayer EOA 模式批量换手
|
|
3
|
+
*
|
|
4
|
+
* 专门为 XLayer 链设计,不影响 BSC 链的逻辑
|
|
5
|
+
* - 使用 PotatoSwap V3 Quoter
|
|
6
|
+
* - 使用 WOKB 作为 Wrapped Native Token
|
|
7
|
+
* - 支持多跳转账和利润刮取
|
|
8
|
+
*/
|
|
9
|
+
import { type GeneratedWallet } from '../utils/wallet.js';
|
|
10
|
+
export type UserType = 'v0' | 'v1';
|
|
11
|
+
export type SwapRouteType = 'v2' | 'v3-single' | 'v3-multi';
|
|
12
|
+
export interface V2RouteParams {
|
|
13
|
+
routeType: 'v2';
|
|
14
|
+
v2Path: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface V3SingleRouteParams {
|
|
17
|
+
routeType: 'v3-single';
|
|
18
|
+
v3TokenIn: string;
|
|
19
|
+
v3TokenOut: string;
|
|
20
|
+
v3Fee: number;
|
|
21
|
+
v2Path?: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface V3MultiRouteParams {
|
|
24
|
+
routeType: 'v3-multi';
|
|
25
|
+
v3LpAddresses: string[];
|
|
26
|
+
v3ExactTokenIn: string;
|
|
27
|
+
v2Path?: string[];
|
|
28
|
+
}
|
|
29
|
+
export type RouteParams = V2RouteParams | V3SingleRouteParams | V3MultiRouteParams;
|
|
30
|
+
export interface XLayerEoaSwapConfig {
|
|
31
|
+
rpcUrl: string;
|
|
32
|
+
gasLimit?: number | bigint;
|
|
33
|
+
gasLimitMultiplier?: number;
|
|
34
|
+
minGasPriceGwei?: number;
|
|
35
|
+
maxGasPriceGwei?: number;
|
|
36
|
+
txType?: 0 | 2;
|
|
37
|
+
reserveGasBNB?: number;
|
|
38
|
+
userType?: UserType;
|
|
39
|
+
bribeAmount?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface XLayerQuickBatchSwapParams {
|
|
42
|
+
sellerPrivateKey: string;
|
|
43
|
+
sellAmount?: string;
|
|
44
|
+
sellPercentage?: number;
|
|
45
|
+
buyerPrivateKeys: string[];
|
|
46
|
+
buyerRatios?: number[];
|
|
47
|
+
buyerAmounts?: string[];
|
|
48
|
+
tokenAddress: string;
|
|
49
|
+
routeParams: RouteParams;
|
|
50
|
+
config: XLayerEoaSwapConfig;
|
|
51
|
+
quoteToken?: string;
|
|
52
|
+
quoteTokenDecimals?: number;
|
|
53
|
+
disperseHopCount?: number;
|
|
54
|
+
startNonces?: number[];
|
|
55
|
+
}
|
|
56
|
+
export interface XLayerQuickBatchSwapResult {
|
|
57
|
+
signedTransactions: string[];
|
|
58
|
+
disperseHopWallets?: GeneratedWallet[];
|
|
59
|
+
profitHopWallets?: GeneratedWallet[];
|
|
60
|
+
metadata?: {
|
|
61
|
+
sellerAddress: string;
|
|
62
|
+
buyerAddresses: string[];
|
|
63
|
+
sellAmount: string;
|
|
64
|
+
estimatedOutput: string;
|
|
65
|
+
transferAmounts: string[];
|
|
66
|
+
profitAmount?: string;
|
|
67
|
+
useNativeToken: boolean;
|
|
68
|
+
disperseHopCount?: number;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export interface XLayerCrossSwapParams {
|
|
72
|
+
sellerPrivateKeys: string[];
|
|
73
|
+
sellAmounts: string[];
|
|
74
|
+
buyerPrivateKeys: string[];
|
|
75
|
+
tokenAddress: string;
|
|
76
|
+
routeParams: RouteParams;
|
|
77
|
+
config: XLayerEoaSwapConfig;
|
|
78
|
+
quoteToken?: string;
|
|
79
|
+
quoteTokenDecimals?: number;
|
|
80
|
+
buyersPerSell?: number;
|
|
81
|
+
disperseHopCount?: number;
|
|
82
|
+
}
|
|
83
|
+
export interface XLayerCrossSwapResult {
|
|
84
|
+
signedTransactions: string[];
|
|
85
|
+
rounds: Array<{
|
|
86
|
+
sellerAddress: string;
|
|
87
|
+
buyerAddresses: string[];
|
|
88
|
+
sellAmount: string;
|
|
89
|
+
bundleHash?: string;
|
|
90
|
+
}>;
|
|
91
|
+
disperseHopWallets?: GeneratedWallet[];
|
|
92
|
+
profitHopWallets?: GeneratedWallet[];
|
|
93
|
+
}
|
|
94
|
+
export declare function xlayerQuickBatchSwapMerkle(params: XLayerQuickBatchSwapParams): Promise<XLayerQuickBatchSwapResult>;
|
|
95
|
+
export declare function xlayerCrossSwapMerkle(params: XLayerCrossSwapParams): Promise<XLayerCrossSwapResult>;
|
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XLayer EOA 模式批量换手
|
|
3
|
+
*
|
|
4
|
+
* 专门为 XLayer 链设计,不影响 BSC 链的逻辑
|
|
5
|
+
* - 使用 PotatoSwap V3 Quoter
|
|
6
|
+
* - 使用 WOKB 作为 Wrapped Native Token
|
|
7
|
+
* - 支持多跳转账和利润刮取
|
|
8
|
+
*/
|
|
9
|
+
import { ethers, Contract, Wallet } from 'ethers';
|
|
10
|
+
import { calculateSellAmount } from '../utils/swap-helpers.js';
|
|
11
|
+
import { NonceManager, getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../utils/bundle-helpers.js';
|
|
12
|
+
import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from '../utils/constants.js';
|
|
13
|
+
import { quoteV2, quoteV3 } from '../utils/quote-helpers.js';
|
|
14
|
+
import { V2_ROUTER_ABI, V3_ROUTER02_ABI } from '../abis/common.js';
|
|
15
|
+
import { generateWallets } from '../utils/wallet.js';
|
|
16
|
+
// ==================== XLayer 专用常量 ====================
|
|
17
|
+
const XLAYER_CHAIN_ID = 196;
|
|
18
|
+
const XLAYER_CHAIN_NAME = 'XLAYER';
|
|
19
|
+
const WOKB_ADDRESS = ADDRESSES.XLAYER.WOKB;
|
|
20
|
+
const POTATO_V2_ROUTER = '0x881fb2f98c13d521009464e7d1cbf16e1b394e8e';
|
|
21
|
+
const POTATO_V3_ROUTER = '0x4e57fe71624fBCD5F1175C6B1a1e5d5fE2e4Da45';
|
|
22
|
+
const NATIVE_TRANSFER_GAS_LIMIT = 21055n;
|
|
23
|
+
const ERC20_TRANSFER_GAS_LIMIT = 65000n;
|
|
24
|
+
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
25
|
+
// ==================== 工具函数 ====================
|
|
26
|
+
function createXLayerContext(config) {
|
|
27
|
+
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
|
|
28
|
+
chainId: XLAYER_CHAIN_ID,
|
|
29
|
+
name: 'XLayer'
|
|
30
|
+
});
|
|
31
|
+
return { chainId: XLAYER_CHAIN_ID, provider };
|
|
32
|
+
}
|
|
33
|
+
function getGasLimit(config, defaultGas = 800000) {
|
|
34
|
+
if (config.gasLimit !== undefined) {
|
|
35
|
+
return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
|
|
36
|
+
}
|
|
37
|
+
const multiplier = config.gasLimitMultiplier ?? 1.0;
|
|
38
|
+
return BigInt(Math.ceil(defaultGas * multiplier));
|
|
39
|
+
}
|
|
40
|
+
async function getGasPrice(provider, config) {
|
|
41
|
+
const feeData = await provider.getFeeData();
|
|
42
|
+
let gasPrice = feeData.gasPrice || ethers.parseUnits('0.1', 'gwei');
|
|
43
|
+
if (config.minGasPriceGwei) {
|
|
44
|
+
const minGas = ethers.parseUnits(config.minGasPriceGwei.toString(), 'gwei');
|
|
45
|
+
if (gasPrice < minGas)
|
|
46
|
+
gasPrice = minGas;
|
|
47
|
+
}
|
|
48
|
+
if (config.maxGasPriceGwei) {
|
|
49
|
+
const maxGas = ethers.parseUnits(config.maxGasPriceGwei.toString(), 'gwei');
|
|
50
|
+
if (gasPrice > maxGas)
|
|
51
|
+
gasPrice = maxGas;
|
|
52
|
+
}
|
|
53
|
+
return gasPrice;
|
|
54
|
+
}
|
|
55
|
+
function getProfitRateBps(userType) {
|
|
56
|
+
if (userType === 'v0')
|
|
57
|
+
return PROFIT_CONFIG.RATE_BPS_V0;
|
|
58
|
+
if (userType === 'v1')
|
|
59
|
+
return PROFIT_CONFIG.RATE_BPS_V1;
|
|
60
|
+
return PROFIT_CONFIG.RATE_BPS_V0;
|
|
61
|
+
}
|
|
62
|
+
function calculateProfitAmount(estimatedOutput, userType) {
|
|
63
|
+
if (estimatedOutput <= 0n)
|
|
64
|
+
return 0n;
|
|
65
|
+
const rateBps = getProfitRateBps(userType);
|
|
66
|
+
return (estimatedOutput * BigInt(rateBps)) / 10000n;
|
|
67
|
+
}
|
|
68
|
+
// ==================== 报价函数 ====================
|
|
69
|
+
async function quoteSellOutput(provider, routeParams, sellAmountWei) {
|
|
70
|
+
console.log(`[XLayer quoteSellOutput] 开始报价, routeType=${routeParams.routeType}`);
|
|
71
|
+
if (routeParams.routeType === 'v2') {
|
|
72
|
+
const { v2Path } = routeParams;
|
|
73
|
+
const tokenIn = v2Path[0];
|
|
74
|
+
const tokenOut = v2Path[v2Path.length - 1];
|
|
75
|
+
const result = await quoteV2(provider, tokenIn, tokenOut, sellAmountWei, XLAYER_CHAIN_NAME);
|
|
76
|
+
if (result.amountOut > 0n) {
|
|
77
|
+
console.log(`[XLayer quoteSellOutput] V2 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
|
|
78
|
+
return result.amountOut;
|
|
79
|
+
}
|
|
80
|
+
throw new Error('V2 报价失败');
|
|
81
|
+
}
|
|
82
|
+
if (routeParams.routeType === 'v3-single') {
|
|
83
|
+
const params = routeParams;
|
|
84
|
+
console.log(`[XLayer quoteSellOutput] V3 单跳: ${params.v3TokenIn} → ${params.v3TokenOut}, fee=${params.v3Fee}`);
|
|
85
|
+
const result = await quoteV3(provider, params.v3TokenIn, params.v3TokenOut, sellAmountWei, XLAYER_CHAIN_NAME, params.v3Fee);
|
|
86
|
+
if (result.amountOut > 0n) {
|
|
87
|
+
console.log(`[XLayer quoteSellOutput] V3 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
|
|
88
|
+
return result.amountOut;
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`V3 单跳报价失败: tokenIn=${params.v3TokenIn}, tokenOut=${params.v3TokenOut}, fee=${params.v3Fee}`);
|
|
91
|
+
}
|
|
92
|
+
if (routeParams.routeType === 'v3-multi') {
|
|
93
|
+
const params = routeParams;
|
|
94
|
+
const tokenIn = params.v3ExactTokenIn;
|
|
95
|
+
const result = await quoteV3(provider, tokenIn, WOKB_ADDRESS, sellAmountWei, XLAYER_CHAIN_NAME);
|
|
96
|
+
if (result.amountOut > 0n) {
|
|
97
|
+
console.log(`[XLayer quoteSellOutput] V3 多跳报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
|
|
98
|
+
return result.amountOut;
|
|
99
|
+
}
|
|
100
|
+
throw new Error(`V3 多跳报价失败`);
|
|
101
|
+
}
|
|
102
|
+
throw new Error(`不支持的路由类型: ${routeParams.routeType}`);
|
|
103
|
+
}
|
|
104
|
+
async function getERC20ToOkbQuote(provider, tokenAddress, tokenAmount, version = 'v2', fee) {
|
|
105
|
+
if (tokenAmount <= 0n)
|
|
106
|
+
return 0n;
|
|
107
|
+
const tokenLower = tokenAddress.toLowerCase();
|
|
108
|
+
const wokbLower = WOKB_ADDRESS.toLowerCase();
|
|
109
|
+
if (tokenLower === wokbLower)
|
|
110
|
+
return tokenAmount;
|
|
111
|
+
if (version === 'v3') {
|
|
112
|
+
const result = await quoteV3(provider, tokenAddress, WOKB_ADDRESS, tokenAmount, XLAYER_CHAIN_NAME, fee);
|
|
113
|
+
if (result.amountOut > 0n) {
|
|
114
|
+
console.log(`[XLayer getERC20ToOkbQuote] V3 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
|
|
115
|
+
return result.amountOut;
|
|
116
|
+
}
|
|
117
|
+
return 0n;
|
|
118
|
+
}
|
|
119
|
+
const result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, tokenAmount, XLAYER_CHAIN_NAME);
|
|
120
|
+
if (result.amountOut > 0n) {
|
|
121
|
+
console.log(`[XLayer getERC20ToOkbQuote] V2 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
|
|
122
|
+
return result.amountOut;
|
|
123
|
+
}
|
|
124
|
+
return 0n;
|
|
125
|
+
}
|
|
126
|
+
function generateDisperseHopPaths(targetAddresses, hopCount, provider) {
|
|
127
|
+
if (hopCount <= 0) {
|
|
128
|
+
return targetAddresses.map(addr => ({
|
|
129
|
+
targetAddress: addr,
|
|
130
|
+
hopWallets: [],
|
|
131
|
+
hopWalletsInfo: []
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
return targetAddresses.map(targetAddress => {
|
|
135
|
+
const hopWalletsInfo = generateWallets(hopCount);
|
|
136
|
+
const hopWallets = hopWalletsInfo.map(w => new Wallet(w.privateKey, provider));
|
|
137
|
+
return { targetAddress, hopWallets, hopWalletsInfo };
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
async function buildNativeHopChain(payer, path, finalAmount, gasPrice, txType, payerNonce) {
|
|
141
|
+
const signedTxs = [];
|
|
142
|
+
const hopCount = path.hopWallets.length;
|
|
143
|
+
if (hopCount === 0) {
|
|
144
|
+
const tx = {
|
|
145
|
+
to: path.targetAddress,
|
|
146
|
+
value: finalAmount,
|
|
147
|
+
nonce: payerNonce,
|
|
148
|
+
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
149
|
+
chainId: XLAYER_CHAIN_ID,
|
|
150
|
+
type: txType
|
|
151
|
+
};
|
|
152
|
+
if (txType === 2) {
|
|
153
|
+
tx.maxFeePerGas = gasPrice;
|
|
154
|
+
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
tx.gasPrice = gasPrice;
|
|
158
|
+
}
|
|
159
|
+
signedTxs.push(await payer.signTransaction(tx));
|
|
160
|
+
return signedTxs;
|
|
161
|
+
}
|
|
162
|
+
const hopGasCost = NATIVE_TRANSFER_GAS_LIMIT * gasPrice;
|
|
163
|
+
const amountsPerHop = [];
|
|
164
|
+
for (let i = 0; i < hopCount; i++) {
|
|
165
|
+
const remainingHops = hopCount - i;
|
|
166
|
+
amountsPerHop.push(finalAmount + hopGasCost * BigInt(remainingHops));
|
|
167
|
+
}
|
|
168
|
+
// payer → hop1
|
|
169
|
+
const firstTx = {
|
|
170
|
+
to: path.hopWallets[0].address,
|
|
171
|
+
value: amountsPerHop[0],
|
|
172
|
+
nonce: payerNonce,
|
|
173
|
+
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
174
|
+
chainId: XLAYER_CHAIN_ID,
|
|
175
|
+
type: txType
|
|
176
|
+
};
|
|
177
|
+
if (txType === 2) {
|
|
178
|
+
firstTx.maxFeePerGas = gasPrice;
|
|
179
|
+
firstTx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
firstTx.gasPrice = gasPrice;
|
|
183
|
+
}
|
|
184
|
+
signedTxs.push(await payer.signTransaction(firstTx));
|
|
185
|
+
// hop1 → hop2 → ... → target
|
|
186
|
+
for (let i = 0; i < hopCount; i++) {
|
|
187
|
+
const fromWallet = path.hopWallets[i];
|
|
188
|
+
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
189
|
+
const amount = i === hopCount - 1 ? finalAmount : amountsPerHop[i + 1];
|
|
190
|
+
const tx = {
|
|
191
|
+
to: toAddress,
|
|
192
|
+
value: amount,
|
|
193
|
+
nonce: 0,
|
|
194
|
+
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
195
|
+
chainId: XLAYER_CHAIN_ID,
|
|
196
|
+
type: txType
|
|
197
|
+
};
|
|
198
|
+
if (txType === 2) {
|
|
199
|
+
tx.maxFeePerGas = gasPrice;
|
|
200
|
+
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
tx.gasPrice = gasPrice;
|
|
204
|
+
}
|
|
205
|
+
signedTxs.push(await fromWallet.signTransaction(tx));
|
|
206
|
+
}
|
|
207
|
+
return signedTxs;
|
|
208
|
+
}
|
|
209
|
+
// ==================== XLayer 快捷批量换手 ====================
|
|
210
|
+
export async function xlayerQuickBatchSwapMerkle(params) {
|
|
211
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerRatios, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, disperseHopCount = 0, startNonces } = params;
|
|
212
|
+
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
213
|
+
const context = createXLayerContext(config);
|
|
214
|
+
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
215
|
+
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
216
|
+
// 校验卖出路径输出代币
|
|
217
|
+
let sellOutputToken;
|
|
218
|
+
if (routeParams.routeType === 'v2') {
|
|
219
|
+
sellOutputToken = routeParams.v2Path[routeParams.v2Path.length - 1];
|
|
220
|
+
}
|
|
221
|
+
else if (routeParams.routeType === 'v3-single') {
|
|
222
|
+
sellOutputToken = routeParams.v3TokenOut;
|
|
223
|
+
}
|
|
224
|
+
if (useNativeToken) {
|
|
225
|
+
if (!sellOutputToken || sellOutputToken.toLowerCase() !== WOKB_ADDRESS.toLowerCase()) {
|
|
226
|
+
throw new Error(`原生代币模式要求卖出路径以 WOKB 结尾(当前输出: ${sellOutputToken || '未知'})`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// 动态计算最大买方数量
|
|
230
|
+
const fixedOverhead = 1 + 1 + PROFIT_HOP_COUNT + 1;
|
|
231
|
+
const maxTxs = 50 - fixedOverhead;
|
|
232
|
+
const MAX_BUYERS = Math.max(1, Math.floor(maxTxs / (disperseHopCount + 2)));
|
|
233
|
+
if (buyerPrivateKeys.length === 0) {
|
|
234
|
+
throw new Error('至少需要一个买方钱包');
|
|
235
|
+
}
|
|
236
|
+
if (buyerPrivateKeys.length > MAX_BUYERS) {
|
|
237
|
+
throw new Error(`买方钱包数量超过限制: ${buyerPrivateKeys.length} > ${MAX_BUYERS}`);
|
|
238
|
+
}
|
|
239
|
+
const nonceManager = new NonceManager(context.provider);
|
|
240
|
+
const finalGasLimit = getGasLimit(config);
|
|
241
|
+
const txType = config.txType ?? 0;
|
|
242
|
+
const [gasPrice, sellAmountResult] = await Promise.all([
|
|
243
|
+
getGasPrice(context.provider, config),
|
|
244
|
+
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
245
|
+
]);
|
|
246
|
+
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
247
|
+
console.log(`[xlayerQuickBatchSwapMerkle] 模式: ${useNativeToken ? 'OKB' : 'ERC20'}, 卖出: ${ethers.formatUnits(sellAmountWei, decimals)}`);
|
|
248
|
+
// 获取卖出报价
|
|
249
|
+
const estimatedOutput = await quoteSellOutput(context.provider, routeParams, sellAmountWei);
|
|
250
|
+
// 计算利润
|
|
251
|
+
let profitAmount;
|
|
252
|
+
if (useNativeToken) {
|
|
253
|
+
profitAmount = calculateProfitAmount(estimatedOutput, config.userType);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
const version = routeParams.routeType === 'v2' ? 'v2' : 'v3';
|
|
257
|
+
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
258
|
+
const estimatedOkbValue = await getERC20ToOkbQuote(context.provider, quoteToken, estimatedOutput, version, fee);
|
|
259
|
+
profitAmount = calculateProfitAmount(estimatedOkbValue, config.userType);
|
|
260
|
+
}
|
|
261
|
+
const distributableAmount = estimatedOutput - (useNativeToken ? profitAmount : 0n);
|
|
262
|
+
// 计算每个买方分到的金额
|
|
263
|
+
let transferAmountsWei;
|
|
264
|
+
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
265
|
+
transferAmountsWei = buyerAmounts.map(amt => useNativeToken ? ethers.parseEther(amt) : ethers.parseUnits(amt, quoteTokenDecimals));
|
|
266
|
+
}
|
|
267
|
+
else if (buyerRatios && buyerRatios.length === buyers.length) {
|
|
268
|
+
transferAmountsWei = buyerRatios.map(ratio => (distributableAmount * BigInt(Math.round(ratio * 10000))) / 10000n);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
throw new Error('必须提供 buyerRatios 或 buyerAmounts');
|
|
272
|
+
}
|
|
273
|
+
// 获取贿赂金额
|
|
274
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
275
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
276
|
+
: 0n;
|
|
277
|
+
// 规划 Nonce
|
|
278
|
+
let sellerNonce = startNonces && startNonces.length > 0
|
|
279
|
+
? startNonces[0]
|
|
280
|
+
: await nonceManager.getNextNonce(seller);
|
|
281
|
+
const deadline = getDeadline();
|
|
282
|
+
// 1. 贿赂交易
|
|
283
|
+
let bribeTx = null;
|
|
284
|
+
if (bribeAmount > 0n) {
|
|
285
|
+
bribeTx = await seller.signTransaction({
|
|
286
|
+
to: BLOCKRAZOR_BUILDER_EOA,
|
|
287
|
+
value: bribeAmount,
|
|
288
|
+
nonce: sellerNonce++,
|
|
289
|
+
gasPrice,
|
|
290
|
+
gasLimit: 21000n,
|
|
291
|
+
chainId: XLAYER_CHAIN_ID,
|
|
292
|
+
type: txType
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
// 2. 卖出交易
|
|
296
|
+
const v3RouterIface = new ethers.Interface(V3_ROUTER02_ABI);
|
|
297
|
+
let sellUnsigned;
|
|
298
|
+
if (routeParams.routeType === 'v2') {
|
|
299
|
+
const { v2Path } = routeParams;
|
|
300
|
+
const v2RouterSeller = new Contract(POTATO_V2_ROUTER, V2_ROUTER_ABI, seller);
|
|
301
|
+
if (useNativeToken) {
|
|
302
|
+
sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else if (routeParams.routeType === 'v3-single') {
|
|
309
|
+
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
310
|
+
const v3RouterSeller = new Contract(POTATO_V3_ROUTER, V3_ROUTER02_ABI, seller);
|
|
311
|
+
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
312
|
+
tokenIn: v3TokenIn,
|
|
313
|
+
tokenOut: v3TokenOut,
|
|
314
|
+
fee: v3Fee,
|
|
315
|
+
recipient: useNativeToken ? POTATO_V3_ROUTER : seller.address,
|
|
316
|
+
amountIn: sellAmountWei,
|
|
317
|
+
amountOutMinimum: 0n,
|
|
318
|
+
sqrtPriceLimitX96: 0n
|
|
319
|
+
}]);
|
|
320
|
+
if (useNativeToken) {
|
|
321
|
+
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
322
|
+
sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData]);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
throw new Error('V3 多跳路由暂不支持');
|
|
330
|
+
}
|
|
331
|
+
const signedSell = await seller.signTransaction({
|
|
332
|
+
...sellUnsigned,
|
|
333
|
+
from: seller.address,
|
|
334
|
+
nonce: sellerNonce++,
|
|
335
|
+
gasLimit: finalGasLimit,
|
|
336
|
+
gasPrice,
|
|
337
|
+
chainId: XLAYER_CHAIN_ID,
|
|
338
|
+
type: txType
|
|
339
|
+
});
|
|
340
|
+
// 3. 转账交易
|
|
341
|
+
const buyerGasCost = gasPrice * finalGasLimit;
|
|
342
|
+
const hopPaths = generateDisperseHopPaths(buyers.map(b => b.address), disperseHopCount, context.provider);
|
|
343
|
+
const allHopWallets = [];
|
|
344
|
+
hopPaths.forEach(path => allHopWallets.push(...path.hopWalletsInfo));
|
|
345
|
+
let transferTxs = [];
|
|
346
|
+
if (disperseHopCount === 0) {
|
|
347
|
+
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
348
|
+
sellerNonce += buyers.length;
|
|
349
|
+
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
350
|
+
const transferValue = transferAmountsWei[i] + buyerGasCost;
|
|
351
|
+
return seller.signTransaction({
|
|
352
|
+
to: buyer.address,
|
|
353
|
+
value: transferValue,
|
|
354
|
+
nonce: transferNonces[i],
|
|
355
|
+
gasPrice,
|
|
356
|
+
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
357
|
+
chainId: XLAYER_CHAIN_ID,
|
|
358
|
+
type: txType
|
|
359
|
+
});
|
|
360
|
+
}));
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
const hopChains = await Promise.all(hopPaths.map((path, i) => {
|
|
364
|
+
const finalAmount = transferAmountsWei[i] + buyerGasCost;
|
|
365
|
+
const payerNonce = sellerNonce + i;
|
|
366
|
+
return buildNativeHopChain(seller, path, finalAmount, gasPrice, txType, payerNonce);
|
|
367
|
+
}));
|
|
368
|
+
transferTxs = hopChains.flat();
|
|
369
|
+
sellerNonce += buyers.length;
|
|
370
|
+
}
|
|
371
|
+
// 4. 买入交易
|
|
372
|
+
const buyerNonces = startNonces && startNonces.length > 1
|
|
373
|
+
? startNonces.slice(1)
|
|
374
|
+
: await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
375
|
+
const signedBuys = await Promise.all(buyers.map(async (buyer, i) => {
|
|
376
|
+
const buyAmount = transferAmountsWei[i];
|
|
377
|
+
const buyValue = useNativeToken ? buyAmount : 0n;
|
|
378
|
+
let buyUnsigned;
|
|
379
|
+
if (routeParams.routeType === 'v2') {
|
|
380
|
+
const { v2Path } = routeParams;
|
|
381
|
+
const reversePath = [...v2Path].reverse();
|
|
382
|
+
const v2RouterBuyer = new Contract(POTATO_V2_ROUTER, V2_ROUTER_ABI, buyer);
|
|
383
|
+
if (useNativeToken) {
|
|
384
|
+
buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmount, 0n, reversePath, buyer.address, deadline);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
else if (routeParams.routeType === 'v3-single') {
|
|
391
|
+
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
392
|
+
const v3RouterBuyer = new Contract(POTATO_V3_ROUTER, V3_ROUTER02_ABI, buyer);
|
|
393
|
+
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
394
|
+
tokenIn: v3TokenOut,
|
|
395
|
+
tokenOut: v3TokenIn,
|
|
396
|
+
fee: v3Fee,
|
|
397
|
+
recipient: buyer.address,
|
|
398
|
+
amountIn: buyAmount,
|
|
399
|
+
amountOutMinimum: 0n,
|
|
400
|
+
sqrtPriceLimitX96: 0n
|
|
401
|
+
}]);
|
|
402
|
+
buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
throw new Error('V3 多跳路由暂不支持');
|
|
406
|
+
}
|
|
407
|
+
return buyer.signTransaction({
|
|
408
|
+
...buyUnsigned,
|
|
409
|
+
from: buyer.address,
|
|
410
|
+
nonce: buyerNonces[i],
|
|
411
|
+
gasLimit: finalGasLimit,
|
|
412
|
+
gasPrice,
|
|
413
|
+
chainId: XLAYER_CHAIN_ID,
|
|
414
|
+
type: txType
|
|
415
|
+
});
|
|
416
|
+
}));
|
|
417
|
+
nonceManager.clearTemp();
|
|
418
|
+
// 组装交易数组
|
|
419
|
+
const signedTransactions = [];
|
|
420
|
+
if (bribeTx)
|
|
421
|
+
signedTransactions.push(bribeTx);
|
|
422
|
+
signedTransactions.push(signedSell);
|
|
423
|
+
signedTransactions.push(...transferTxs);
|
|
424
|
+
signedTransactions.push(...signedBuys);
|
|
425
|
+
// 5. 利润多跳转账
|
|
426
|
+
let profitHopWallets;
|
|
427
|
+
if (profitAmount > 0n) {
|
|
428
|
+
const profitHopResult = await buildProfitHopTransactions({
|
|
429
|
+
provider: context.provider,
|
|
430
|
+
payerWallet: seller,
|
|
431
|
+
profitAmount,
|
|
432
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
433
|
+
hopCount: PROFIT_HOP_COUNT,
|
|
434
|
+
gasPrice,
|
|
435
|
+
chainId: XLAYER_CHAIN_ID,
|
|
436
|
+
txType,
|
|
437
|
+
startNonce: sellerNonce++
|
|
438
|
+
});
|
|
439
|
+
signedTransactions.push(...profitHopResult.signedTransactions);
|
|
440
|
+
profitHopWallets = profitHopResult.hopWallets;
|
|
441
|
+
}
|
|
442
|
+
console.log(`[xlayerQuickBatchSwapMerkle] 完成: ${signedTransactions.length} 笔交易`);
|
|
443
|
+
return {
|
|
444
|
+
signedTransactions,
|
|
445
|
+
disperseHopWallets: allHopWallets.length > 0 ? allHopWallets : undefined,
|
|
446
|
+
profitHopWallets,
|
|
447
|
+
metadata: {
|
|
448
|
+
sellerAddress: seller.address,
|
|
449
|
+
buyerAddresses: buyers.map(b => b.address),
|
|
450
|
+
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
451
|
+
estimatedOutput: ethers.formatEther(estimatedOutput),
|
|
452
|
+
transferAmounts: transferAmountsWei.map(amt => ethers.formatEther(amt)),
|
|
453
|
+
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
|
|
454
|
+
useNativeToken,
|
|
455
|
+
disperseHopCount: disperseHopCount > 0 ? disperseHopCount : undefined
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
// ==================== XLayer 交叉换手 ====================
|
|
460
|
+
export async function xlayerCrossSwapMerkle(params) {
|
|
461
|
+
const { sellerPrivateKeys, sellAmounts, buyerPrivateKeys, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, buyersPerSell, disperseHopCount = 0 } = params;
|
|
462
|
+
if (sellerPrivateKeys.length === 0)
|
|
463
|
+
throw new Error('至少需要一个卖方');
|
|
464
|
+
if (sellerPrivateKeys.length !== sellAmounts.length)
|
|
465
|
+
throw new Error('sellAmounts 长度必须与卖方数量一致');
|
|
466
|
+
if (buyerPrivateKeys.length === 0)
|
|
467
|
+
throw new Error('至少需要一个买方');
|
|
468
|
+
const context = createXLayerContext(config);
|
|
469
|
+
const nonceManager = new NonceManager(context.provider);
|
|
470
|
+
const allSellerWallets = sellerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
471
|
+
const allBuyerWallets = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
472
|
+
// 预先获取所有钱包的初始 nonce
|
|
473
|
+
const addressToNonce = new Map();
|
|
474
|
+
for (const wallet of allSellerWallets) {
|
|
475
|
+
if (!addressToNonce.has(wallet.address)) {
|
|
476
|
+
addressToNonce.set(wallet.address, await nonceManager.getNextNonce(wallet));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
for (const wallet of allBuyerWallets) {
|
|
480
|
+
if (!addressToNonce.has(wallet.address)) {
|
|
481
|
+
addressToNonce.set(wallet.address, await nonceManager.getNextNonce(wallet));
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
console.log(`[xlayerCrossSwapMerkle] 初始化: ${sellerPrivateKeys.length} 卖方, ${buyerPrivateKeys.length} 买方`);
|
|
485
|
+
const allSigned = [];
|
|
486
|
+
const allDisperse = [];
|
|
487
|
+
const allProfit = [];
|
|
488
|
+
const rounds = [];
|
|
489
|
+
let buyerCursor = 0;
|
|
490
|
+
const buyerBatchSize = Math.max(1, buyersPerSell ?? buyerPrivateKeys.length);
|
|
491
|
+
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
492
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
493
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
494
|
+
: 0n;
|
|
495
|
+
const hasBribe = bribeAmount > 0n;
|
|
496
|
+
for (let i = 0; i < sellerPrivateKeys.length; i++) {
|
|
497
|
+
const sellerPk = sellerPrivateKeys[i];
|
|
498
|
+
const sellAmount = sellAmounts[i];
|
|
499
|
+
const sellerWallet = allSellerWallets[i];
|
|
500
|
+
const roundBuyerPks = [];
|
|
501
|
+
const roundBuyerWallets = [];
|
|
502
|
+
for (let k = 0; k < buyerBatchSize; k++) {
|
|
503
|
+
const idx = buyerCursor % buyerPrivateKeys.length;
|
|
504
|
+
roundBuyerPks.push(buyerPrivateKeys[idx]);
|
|
505
|
+
roundBuyerWallets.push(allBuyerWallets[idx]);
|
|
506
|
+
buyerCursor++;
|
|
507
|
+
}
|
|
508
|
+
const sellerNonce = addressToNonce.get(sellerWallet.address);
|
|
509
|
+
const roundBuyerNonces = roundBuyerWallets.map(w => {
|
|
510
|
+
const nonce = addressToNonce.get(w.address);
|
|
511
|
+
addressToNonce.set(w.address, nonce + 1);
|
|
512
|
+
return nonce;
|
|
513
|
+
});
|
|
514
|
+
const ratio = 1 / roundBuyerPks.length;
|
|
515
|
+
const buyerRatios = roundBuyerPks.map(() => ratio);
|
|
516
|
+
const startNonces = [sellerNonce, ...roundBuyerNonces];
|
|
517
|
+
// 预先计算卖方 nonce 消耗
|
|
518
|
+
let sellerNonceConsumed = 0;
|
|
519
|
+
if (hasBribe)
|
|
520
|
+
sellerNonceConsumed += 1;
|
|
521
|
+
sellerNonceConsumed += 1; // 卖出
|
|
522
|
+
sellerNonceConsumed += roundBuyerPks.length; // 转账
|
|
523
|
+
sellerNonceConsumed += 1; // 利润
|
|
524
|
+
const res = await xlayerQuickBatchSwapMerkle({
|
|
525
|
+
sellerPrivateKey: sellerPk,
|
|
526
|
+
sellAmount,
|
|
527
|
+
buyerPrivateKeys: roundBuyerPks,
|
|
528
|
+
buyerRatios,
|
|
529
|
+
tokenAddress,
|
|
530
|
+
routeParams,
|
|
531
|
+
config,
|
|
532
|
+
quoteToken,
|
|
533
|
+
quoteTokenDecimals,
|
|
534
|
+
disperseHopCount,
|
|
535
|
+
startNonces
|
|
536
|
+
});
|
|
537
|
+
addressToNonce.set(sellerWallet.address, sellerNonce + sellerNonceConsumed);
|
|
538
|
+
console.log(`[xlayerCrossSwapMerkle] 轮次 ${i + 1}: 卖方=${sellerWallet.address.slice(0, 10)}..., 买方=${roundBuyerPks.length}, 交易=${res.signedTransactions.length}`);
|
|
539
|
+
allSigned.push(...res.signedTransactions);
|
|
540
|
+
if (res.disperseHopWallets)
|
|
541
|
+
allDisperse.push(...res.disperseHopWallets);
|
|
542
|
+
if (res.profitHopWallets)
|
|
543
|
+
allProfit.push(...res.profitHopWallets);
|
|
544
|
+
rounds.push({
|
|
545
|
+
sellerAddress: res.metadata?.sellerAddress || '',
|
|
546
|
+
buyerAddresses: res.metadata?.buyerAddresses || [],
|
|
547
|
+
sellAmount,
|
|
548
|
+
bundleHash: undefined
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
nonceManager.clearTemp();
|
|
552
|
+
console.log(`[xlayerCrossSwapMerkle] 完成: ${rounds.length} 轮, ${allSigned.length} 笔交易`);
|
|
553
|
+
return {
|
|
554
|
+
signedTransactions: allSigned,
|
|
555
|
+
rounds,
|
|
556
|
+
disperseHopWallets: allDisperse.length > 0 ? allDisperse : undefined,
|
|
557
|
+
profitHopWallets: allProfit.length > 0 ? allProfit : undefined
|
|
558
|
+
};
|
|
559
|
+
}
|
package/dist/xlayer/index.d.ts
CHANGED
|
@@ -92,6 +92,7 @@ export { buildDexBatchSellOps, buildDexBatchSellOpsV3, type DexBatchSellParams,
|
|
|
92
92
|
export { buildDisperseFromSingleOwnerOpsWithProfit, buildTransfersWithProfit, } from './aa-transfer-profit.js';
|
|
93
93
|
export { buildWashOps, type WashOpsParams, type WashOpsResult, type WashPoolType, type WashUserType, } from './wash-ops.js';
|
|
94
94
|
export { buildBundleBuyOps, buildBundleSellOps, buildBundleProfitOp, filterOutProfitOps, type BundleBuyOpsParams, type BundleSellOpsParams, type BundleProfitOpParams, type BundleOpsResult, type BundlePoolType, type BundleUserType, } from './bundle-buy-first.js';
|
|
95
|
+
export { xlayerQuickBatchSwapMerkle, xlayerCrossSwapMerkle, type XLayerEoaSwapConfig, type XLayerQuickBatchSwapParams, type XLayerQuickBatchSwapResult, type XLayerCrossSwapParams, type XLayerCrossSwapResult, type UserType as XLayerUserType, type RouteParams as XLayerRouteParams, type V2RouteParams as XLayerV2RouteParams, type V3SingleRouteParams as XLayerV3SingleRouteParams, } from './eoa-bundle-swap.js';
|
|
95
96
|
export declare const xlayer: {
|
|
96
97
|
bundleBuy: (params: import("./types.js").BundleBuyParams) => Promise<import("./types.js").BundleBuyResult>;
|
|
97
98
|
bundleSell: (params: import("./types.js").BundleSellParams) => Promise<import("./types.js").BundleSellResult>;
|
package/dist/xlayer/index.js
CHANGED
|
@@ -172,6 +172,10 @@ export { buildWashOps, } from './wash-ops.js';
|
|
|
172
172
|
// ============================================================================
|
|
173
173
|
export { buildBundleBuyOps, buildBundleSellOps, buildBundleProfitOp, filterOutProfitOps, } from './bundle-buy-first.js';
|
|
174
174
|
// ============================================================================
|
|
175
|
+
// ✅ XLayer EOA 模式换手(不影响 BSC 链)
|
|
176
|
+
// ============================================================================
|
|
177
|
+
export { xlayerQuickBatchSwapMerkle, xlayerCrossSwapMerkle, } from './eoa-bundle-swap.js';
|
|
178
|
+
// ============================================================================
|
|
175
179
|
// 便捷重导出
|
|
176
180
|
// ============================================================================
|
|
177
181
|
// 将最常用的函数放在默认导出对象中
|