four-flap-meme-sdk 2.0.0 → 2.2.0
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/__tests__/subpath-exports.test.js +64 -0
- package/dist/chains/bsc/iro.d.ts +5 -0
- package/dist/chains/bsc/iro.js +4 -0
- package/dist/chains/eni/flat-aliases.d.ts +10 -0
- package/dist/chains/eni/flat-aliases.js +8 -0
- package/dist/chains/eni/index.d.ts +1 -0
- package/dist/chains/eni/index.js +1 -0
- package/dist/chains/index.d.ts +13 -0
- package/dist/chains/index.js +13 -0
- package/dist/chains/xlayer/eip7702/flat-aliases.d.ts +13 -0
- package/dist/chains/xlayer/eip7702/flat-aliases.js +10 -0
- package/dist/chains/xlayer/eip7702/index.d.ts +1 -0
- package/dist/chains/xlayer/eip7702/index.js +1 -0
- package/dist/chains/xlayer/index.d.ts +3 -2
- package/dist/chains/xlayer/index.js +4 -7
- package/dist/flap/index.d.ts +10 -0
- package/dist/flap/index.js +8 -0
- package/dist/merkle/index.d.ts +12 -0
- package/dist/merkle/index.js +11 -0
- package/dist/shared/constants/index.d.ts +2 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/vanity/index.d.ts +5 -0
- package/dist/vanity/index.js +5 -0
- package/package.json +93 -2
- package/dist/chains/bsc/four/disperse.d.ts +0 -12
- package/dist/chains/bsc/four/disperse.js +0 -470
- package/dist/chains/bsc/four/pairwise.d.ts +0 -7
- package/dist/chains/bsc/four/pairwise.js +0 -308
- package/dist/chains/bsc/four/submit/blockrazor.d.ts +0 -18
- package/dist/chains/bsc/four/submit/blockrazor.js +0 -86
- package/dist/chains/bsc/four/submit/direct.d.ts +0 -66
- package/dist/chains/bsc/four/submit/direct.js +0 -452
- package/dist/chains/bsc/four/submit/helpers.d.ts +0 -18
- package/dist/chains/bsc/four/submit/helpers.js +0 -57
- package/dist/chains/bsc/four/submit/index.d.ts +0 -12
- package/dist/chains/bsc/four/submit/index.js +0 -11
- package/dist/chains/bsc/four/submit/merkle.d.ts +0 -18
- package/dist/chains/bsc/four/submit/merkle.js +0 -74
- package/dist/chains/bsc/four/submit/types.d.ts +0 -143
- package/dist/chains/bsc/four/swap/index.d.ts +0 -32
- package/dist/chains/bsc/four/swap/index.js +0 -829
- package/dist/chains/bsc/four/swap/types.d.ts +0 -70
- package/dist/chains/bsc/four/swap/types.js +0 -1
- package/dist/chains/bsc/four/sweep.d.ts +0 -13
- package/dist/chains/bsc/four/sweep.js +0 -788
- package/dist/chains/bsc/four/utils/index.d.ts +0 -20
- package/dist/chains/bsc/four/utils/index.js +0 -1558
- package/dist/chains/bsc/four/utils/types.d.ts +0 -1
- package/dist/chains/bsc/four/utils/types.js +0 -1
- package/dist/chains/bsc/pancake/bundle-buy-first/index.d.ts +0 -8
- package/dist/chains/bsc/pancake/bundle-buy-first/index.js +0 -907
- package/dist/chains/bsc/pancake/bundle-buy-first/types.d.ts +0 -73
- package/dist/chains/bsc/pancake/bundle-buy-first/types.js +0 -1
- package/dist/chains/bsc/pancake/bundle-swap/helpers.d.ts +0 -102
- package/dist/chains/bsc/pancake/bundle-swap/helpers.js +0 -572
- package/dist/chains/bsc/pancake/bundle-swap/index.d.ts +0 -50
- package/dist/chains/bsc/pancake/bundle-swap/index.js +0 -1066
- package/dist/chains/bsc/pancake/bundle-swap/types.d.ts +0 -202
- package/dist/chains/bsc/pancake/bundle-swap/types.js +0 -3
- package/dist/chains/xlayer/eip7702/bundle-swap/index.d.ts +0 -72
- package/dist/chains/xlayer/eip7702/bundle-swap/index.js +0 -921
- package/dist/chains/xlayer/eip7702/bundle-swap/types.d.ts +0 -65
- package/dist/chains/xlayer/eip7702/bundle-swap/types.js +0 -1
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.d.ts +0 -128
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.js +0 -857
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.d.ts +0 -85
- package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.js +0 -1
- package/dist/chains/xlayer/eip7702/volume/index.d.ts +0 -96
- package/dist/chains/xlayer/eip7702/volume/index.js +0 -793
- package/dist/chains/xlayer/eip7702/volume/types.d.ts +0 -124
- package/dist/chains/xlayer/eip7702/volume/types.js +0 -1
- package/dist/chains/xlayer/eoa/types-core.d.ts +0 -363
- package/dist/chains/xlayer/eoa/types-core.js +0 -53
- package/dist/chains/xlayer/eoa/types-create.d.ts +0 -413
- package/dist/chains/xlayer/eoa/types-create.js +0 -9
- package/dist/chains/xlayer/eoa/types-volume.d.ts +0 -209
- package/dist/chains/xlayer/eoa/types-volume.js +0 -13
- package/dist/dex/direct-router/index.d.ts +0 -70
- package/dist/dex/direct-router/index.js +0 -1410
- package/dist/dex/direct-router/types.d.ts +0 -81
- package/dist/dex/direct-router/types.js +0 -1
- package/dist/shared/abis/TaxToken.json +0 -969
- package/dist/shared/abis/TokenManager.json +0 -836
- package/dist/shared/abis/TokenManager2.json +0 -136
- package/dist/shared/abis/TokenManagerHelper3.json +0 -993
- package/dist/shared/abis 2/TaxToken.json +0 -105
- package/dist/shared/abis 2/TokenManager.json +0 -836
- package/dist/shared/abis 2/TokenManager2.json +0 -60
- package/dist/shared/abis 2/TokenManagerHelper3.json +0 -993
- package/dist/shared/abis 2/common.d.ts +0 -85
- package/dist/shared/abis 2/common.js +0 -254
- package/dist/shared/abis 2/index.d.ts +0 -8
- package/dist/shared/abis 2/index.js +0 -8
- package/dist/shared/clients 2/blockrazor.d.ts +0 -314
- package/dist/shared/clients 2/blockrazor.js +0 -596
- package/dist/shared/clients 2/club48.d.ts +0 -154
- package/dist/shared/clients 2/club48.js +0 -331
- package/dist/shared/clients 2/emitservice.d.ts +0 -47
- package/dist/shared/clients 2/emitservice.js +0 -44
- package/dist/shared/clients 2/four.d.ts +0 -132
- package/dist/shared/clients 2/four.js +0 -281
- package/dist/shared/clients 2/merkle.d.ts +0 -210
- package/dist/shared/clients 2/merkle.js +0 -400
- package/dist/shared/flap/__tests__/curve.test.d.ts +0 -1
- package/dist/shared/flap/__tests__/curve.test.js +0 -85
- package/dist/shared/flap/portal/index.d.ts +0 -12
- package/dist/shared/flap/portal/index.js +0 -11
- package/dist/shared/flap/portal/portal.d.ts +0 -47
- package/dist/shared/flap/portal/portal.js +0 -218
- package/dist/shared/flap/portal/types.d.ts +0 -227
- package/dist/shared/flap/portal/types.js +0 -80
- package/dist/shared/flap/portal/writer.d.ts +0 -121
- package/dist/shared/flap/portal/writer.js +0 -265
- package/dist/shared/flap/portal-bundle-merkle/core/index.d.ts +0 -18
- package/dist/shared/flap/portal-bundle-merkle/core/index.js +0 -938
- package/dist/shared/flap/portal-bundle-merkle/core/types.d.ts +0 -1
- package/dist/shared/flap/portal-bundle-merkle/core/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/swap/index.d.ts +0 -42
- package/dist/shared/flap/portal-bundle-merkle/swap/index.js +0 -1448
- package/dist/shared/flap/portal-bundle-merkle/swap/types.d.ts +0 -84
- package/dist/shared/flap/portal-bundle-merkle/swap/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/utils/index.d.ts +0 -17
- package/dist/shared/flap/portal-bundle-merkle/utils/index.js +0 -1024
- package/dist/shared/flap/portal-bundle-merkle/utils/types.d.ts +0 -16
- package/dist/shared/flap/portal-bundle-merkle/utils/types.js +0 -1
- package/dist/shared/flap/portal-bundle-merkle/utils-helpers.d.ts +0 -100
- package/dist/shared/flap/portal-bundle-merkle/utils-helpers.js +0 -133
- package/dist/shared/flap 2/__tests__/curve.test.d.ts +0 -1
- package/dist/shared/flap 2/__tests__/curve.test.js +0 -85
- package/dist/shared/flap 2/abi.d.ts +0 -4
- package/dist/shared/flap 2/abi.js +0 -4
- package/dist/shared/flap 2/constants.d.ts +0 -128
- package/dist/shared/flap 2/constants.js +0 -143
- package/dist/shared/flap 2/curve.d.ts +0 -33
- package/dist/shared/flap 2/curve.js +0 -84
- package/dist/shared/flap 2/errors.d.ts +0 -37
- package/dist/shared/flap 2/errors.js +0 -114
- package/dist/shared/flap 2/index.d.ts +0 -22
- package/dist/shared/flap 2/index.js +0 -33
- package/dist/shared/flap 2/ipfs.d.ts +0 -21
- package/dist/shared/flap 2/ipfs.js +0 -38
- package/dist/shared/flap 2/meta.d.ts +0 -30
- package/dist/shared/flap 2/meta.js +0 -195
- package/dist/shared/flap 2/permit.d.ts +0 -16
- package/dist/shared/flap 2/permit.js +0 -67
- package/dist/shared/flap 2/pinata.d.ts +0 -40
- package/dist/shared/flap 2/pinata.js +0 -106
- package/dist/shared/flap 2/portal-bundle-merkle/config.d.ts +0 -79
- package/dist/shared/flap 2/portal-bundle-merkle/config.js +0 -133
- package/dist/shared/flap 2/portal-bundle-merkle/core.d.ts +0 -18
- package/dist/shared/flap 2/portal-bundle-merkle/core.js +0 -938
- package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.d.ts +0 -125
- package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.js +0 -665
- package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.d.ts +0 -88
- package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.js +0 -446
- package/dist/shared/flap 2/portal-bundle-merkle/index.d.ts +0 -14
- package/dist/shared/flap 2/portal-bundle-merkle/index.js +0 -26
- package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.d.ts +0 -28
- package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.js +0 -701
- package/dist/shared/flap 2/portal-bundle-merkle/private.d.ts +0 -17
- package/dist/shared/flap 2/portal-bundle-merkle/private.js +0 -549
- package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.d.ts +0 -65
- package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.js +0 -831
- package/dist/shared/flap 2/portal-bundle-merkle/swap.d.ts +0 -201
- package/dist/shared/flap 2/portal-bundle-merkle/swap.js +0 -1359
- package/dist/shared/flap 2/portal-bundle-merkle/types.d.ts +0 -358
- package/dist/shared/flap 2/portal-bundle-merkle/types.js +0 -1
- package/dist/shared/flap 2/portal-bundle-merkle/utils.d.ts +0 -89
- package/dist/shared/flap 2/portal-bundle-merkle/utils.js +0 -963
- package/dist/shared/flap 2/portal-bundle.d.ts +0 -119
- package/dist/shared/flap 2/portal-bundle.js +0 -584
- package/dist/shared/flap 2/portal.d.ts +0 -392
- package/dist/shared/flap 2/portal.js +0 -559
- package/dist/shared/flap 2/vanity.d.ts +0 -48
- package/dist/shared/flap 2/vanity.js +0 -110
- package/dist/shared/flap 2/vault.d.ts +0 -240
- package/dist/shared/flap 2/vault.js +0 -366
- package/dist/shared/four 2/index.d.ts +0 -7
- package/dist/shared/four 2/index.js +0 -22
- package/dist/shared/four 2/tax-token.d.ts +0 -176
- package/dist/shared/four 2/tax-token.js +0 -302
- package/dist/shared/index 2.js +0 -10
- package/dist/shared/index.d 2.ts +0 -10
- package/dist/types/distribute.d.ts +0 -72
- package/dist/types/distribute.js +0 -1
- package/dist/types 2/errors.d.ts +0 -27
- package/dist/types 2/errors.js +0 -34
- package/dist/utils/__tests__/errors.test.d.ts +0 -1
- package/dist/utils/__tests__/errors.test.js +0 -76
- package/dist/utils/airdrop-sweep-types.d.ts +0 -1
- package/dist/utils/airdrop-sweep-types.js +0 -1
- package/dist/utils/erc20/index.d.ts +0 -242
- package/dist/utils/erc20/index.js +0 -645
- package/dist/utils/erc20/types.d.ts +0 -77
- package/dist/utils/erc20/types.js +0 -1
- package/dist/utils/erc20-types.d.ts +0 -1
- package/dist/utils/erc20-types.js +0 -1
- package/dist/utils/holders-maker/helpers.d.ts +0 -43
- package/dist/utils/holders-maker/helpers.js +0 -371
- package/dist/utils/holders-maker/index.d.ts +0 -26
- package/dist/utils/holders-maker/index.js +0 -218
- package/dist/utils/holders-maker/types.d.ts +0 -72
- package/dist/utils/holders-maker/types.js +0 -4
- package/dist/utils/holders-maker-types.d.ts +0 -1
- package/dist/utils/holders-maker-types.js +0 -1
- package/dist/utils/lp-inspect/index.d.ts +0 -44
- package/dist/utils/lp-inspect/index.js +0 -937
- package/dist/utils/lp-inspect/types.d.ts +0 -100
- package/dist/utils/lp-inspect/types.js +0 -1
- package/dist/utils/lp-inspect-types.d.ts +0 -1
- package/dist/utils/lp-inspect-types.js +0 -1
- package/dist/utils/private-sale-types.d.ts +0 -1
- package/dist/utils/private-sale-types.js +0 -1
- package/dist/utils/quote-helpers/index.d.ts +0 -107
- package/dist/utils/quote-helpers/index.js +0 -346
- package/dist/utils/quote-helpers/types.d.ts +0 -16
- package/dist/utils/quote-helpers/types.js +0 -1
- package/dist/utils/quote-helpers-types.d.ts +0 -1
- package/dist/utils/quote-helpers-types.js +0 -1
- /package/dist/{chains/bsc/four/submit/types.js → __tests__/subpath-exports.test.d.ts} +0 -0
|
@@ -1,921 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EIP-7702 捆绑换手
|
|
3
|
-
*
|
|
4
|
-
* 在一笔交易中完成:
|
|
5
|
-
* 1. 主钱包卖币 → 获得 OKB
|
|
6
|
-
* 2. OKB 多跳转账 → 中间钱包1 → 中间钱包2 → 最终钱包
|
|
7
|
-
* 3. 最终钱包买币
|
|
8
|
-
*
|
|
9
|
-
* 支持 FLAP 内盘、V2 和 V3 三种交易类型
|
|
10
|
-
* 只生成签名,由调用方决定如何提交
|
|
11
|
-
*/
|
|
12
|
-
import { ethers, Contract, JsonRpcProvider } from 'ethers';
|
|
13
|
-
import { quoteV2, quoteV3 } from '../../../../utils/quote-helpers.js';
|
|
14
|
-
// ✅ 扩展参数类型,添加 userType 和 useFundUtilization 支持
|
|
15
|
-
import { getCachedProvider, createWallet, signAuthorization, signAuthorizationsWithNonces, batchGetNonces, buildEIP7702TransactionSync, calculateProfitAmountByUserType, calculateProfitAmountByTxCount, getProfitRecipient, generateRandomWallets, } from '../utils.js';
|
|
16
|
-
import { UNIFIED_DELEGATE_ADDRESS, POTATOSWAP_V2_ROUTER, POTATOSWAP_V3_ROUTER, FLAP_PORTAL_ADDRESS, WOKB_ADDRESS, V3_FEE_TIERS, UNIFIED_DELEGATE_ABI, FLAP_PORTAL_ABI, ERC20_ABI, XLAYER_RPC_URL, XLAYER_CHAIN_ID, } from '../constants.js';
|
|
17
|
-
// ========================================
|
|
18
|
-
// 路由地址获取
|
|
19
|
-
// ========================================
|
|
20
|
-
function getDefaultRouter(tradeType) {
|
|
21
|
-
switch (tradeType) {
|
|
22
|
-
case 'FLAP':
|
|
23
|
-
return FLAP_PORTAL_ADDRESS;
|
|
24
|
-
case 'V2':
|
|
25
|
-
return POTATOSWAP_V2_ROUTER;
|
|
26
|
-
case 'V3':
|
|
27
|
-
return POTATOSWAP_V3_ROUTER;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* 截断小数位数,避免 parseEther/parseUnits 报错
|
|
32
|
-
* @param value 数值字符串
|
|
33
|
-
* @param decimals 保留的小数位数(默认 18)
|
|
34
|
-
*/
|
|
35
|
-
function truncateDecimals(value, decimals = 18) {
|
|
36
|
-
if (!value || value === '0')
|
|
37
|
-
return value;
|
|
38
|
-
const parts = String(value).split('.');
|
|
39
|
-
if (parts.length === 1)
|
|
40
|
-
return value;
|
|
41
|
-
const intPart = parts[0] || '0';
|
|
42
|
-
const decPart = (parts[1] || '').slice(0, decimals);
|
|
43
|
-
return decPart ? `${intPart}.${decPart}` : intPart;
|
|
44
|
-
}
|
|
45
|
-
// ========================================
|
|
46
|
-
// 卖出报价(Token → OKB)
|
|
47
|
-
// ========================================
|
|
48
|
-
/**
|
|
49
|
-
* 获取卖出报价:预估卖出代币能得到多少 OKB
|
|
50
|
-
*/
|
|
51
|
-
async function quoteSellOutput(sellAmount, tokenAddress, tradeType, fee, rpcUrl) {
|
|
52
|
-
if (sellAmount <= 0n)
|
|
53
|
-
return 0n;
|
|
54
|
-
const provider = new JsonRpcProvider(rpcUrl || XLAYER_RPC_URL, { chainId: XLAYER_CHAIN_ID, name: 'xlayer', ensAddress: undefined });
|
|
55
|
-
try {
|
|
56
|
-
switch (tradeType) {
|
|
57
|
-
case 'FLAP': {
|
|
58
|
-
// FLAP Portal 报价
|
|
59
|
-
const portalContract = new Contract(FLAP_PORTAL_ADDRESS, FLAP_PORTAL_ABI, provider);
|
|
60
|
-
// ✅ 优先使用 quoteExactInput.staticCall(与 AA 模式一致)
|
|
61
|
-
try {
|
|
62
|
-
const okbOut = await portalContract.quoteExactInput.staticCall({
|
|
63
|
-
inputToken: tokenAddress,
|
|
64
|
-
outputToken: ethers.ZeroAddress,
|
|
65
|
-
inputAmount: sellAmount,
|
|
66
|
-
});
|
|
67
|
-
console.log(`[Bundle Swap] FLAP quoteExactInput 成功: ${ethers.formatEther(okbOut)} OKB`);
|
|
68
|
-
return BigInt(okbOut.toString());
|
|
69
|
-
}
|
|
70
|
-
catch (e) {
|
|
71
|
-
console.warn(`[Bundle Swap] FLAP quoteExactInput 失败:`, e);
|
|
72
|
-
}
|
|
73
|
-
// 回退到 previewSell
|
|
74
|
-
try {
|
|
75
|
-
const okbOut = await portalContract.previewSell(tokenAddress, sellAmount);
|
|
76
|
-
console.log(`[Bundle Swap] FLAP previewSell 成功: ${ethers.formatEther(okbOut)} OKB`);
|
|
77
|
-
return BigInt(okbOut.toString());
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
console.warn(`[Bundle Swap] FLAP previewSell 失败,尝试 V2...`);
|
|
81
|
-
}
|
|
82
|
-
// 回退到 V2
|
|
83
|
-
const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
|
|
84
|
-
if (v2Result.amountOut > 0n) {
|
|
85
|
-
console.log(`[Bundle Swap] FLAP 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
|
|
86
|
-
return v2Result.amountOut;
|
|
87
|
-
}
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
case 'V2': {
|
|
91
|
-
const result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
|
|
92
|
-
if (result.amountOut > 0n) {
|
|
93
|
-
console.log(`[Bundle Swap] V2 报价成功: ${ethers.formatEther(result.amountOut)} OKB`);
|
|
94
|
-
return result.amountOut;
|
|
95
|
-
}
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
case 'V3': {
|
|
99
|
-
const result = await quoteV3(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER', fee);
|
|
100
|
-
if (result.amountOut > 0n) {
|
|
101
|
-
console.log(`[Bundle Swap] V3 报价成功 (fee=${result.fee}): ${ethers.formatEther(result.amountOut)} OKB`);
|
|
102
|
-
return result.amountOut;
|
|
103
|
-
}
|
|
104
|
-
// 回退到 V2
|
|
105
|
-
const v2Result = await quoteV2(provider, tokenAddress, WOKB_ADDRESS, sellAmount, 'XLAYER');
|
|
106
|
-
if (v2Result.amountOut > 0n) {
|
|
107
|
-
console.log(`[Bundle Swap] V3 回退 V2 报价成功: ${ethers.formatEther(v2Result.amountOut)} OKB`);
|
|
108
|
-
return v2Result.amountOut;
|
|
109
|
-
}
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
console.error(`[Bundle Swap] 报价失败:`, error);
|
|
116
|
-
}
|
|
117
|
-
console.warn(`[Bundle Swap] 无法获取报价,利润计算可能不准确`);
|
|
118
|
-
return 0n;
|
|
119
|
-
}
|
|
120
|
-
// ========================================
|
|
121
|
-
// 构建卖出并转账调用
|
|
122
|
-
// ========================================
|
|
123
|
-
function buildSellAndTransferCall(sellerWallet, sellAmount, tokenAddress, recipient, tradeType, routerAddress, fee) {
|
|
124
|
-
const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
|
|
125
|
-
const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
126
|
-
switch (tradeType) {
|
|
127
|
-
case 'FLAP': {
|
|
128
|
-
// ✅ FLAP: 使用 swapExactInput 卖出(与 AA 模式保持一致)
|
|
129
|
-
const portalCallData = portalInterface.encodeFunctionData('swapExactInput', [{
|
|
130
|
-
inputToken: tokenAddress,
|
|
131
|
-
outputToken: ethers.ZeroAddress, // 0x0 = OKB
|
|
132
|
-
inputAmount: sellAmount,
|
|
133
|
-
minOutputAmount: 0n,
|
|
134
|
-
permitData: '0x',
|
|
135
|
-
}]);
|
|
136
|
-
// 通过 executeBatch 让 seller 钱包调用 Portal
|
|
137
|
-
const batchCalls = [{ target: FLAP_PORTAL_ADDRESS, value: 0n, data: portalCallData }];
|
|
138
|
-
const callData = delegateInterface.encodeFunctionData('executeBatch', [batchCalls]);
|
|
139
|
-
return {
|
|
140
|
-
target: sellerWallet.address,
|
|
141
|
-
allowFailure: false,
|
|
142
|
-
value: 0n,
|
|
143
|
-
callData,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
case 'V2': {
|
|
147
|
-
// V2: 使用 executeSellAndTransferV2
|
|
148
|
-
const path = [tokenAddress, WOKB_ADDRESS];
|
|
149
|
-
const callData = delegateInterface.encodeFunctionData('executeSellAndTransferV2', [
|
|
150
|
-
routerAddress,
|
|
151
|
-
path,
|
|
152
|
-
sellAmount,
|
|
153
|
-
recipient,
|
|
154
|
-
]);
|
|
155
|
-
return {
|
|
156
|
-
target: sellerWallet.address,
|
|
157
|
-
allowFailure: false,
|
|
158
|
-
value: 0n,
|
|
159
|
-
callData,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
case 'V3': {
|
|
163
|
-
// V3: 使用 executeSellAndTransfer
|
|
164
|
-
const callData = delegateInterface.encodeFunctionData('executeSellAndTransfer', [
|
|
165
|
-
routerAddress,
|
|
166
|
-
tokenAddress,
|
|
167
|
-
WOKB_ADDRESS,
|
|
168
|
-
fee,
|
|
169
|
-
sellAmount,
|
|
170
|
-
recipient,
|
|
171
|
-
]);
|
|
172
|
-
return {
|
|
173
|
-
target: sellerWallet.address,
|
|
174
|
-
allowFailure: false,
|
|
175
|
-
value: 0n,
|
|
176
|
-
callData,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// ========================================
|
|
182
|
-
// 构建买入调用
|
|
183
|
-
// ========================================
|
|
184
|
-
function buildBuyCall(buyerWallet, tokenAddress, tradeType, routerAddress, fee, buyAmount // ✅ 新增:买入金额(防止使用全部余额导致问题)
|
|
185
|
-
) {
|
|
186
|
-
const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
|
|
187
|
-
const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
188
|
-
switch (tradeType) {
|
|
189
|
-
case 'FLAP': {
|
|
190
|
-
// ✅ FLAP: 使用 swapExactInput 买入(与 AA 模式保持一致)
|
|
191
|
-
// 原 buy(address token) ABI 与 Portal 不匹配,改用 swapExactInput
|
|
192
|
-
const portalCallData = portalInterface.encodeFunctionData('swapExactInput', [{
|
|
193
|
-
inputToken: ethers.ZeroAddress, // 0x0 = OKB (原生代币)
|
|
194
|
-
outputToken: tokenAddress,
|
|
195
|
-
inputAmount: buyAmount,
|
|
196
|
-
minOutputAmount: 0n,
|
|
197
|
-
permitData: '0x',
|
|
198
|
-
}]);
|
|
199
|
-
const batchCalls = [{ target: FLAP_PORTAL_ADDRESS, value: buyAmount, data: portalCallData }];
|
|
200
|
-
return {
|
|
201
|
-
target: buyerWallet.address,
|
|
202
|
-
allowFailure: false,
|
|
203
|
-
value: 0n,
|
|
204
|
-
callData: delegateInterface.encodeFunctionData('executeBatch', [batchCalls]),
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
case 'V2': {
|
|
208
|
-
// ✅ V2: 使用 executeBuyV2,传入指定金额
|
|
209
|
-
const path = [WOKB_ADDRESS, tokenAddress];
|
|
210
|
-
const callData = delegateInterface.encodeFunctionData('executeBuyV2', [
|
|
211
|
-
routerAddress,
|
|
212
|
-
path,
|
|
213
|
-
buyAmount, // ✅ 使用指定金额,不是 0
|
|
214
|
-
]);
|
|
215
|
-
return {
|
|
216
|
-
target: buyerWallet.address,
|
|
217
|
-
allowFailure: false,
|
|
218
|
-
value: 0n,
|
|
219
|
-
callData,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
case 'V3': {
|
|
223
|
-
// ✅ V3: 使用 executeBuy,传入指定金额
|
|
224
|
-
const callData = delegateInterface.encodeFunctionData('executeBuy', [
|
|
225
|
-
routerAddress,
|
|
226
|
-
WOKB_ADDRESS,
|
|
227
|
-
tokenAddress,
|
|
228
|
-
fee,
|
|
229
|
-
buyAmount, // ✅ 使用指定金额,不是 0
|
|
230
|
-
]);
|
|
231
|
-
return {
|
|
232
|
-
target: buyerWallet.address,
|
|
233
|
-
allowFailure: false,
|
|
234
|
-
value: 0n,
|
|
235
|
-
callData,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* 构建捆绑换手交易
|
|
242
|
-
*/
|
|
243
|
-
async function buildSwapCalls(sellerWallet, buyerWallet, hopWallets, sellAmount, buyAmount, // ✅ 新增:买入金额(防止使用全部余额)
|
|
244
|
-
tokenAddress, tradeType, routerAddress, fee) {
|
|
245
|
-
const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
|
|
246
|
-
const calls = [];
|
|
247
|
-
// 第一步:卖家卖币并转账给第一个中间钱包
|
|
248
|
-
const firstRecipient = hopWallets.length > 0 ? hopWallets[0].address : buyerWallet.address;
|
|
249
|
-
const sellCall = buildSellAndTransferCall(sellerWallet, sellAmount, tokenAddress, firstRecipient, tradeType, routerAddress, fee);
|
|
250
|
-
calls.push(sellCall);
|
|
251
|
-
// FLAP 模式需要额外的转账调用(因为 sell 函数不支持直接转账)
|
|
252
|
-
if (tradeType === 'FLAP') {
|
|
253
|
-
const transferData = delegateInterface.encodeFunctionData('transferTo', [firstRecipient]);
|
|
254
|
-
calls.push({
|
|
255
|
-
target: sellerWallet.address,
|
|
256
|
-
allowFailure: false,
|
|
257
|
-
value: 0n,
|
|
258
|
-
callData: transferData,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
// 中间钱包之间的转账
|
|
262
|
-
for (let i = 0; i < hopWallets.length; i++) {
|
|
263
|
-
const nextRecipient = i < hopWallets.length - 1 ? hopWallets[i + 1].address : buyerWallet.address;
|
|
264
|
-
const transferData = delegateInterface.encodeFunctionData('transferTo', [nextRecipient]);
|
|
265
|
-
calls.push({
|
|
266
|
-
target: hopWallets[i].address,
|
|
267
|
-
allowFailure: false,
|
|
268
|
-
value: 0n,
|
|
269
|
-
callData: transferData,
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
// ✅ 最后一步:买家买币(使用指定金额)
|
|
273
|
-
const buyCall = buildBuyCall(buyerWallet, tokenAddress, tradeType, routerAddress, fee, buyAmount);
|
|
274
|
-
calls.push(buyCall);
|
|
275
|
-
return calls;
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* 捆绑换手 - 生成签名后的交易
|
|
279
|
-
*
|
|
280
|
-
* @param params 换手参数
|
|
281
|
-
* @returns 签名后的交易和元数据
|
|
282
|
-
*
|
|
283
|
-
* @example
|
|
284
|
-
* // FLAP 内盘换手
|
|
285
|
-
* const result = await bundleSwap({
|
|
286
|
-
* sellerPrivateKey: '0x...',
|
|
287
|
-
* buyerPrivateKey: '0x...',
|
|
288
|
-
* tokenAddress: '0x...',
|
|
289
|
-
* sellAmount: '100',
|
|
290
|
-
* tradeType: 'FLAP',
|
|
291
|
-
* hopCount: 2,
|
|
292
|
-
* });
|
|
293
|
-
*/
|
|
294
|
-
export async function bundleSwap(params) {
|
|
295
|
-
const { sellerPrivateKey, buyerPrivateKey, tokenAddress, tokenDecimals = 18, sellAmount, hopCount = 0, tradeType = 'V3', routerAddress, fee = V3_FEE_TIERS.MEDIUM, userType = 'v0', useFundUtilization = false, // ✅ 默认普通模式
|
|
296
|
-
config, } = params;
|
|
297
|
-
const provider = getCachedProvider(config?.rpcUrl);
|
|
298
|
-
const delegateAddress = (config?.delegateAddress && config.delegateAddress.trim()) || UNIFIED_DELEGATE_ADDRESS;
|
|
299
|
-
const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
|
|
300
|
-
const actualRouter = routerAddress ?? getDefaultRouter(tradeType);
|
|
301
|
-
const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
302
|
-
// ========================================
|
|
303
|
-
// 同步操作 - 创建钱包
|
|
304
|
-
// ========================================
|
|
305
|
-
const sellerWallet = createWallet(sellerPrivateKey, provider);
|
|
306
|
-
const buyerWallet = createWallet(buyerPrivateKey, provider);
|
|
307
|
-
// 资金利用率模式才需要中间钱包
|
|
308
|
-
const actualHopCount = useFundUtilization ? hopCount : 0;
|
|
309
|
-
const hopWalletInfos = actualHopCount > 0 ? generateRandomWallets(actualHopCount) : [];
|
|
310
|
-
const hopWallets = hopWalletInfos.map(info => createWallet(info.privateKey, provider));
|
|
311
|
-
const sellAmountWei = ethers.parseUnits(truncateDecimals(sellAmount, tokenDecimals), tokenDecimals);
|
|
312
|
-
// 获取卖出报价
|
|
313
|
-
const estimatedOkbOut = await quoteSellOutput(sellAmountWei, tokenAddress, tradeType, fee, config?.rpcUrl);
|
|
314
|
-
// 使用双边费率计算利润(换手 = 卖 + 买)
|
|
315
|
-
const profitAmount = estimatedOkbOut > 0n
|
|
316
|
-
? calculateProfitAmountByUserType(estimatedOkbOut, userType, true)
|
|
317
|
-
: 0n;
|
|
318
|
-
const profitRecipient = getProfitRecipient(config);
|
|
319
|
-
// 计算买入金额
|
|
320
|
-
const buyAmountAfterProfit = estimatedOkbOut > profitAmount
|
|
321
|
-
? estimatedOkbOut - profitAmount
|
|
322
|
-
: 0n;
|
|
323
|
-
const modeLabel = useFundUtilization ? '资金利用率' : '普通';
|
|
324
|
-
console.log(`[Bundle Swap] 模式: ${modeLabel}`);
|
|
325
|
-
console.log(`[Bundle Swap] 卖出数量: ${sellAmount} Token`);
|
|
326
|
-
console.log(`[Bundle Swap] 预估获得: ${ethers.formatEther(estimatedOkbOut)} OKB`);
|
|
327
|
-
console.log(`[Bundle Swap] 利润: ${ethers.formatEther(profitAmount)} OKB (userType=${userType})`);
|
|
328
|
-
console.log(`[Bundle Swap] 买入金额: ${ethers.formatEther(buyAmountAfterProfit)} OKB`);
|
|
329
|
-
// ========================================
|
|
330
|
-
// 并行获取数据
|
|
331
|
-
// ========================================
|
|
332
|
-
const [nonces, feeData, buyerBalance] = await Promise.all([
|
|
333
|
-
batchGetNonces([sellerWallet.address, buyerWallet.address], provider),
|
|
334
|
-
provider.getFeeData(),
|
|
335
|
-
provider.getBalance(buyerWallet.address),
|
|
336
|
-
]);
|
|
337
|
-
const sellerNonce = nonces[0];
|
|
338
|
-
const buyerNonce = nonces[1];
|
|
339
|
-
// ========================================
|
|
340
|
-
// 验证余额
|
|
341
|
-
// ========================================
|
|
342
|
-
if (tradeType === 'FLAP' && buyAmountAfterProfit <= 0n) {
|
|
343
|
-
throw new Error('FLAP 内盘模式需要有效的报价,请检查代币地址或稍后重试');
|
|
344
|
-
}
|
|
345
|
-
// 普通模式:买家需要预存足够的 OKB
|
|
346
|
-
if (!useFundUtilization && tradeType === 'FLAP') {
|
|
347
|
-
if (buyerBalance < buyAmountAfterProfit) {
|
|
348
|
-
throw new Error(`普通模式需要买家钱包有足够的 OKB,需要 ${ethers.formatEther(buyAmountAfterProfit)},当前 ${ethers.formatEther(buyerBalance)}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
// ========================================
|
|
352
|
-
// 签署授权
|
|
353
|
-
// ========================================
|
|
354
|
-
const authPromises = [];
|
|
355
|
-
// 卖家授权(交易发起者,nonce +1)
|
|
356
|
-
authPromises.push(signAuthorization(sellerWallet, delegateAddress, BigInt(sellerNonce + 1)));
|
|
357
|
-
// 买家授权
|
|
358
|
-
authPromises.push(signAuthorization(buyerWallet, delegateAddress, BigInt(buyerNonce)));
|
|
359
|
-
// 中间钱包授权(资金利用率模式)
|
|
360
|
-
for (const hopWallet of hopWallets) {
|
|
361
|
-
authPromises.push(signAuthorization(hopWallet, delegateAddress, 0n));
|
|
362
|
-
}
|
|
363
|
-
const authorizations = await Promise.all(authPromises);
|
|
364
|
-
// ========================================
|
|
365
|
-
// 构建调用
|
|
366
|
-
// ========================================
|
|
367
|
-
const calls = [];
|
|
368
|
-
if (useFundUtilization) {
|
|
369
|
-
// ========================================
|
|
370
|
-
// ✅ 资金利用率模式
|
|
371
|
-
// 核心逻辑:卖出获得的 OKB 按比例转给买家
|
|
372
|
-
// 1. 卖家卖出代币 → 获得 OKB
|
|
373
|
-
// 2. 利润从卖家 OKB 中扣除
|
|
374
|
-
// 3. (可选多跳)卖家 → hop1 → hop2 → ... → 分发者
|
|
375
|
-
// 4. 分发者按比例转账给买家
|
|
376
|
-
// 5. 买家买入
|
|
377
|
-
// ========================================
|
|
378
|
-
console.log(`[Bundle Swap] 资金利用率模式,hopCount=${actualHopCount}`);
|
|
379
|
-
// 1. 卖家卖出
|
|
380
|
-
const sellCall = buildSellCallWithAmountInternal(sellerWallet, sellAmountWei, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
381
|
-
calls.push(sellCall);
|
|
382
|
-
// 2. 利润刮取(从卖家余额扣除)
|
|
383
|
-
if (profitAmount > 0n) {
|
|
384
|
-
calls.push({
|
|
385
|
-
target: sellerWallet.address,
|
|
386
|
-
allowFailure: false,
|
|
387
|
-
value: 0n,
|
|
388
|
-
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
// 3. 确定分发者:如果有多跳,最后一个 hop 是分发者;否则卖家是分发者
|
|
392
|
-
const distributor = hopWallets.length > 0 ? hopWallets[hopWallets.length - 1] : sellerWallet;
|
|
393
|
-
// 多跳转账(如果有)
|
|
394
|
-
if (hopWallets.length > 0) {
|
|
395
|
-
// ✅ 修复:卖家只转移"卖出所得 - 利润",不是全部余额
|
|
396
|
-
// 避免把卖家原有的 OKB 也转走
|
|
397
|
-
calls.push({
|
|
398
|
-
target: sellerWallet.address,
|
|
399
|
-
allowFailure: false,
|
|
400
|
-
value: 0n,
|
|
401
|
-
callData: delegateInterface.encodeFunctionData('transferAmount', [hopWallets[0].address, buyAmountAfterProfit]),
|
|
402
|
-
});
|
|
403
|
-
// hop1 → hop2 → ... → 最后一个 hop(分发者):使用 transferTo(全部余额)
|
|
404
|
-
for (let i = 0; i < hopWallets.length - 1; i++) {
|
|
405
|
-
calls.push({
|
|
406
|
-
target: hopWallets[i].address,
|
|
407
|
-
allowFailure: false,
|
|
408
|
-
value: 0n,
|
|
409
|
-
callData: delegateInterface.encodeFunctionData('transferTo', [hopWallets[i + 1].address]),
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
// 4. 分发者转账给买家:使用 transferTo(全部余额,确保资金不滞留)
|
|
414
|
-
calls.push({
|
|
415
|
-
target: distributor.address,
|
|
416
|
-
allowFailure: false,
|
|
417
|
-
value: 0n,
|
|
418
|
-
callData: delegateInterface.encodeFunctionData('transferTo', [buyerWallet.address]),
|
|
419
|
-
});
|
|
420
|
-
// 5. 买家买入(使用收到的 OKB)
|
|
421
|
-
const buyCall = buildBuyCallInternal(buyerWallet, buyAmountAfterProfit, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
422
|
-
calls.push(buyCall);
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
// ========================================
|
|
426
|
-
// ✅ 普通模式
|
|
427
|
-
// 1. 卖家卖出代币 → OKB 留在卖家账户
|
|
428
|
-
// 2. 买家用自己预存的 OKB 买入代币
|
|
429
|
-
// 3. 利润从买家的 OKB 中扣除
|
|
430
|
-
// ========================================
|
|
431
|
-
console.log(`[Bundle Swap] 普通模式`);
|
|
432
|
-
// 1. 卖家卖出(OKB 留在卖家账户)
|
|
433
|
-
const sellCall = buildSellCallWithAmountInternal(sellerWallet, sellAmountWei, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
434
|
-
calls.push(sellCall);
|
|
435
|
-
// 2. 利润刮取(从买家余额扣除)
|
|
436
|
-
if (profitAmount > 0n) {
|
|
437
|
-
calls.push({
|
|
438
|
-
target: buyerWallet.address,
|
|
439
|
-
allowFailure: false,
|
|
440
|
-
value: 0n,
|
|
441
|
-
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
// 3. 买家用自己的 OKB 买入(金额 = 卖家卖出所得,避免买卖资金不匹配)
|
|
445
|
-
const buyCall = buildBuyCallInternal(buyerWallet, buyAmountAfterProfit, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
446
|
-
calls.push(buyCall);
|
|
447
|
-
}
|
|
448
|
-
// ========================================
|
|
449
|
-
// 构建交易
|
|
450
|
-
// ========================================
|
|
451
|
-
const signedTransaction = buildEIP7702TransactionSync(sellerWallet, authorizations, calls, 0n, sellerNonce, feeData, config);
|
|
452
|
-
return {
|
|
453
|
-
signedTransaction,
|
|
454
|
-
hopWallets: hopWalletInfos,
|
|
455
|
-
metadata: {
|
|
456
|
-
sellerAddress: sellerWallet.address,
|
|
457
|
-
buyerAddresses: [buyerWallet.address],
|
|
458
|
-
sellAmount: ethers.formatUnits(sellAmountWei, tokenDecimals),
|
|
459
|
-
estimatedOkbOut: ethers.formatEther(estimatedOkbOut),
|
|
460
|
-
estimatedBuyAmount: ethers.formatEther(buyAmountAfterProfit),
|
|
461
|
-
profitAmount: ethers.formatEther(profitAmount),
|
|
462
|
-
profitRecipient,
|
|
463
|
-
userType,
|
|
464
|
-
useFundUtilization,
|
|
465
|
-
},
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* 批量换手(一卖多买)- 生成签名后的交易
|
|
470
|
-
*
|
|
471
|
-
* @param params 批量换手参数
|
|
472
|
-
* @returns 签名后的交易和元数据
|
|
473
|
-
*
|
|
474
|
-
* @example
|
|
475
|
-
* // FLAP 内盘一对多换手
|
|
476
|
-
* const result = await bundleBatchSwap({
|
|
477
|
-
* sellerPrivateKey: '0x...',
|
|
478
|
-
* buyerPrivateKeys: ['0x...', '0x...'],
|
|
479
|
-
* tokenAddress: '0x...',
|
|
480
|
-
* sellAmount: '100',
|
|
481
|
-
* tradeType: 'FLAP',
|
|
482
|
-
* });
|
|
483
|
-
*/
|
|
484
|
-
export async function bundleBatchSwap(params) {
|
|
485
|
-
const { sellerPrivateKey, buyerPrivateKeys, buyerRatios, tokenAddress, tokenDecimals = 18, sellAmount, hopCount = 0, tradeType = 'V3', routerAddress, fee = V3_FEE_TIERS.MEDIUM, userType = 'v0', useFundUtilization = false, // ✅ 默认普通模式
|
|
486
|
-
config, } = params;
|
|
487
|
-
const provider = getCachedProvider(config?.rpcUrl);
|
|
488
|
-
const delegateAddress = (config?.delegateAddress && config.delegateAddress.trim()) || UNIFIED_DELEGATE_ADDRESS;
|
|
489
|
-
const actualRouter = routerAddress ?? getDefaultRouter(tradeType);
|
|
490
|
-
const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
|
|
491
|
-
const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
492
|
-
// ========================================
|
|
493
|
-
// 创建钱包
|
|
494
|
-
// ========================================
|
|
495
|
-
const sellerWallet = createWallet(sellerPrivateKey, provider);
|
|
496
|
-
const buyerWallets = buyerPrivateKeys.map(pk => createWallet(pk, provider));
|
|
497
|
-
// 资金利用率模式才需要中间钱包
|
|
498
|
-
const actualHopCount = useFundUtilization ? hopCount : 0;
|
|
499
|
-
const hopWalletInfos = actualHopCount > 0 ? generateRandomWallets(actualHopCount) : [];
|
|
500
|
-
const hopWallets = hopWalletInfos.map(info => createWallet(info.privateKey, provider));
|
|
501
|
-
const sellAmountWei = ethers.parseUnits(truncateDecimals(sellAmount, tokenDecimals), tokenDecimals);
|
|
502
|
-
// 获取卖出报价
|
|
503
|
-
const estimatedOkbOut = await quoteSellOutput(sellAmountWei, tokenAddress, tradeType, fee, config?.rpcUrl);
|
|
504
|
-
// 计算利润和买入金额
|
|
505
|
-
const profitAmount = estimatedOkbOut > 0n
|
|
506
|
-
? calculateProfitAmountByUserType(estimatedOkbOut, userType, true)
|
|
507
|
-
: 0n;
|
|
508
|
-
const profitRecipient = getProfitRecipient(config);
|
|
509
|
-
const totalBuyAmountAfterProfit = estimatedOkbOut > profitAmount
|
|
510
|
-
? estimatedOkbOut - profitAmount
|
|
511
|
-
: 0n;
|
|
512
|
-
const modeLabel = useFundUtilization ? '资金利用率' : '普通';
|
|
513
|
-
console.log(`[Bundle Batch Swap] 模式: ${modeLabel}`);
|
|
514
|
-
console.log(`[Bundle Batch Swap] 卖出数量: ${sellAmount} Token`);
|
|
515
|
-
console.log(`[Bundle Batch Swap] 预估获得: ${ethers.formatEther(estimatedOkbOut)} OKB`);
|
|
516
|
-
console.log(`[Bundle Batch Swap] 利润: ${ethers.formatEther(profitAmount)} OKB (userType=${userType})`);
|
|
517
|
-
console.log(`[Bundle Batch Swap] 买入金额: ${ethers.formatEther(totalBuyAmountAfterProfit)} OKB`);
|
|
518
|
-
// 验证报价
|
|
519
|
-
if (tradeType === 'FLAP' && totalBuyAmountAfterProfit <= 0n) {
|
|
520
|
-
throw new Error('FLAP 内盘模式需要有效的报价,请检查代币地址或稍后重试');
|
|
521
|
-
}
|
|
522
|
-
// ========================================
|
|
523
|
-
// 并行获取数据
|
|
524
|
-
// ========================================
|
|
525
|
-
const addressesToQuery = [sellerWallet.address, ...buyerWallets.map(w => w.address)];
|
|
526
|
-
const [nonces, feeData, ...buyerBalances] = await Promise.all([
|
|
527
|
-
batchGetNonces(addressesToQuery, provider),
|
|
528
|
-
provider.getFeeData(),
|
|
529
|
-
...buyerWallets.map(w => provider.getBalance(w.address)),
|
|
530
|
-
]);
|
|
531
|
-
const sellerNonce = nonces[0];
|
|
532
|
-
const buyerNonces = nonces.slice(1);
|
|
533
|
-
// 普通模式验证:买家需要预存足够的 OKB
|
|
534
|
-
if (!useFundUtilization && tradeType === 'FLAP') {
|
|
535
|
-
const totalBuyerBalance = buyerBalances.reduce((sum, b) => sum + BigInt(b.toString()), 0n);
|
|
536
|
-
if (totalBuyerBalance < totalBuyAmountAfterProfit) {
|
|
537
|
-
throw new Error(`普通模式需要买家钱包总余额足够,需要 ${ethers.formatEther(totalBuyAmountAfterProfit)},当前总计 ${ethers.formatEther(totalBuyerBalance)}`);
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
// ========================================
|
|
541
|
-
// 签署授权
|
|
542
|
-
// ========================================
|
|
543
|
-
const authPromises = [];
|
|
544
|
-
authPromises.push(signAuthorization(sellerWallet, delegateAddress, BigInt(sellerNonce + 1)));
|
|
545
|
-
for (let i = 0; i < buyerWallets.length; i++) {
|
|
546
|
-
authPromises.push(signAuthorization(buyerWallets[i], delegateAddress, BigInt(buyerNonces[i])));
|
|
547
|
-
}
|
|
548
|
-
// 中间钱包授权(资金利用率模式)
|
|
549
|
-
for (const hopWallet of hopWallets) {
|
|
550
|
-
authPromises.push(signAuthorization(hopWallet, delegateAddress, 0n));
|
|
551
|
-
}
|
|
552
|
-
const authorizations = await Promise.all(authPromises);
|
|
553
|
-
// ========================================
|
|
554
|
-
// 构建调用
|
|
555
|
-
// ========================================
|
|
556
|
-
const calls = [];
|
|
557
|
-
// ✅ 计算每个买家的买入金额
|
|
558
|
-
// 所有模式都按比例分配卖家卖出所得,避免买卖资金不匹配
|
|
559
|
-
let buyAmountsPerBuyer;
|
|
560
|
-
if (buyerWallets.length === 1) {
|
|
561
|
-
// 单买家:使用全部卖出所得
|
|
562
|
-
buyAmountsPerBuyer = [totalBuyAmountAfterProfit];
|
|
563
|
-
}
|
|
564
|
-
else if (buyerRatios && buyerRatios.length === buyerWallets.length) {
|
|
565
|
-
// 多买家 + 有比例:按前端传入的比例分配
|
|
566
|
-
let allocated = 0n;
|
|
567
|
-
buyAmountsPerBuyer = buyerRatios.map((ratio, i) => {
|
|
568
|
-
const amount = (totalBuyAmountAfterProfit * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
569
|
-
if (i === buyerRatios.length - 1) {
|
|
570
|
-
return totalBuyAmountAfterProfit - allocated;
|
|
571
|
-
}
|
|
572
|
-
allocated += amount;
|
|
573
|
-
return amount;
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
else {
|
|
577
|
-
// 多买家无比例:均分
|
|
578
|
-
const avgAmount = totalBuyAmountAfterProfit / BigInt(buyerWallets.length);
|
|
579
|
-
const remainder = totalBuyAmountAfterProfit % BigInt(buyerWallets.length);
|
|
580
|
-
buyAmountsPerBuyer = buyerWallets.map((_, i) => i === buyerWallets.length - 1 ? avgAmount + remainder : avgAmount);
|
|
581
|
-
}
|
|
582
|
-
console.log(`[bundleBatchSwap] 买入金额分配:`, buyAmountsPerBuyer.map(a => ethers.formatEther(a)));
|
|
583
|
-
if (useFundUtilization) {
|
|
584
|
-
// ========================================
|
|
585
|
-
// ✅ 资金利用率模式(一卖多买)
|
|
586
|
-
// 1. 卖家卖出代币 → 获得 OKB
|
|
587
|
-
// 2. 利润从卖家 OKB 中扣除
|
|
588
|
-
// 3. 卖家(或最后一个 hop)按比例转账给各买家
|
|
589
|
-
// 4. 各买家用收到的 OKB 买入
|
|
590
|
-
// ========================================
|
|
591
|
-
console.log(`[Bundle Batch Swap] 资金利用率模式,hopCount=${actualHopCount}`);
|
|
592
|
-
// 1. 卖家卖出
|
|
593
|
-
const sellCall = buildSellCallWithAmountInternal(sellerWallet, sellAmountWei, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
594
|
-
calls.push(sellCall);
|
|
595
|
-
// 2. 利润刮取(从卖家余额扣除)
|
|
596
|
-
if (profitAmount > 0n) {
|
|
597
|
-
calls.push({
|
|
598
|
-
target: sellerWallet.address,
|
|
599
|
-
allowFailure: false,
|
|
600
|
-
value: 0n,
|
|
601
|
-
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
// 3. OKB 转账逻辑
|
|
605
|
-
// 确定分发者:如果有多跳,最后一个 hop 是分发者;否则卖家是分发者
|
|
606
|
-
const distributor = hopWallets.length > 0 ? hopWallets[hopWallets.length - 1] : sellerWallet;
|
|
607
|
-
if (hopWallets.length > 0) {
|
|
608
|
-
// ✅ 修复:卖家只转移"卖出所得 - 利润",不是全部余额
|
|
609
|
-
// 避免把卖家原有的 OKB 也转走
|
|
610
|
-
calls.push({
|
|
611
|
-
target: sellerWallet.address,
|
|
612
|
-
allowFailure: false,
|
|
613
|
-
value: 0n,
|
|
614
|
-
callData: delegateInterface.encodeFunctionData('transferAmount', [hopWallets[0].address, totalBuyAmountAfterProfit]),
|
|
615
|
-
});
|
|
616
|
-
// 中间钱包之间转账(到最后一个 hop):使用 transferTo(全部余额)
|
|
617
|
-
for (let i = 0; i < hopWallets.length - 1; i++) {
|
|
618
|
-
calls.push({
|
|
619
|
-
target: hopWallets[i].address,
|
|
620
|
-
allowFailure: false,
|
|
621
|
-
value: 0n,
|
|
622
|
-
callData: delegateInterface.encodeFunctionData('transferTo', [hopWallets[i + 1].address]),
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
// 分发者按比例转给各买家
|
|
627
|
-
for (let i = 0; i < buyerWallets.length; i++) {
|
|
628
|
-
const amountToTransfer = buyAmountsPerBuyer[i];
|
|
629
|
-
if (amountToTransfer <= 0n)
|
|
630
|
-
continue;
|
|
631
|
-
// ✅ 最后一个买家使用 transferTo(全部余额),确保资金不滞留
|
|
632
|
-
const isLastBuyer = i === buyerWallets.length - 1;
|
|
633
|
-
calls.push({
|
|
634
|
-
target: distributor.address,
|
|
635
|
-
allowFailure: false,
|
|
636
|
-
value: 0n,
|
|
637
|
-
callData: isLastBuyer
|
|
638
|
-
? delegateInterface.encodeFunctionData('transferTo', [buyerWallets[i].address])
|
|
639
|
-
: delegateInterface.encodeFunctionData('transferAmount', [buyerWallets[i].address, amountToTransfer]),
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
// 4. 所有买家买入
|
|
643
|
-
for (let i = 0; i < buyerWallets.length; i++) {
|
|
644
|
-
const buyCall = buildBuyCallInternal(buyerWallets[i], buyAmountsPerBuyer[i], tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
645
|
-
calls.push(buyCall);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
else {
|
|
649
|
-
// ========================================
|
|
650
|
-
// ✅ 普通模式(一卖多买)
|
|
651
|
-
// 1. 卖家卖出代币 → OKB 留在卖家账户
|
|
652
|
-
// 2. 利润从第一个买家 OKB 中扣除
|
|
653
|
-
// 3. 各买家用自己预存的 OKB 买入
|
|
654
|
-
// ========================================
|
|
655
|
-
console.log(`[Bundle Batch Swap] 普通模式`);
|
|
656
|
-
// 1. 卖家卖出(OKB 留在卖家账户)
|
|
657
|
-
const sellCall = buildSellCallWithAmountInternal(sellerWallet, sellAmountWei, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
658
|
-
calls.push(sellCall);
|
|
659
|
-
// 2. 利润刮取(从第一个买家余额扣除)
|
|
660
|
-
if (profitAmount > 0n) {
|
|
661
|
-
calls.push({
|
|
662
|
-
target: buyerWallets[0].address,
|
|
663
|
-
allowFailure: false,
|
|
664
|
-
value: 0n,
|
|
665
|
-
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
// 3. 所有买家买入(使用自己预存的 OKB)
|
|
669
|
-
for (let i = 0; i < buyerWallets.length; i++) {
|
|
670
|
-
const buyCall = buildBuyCallInternal(buyerWallets[i], buyAmountsPerBuyer[i], tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
671
|
-
calls.push(buyCall);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
// ========================================
|
|
675
|
-
// 构建交易
|
|
676
|
-
// ========================================
|
|
677
|
-
const signedTransaction = buildEIP7702TransactionSync(sellerWallet, authorizations, calls, 0n, sellerNonce, feeData, config);
|
|
678
|
-
return {
|
|
679
|
-
signedTransaction,
|
|
680
|
-
hopWallets: hopWalletInfos,
|
|
681
|
-
metadata: {
|
|
682
|
-
sellerAddress: sellerWallet.address,
|
|
683
|
-
buyerAddresses: buyerWallets.map(w => w.address),
|
|
684
|
-
sellAmount: ethers.formatUnits(sellAmountWei, tokenDecimals),
|
|
685
|
-
estimatedOkbOut: ethers.formatEther(estimatedOkbOut),
|
|
686
|
-
estimatedBuyAmount: ethers.formatEther(totalBuyAmountAfterProfit),
|
|
687
|
-
profitAmount: ethers.formatEther(profitAmount),
|
|
688
|
-
profitRecipient,
|
|
689
|
-
userType,
|
|
690
|
-
useFundUtilization,
|
|
691
|
-
},
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
// ========================================
|
|
695
|
-
// 多对多换手 (M sellers → N buyers)
|
|
696
|
-
// ========================================
|
|
697
|
-
/**
|
|
698
|
-
* 多对多换手 - 多个卖家 + 多个买家
|
|
699
|
-
*
|
|
700
|
-
* 核心逻辑:[利润刮取] + [卖出Ops...] + [买入Ops...]
|
|
701
|
-
*
|
|
702
|
-
* 特点:
|
|
703
|
-
* - 多个卖家同时卖出
|
|
704
|
-
* - 多个买家同时买入
|
|
705
|
-
* - 所有操作在一笔交易中原子执行
|
|
706
|
-
* - 利润根据 userType 动态计算
|
|
707
|
-
*
|
|
708
|
-
* @example
|
|
709
|
-
* const result = await bundleMultiSwap({
|
|
710
|
-
* mainPrivateKey: '0x...', // Payer 支付 Gas
|
|
711
|
-
* sellerPrivateKeys: ['0x...', '0x...'],
|
|
712
|
-
* buyerPrivateKeys: ['0x...', '0x...'],
|
|
713
|
-
* totalBuyAmount: '0.1', // 总买入 0.1 OKB
|
|
714
|
-
* tokenAddress: '0x...',
|
|
715
|
-
* tradeType: 'V3',
|
|
716
|
-
* userType: 'v0',
|
|
717
|
-
* });
|
|
718
|
-
*/
|
|
719
|
-
export async function bundleMultiSwap(params) {
|
|
720
|
-
const { mainPrivateKey, sellerPrivateKeys, buyerPrivateKeys, totalBuyAmount, tokenAddress, tokenDecimals = 18, sellPercent = 100, tradeType = 'V3', routerAddress, fee = V3_FEE_TIERS.MEDIUM, userType = 'v0', config, } = params;
|
|
721
|
-
// ✅ 验证私钥格式的辅助函数
|
|
722
|
-
const isValidPrivateKey = (pk) => {
|
|
723
|
-
const trimmed = (pk || '').trim();
|
|
724
|
-
return trimmed.length >= 64 && (trimmed.startsWith('0x') || /^[0-9a-fA-F]{64}$/.test(trimmed));
|
|
725
|
-
};
|
|
726
|
-
if (!mainPrivateKey || !isValidPrivateKey(mainPrivateKey)) {
|
|
727
|
-
throw new Error('必须提供有效的 mainPrivateKey(Payer 钱包私钥)');
|
|
728
|
-
}
|
|
729
|
-
// ✅ 验证卖家私钥
|
|
730
|
-
const validSellerPrivateKeys = sellerPrivateKeys.filter(pk => isValidPrivateKey(pk));
|
|
731
|
-
if (validSellerPrivateKeys.length === 0) {
|
|
732
|
-
throw new Error('没有有效的卖家私钥');
|
|
733
|
-
}
|
|
734
|
-
// ✅ 验证买家私钥
|
|
735
|
-
const validBuyerPrivateKeys = buyerPrivateKeys.filter(pk => isValidPrivateKey(pk));
|
|
736
|
-
if (validBuyerPrivateKeys.length === 0) {
|
|
737
|
-
throw new Error('没有有效的买家私钥');
|
|
738
|
-
}
|
|
739
|
-
if (validSellerPrivateKeys.length !== sellerPrivateKeys.length || validBuyerPrivateKeys.length !== buyerPrivateKeys.length) {
|
|
740
|
-
console.warn(`[bundleMultiSwap] 跳过无效私钥: 卖家 ${sellerPrivateKeys.length - validSellerPrivateKeys.length} 个, 买家 ${buyerPrivateKeys.length - validBuyerPrivateKeys.length} 个`);
|
|
741
|
-
}
|
|
742
|
-
const provider = getCachedProvider(config?.rpcUrl);
|
|
743
|
-
// ✅ 空字符串也使用默认值
|
|
744
|
-
const delegateAddress = (config?.delegateAddress && config.delegateAddress.trim()) || UNIFIED_DELEGATE_ADDRESS;
|
|
745
|
-
const delegateInterface = new ethers.Interface(UNIFIED_DELEGATE_ABI);
|
|
746
|
-
const portalInterface = new ethers.Interface(FLAP_PORTAL_ABI);
|
|
747
|
-
const actualRouter = routerAddress ?? getDefaultRouter(tradeType);
|
|
748
|
-
// ✅ 创建 Payer 钱包(支付 Gas 费)
|
|
749
|
-
const mainWallet = createWallet(mainPrivateKey, provider);
|
|
750
|
-
// 创建钱包
|
|
751
|
-
const sellerWallets = validSellerPrivateKeys.map(pk => createWallet(pk, provider));
|
|
752
|
-
const buyerWallets = validBuyerPrivateKeys.map(pk => createWallet(pk, provider));
|
|
753
|
-
// 合并所有钱包(去重,包括 mainWallet)
|
|
754
|
-
const allWalletsMap = new Map();
|
|
755
|
-
allWalletsMap.set(mainWallet.address.toLowerCase(), mainWallet);
|
|
756
|
-
sellerWallets.forEach(w => allWalletsMap.set(w.address.toLowerCase(), w));
|
|
757
|
-
buyerWallets.forEach(w => allWalletsMap.set(w.address.toLowerCase(), w));
|
|
758
|
-
const allWallets = Array.from(allWalletsMap.values());
|
|
759
|
-
// 找出 mainWallet 在 allWallets 中的索引
|
|
760
|
-
const mainWalletIndex = allWallets.findIndex(w => w.address.toLowerCase() === mainWallet.address.toLowerCase());
|
|
761
|
-
// 计算买入金额
|
|
762
|
-
const totalBuyAmountWei = ethers.parseEther(truncateDecimals(totalBuyAmount));
|
|
763
|
-
const buyAmountPerWallet = totalBuyAmountWei / BigInt(buyerWallets.length);
|
|
764
|
-
// ✅ 计算利润(根据 userType 和交易数量)
|
|
765
|
-
const totalTxCount = sellerWallets.length + buyerWallets.length;
|
|
766
|
-
const profitAmount = calculateProfitAmountByTxCount(totalBuyAmountWei, totalTxCount, userType);
|
|
767
|
-
const profitRecipient = getProfitRecipient(config);
|
|
768
|
-
// ========================================
|
|
769
|
-
// 并行获取数据
|
|
770
|
-
// ========================================
|
|
771
|
-
const allAddresses = allWallets.map(w => w.address);
|
|
772
|
-
// 获取卖出钱包的代币余额
|
|
773
|
-
const tokenContract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
|
|
774
|
-
const [nonces, feeData, sellerBalances] = await Promise.all([
|
|
775
|
-
batchGetNonces(allAddresses, provider),
|
|
776
|
-
provider.getFeeData(),
|
|
777
|
-
Promise.all(sellerWallets.map(w => tokenContract.balanceOf(w.address))),
|
|
778
|
-
]);
|
|
779
|
-
// 计算每个卖家的卖出金额(按百分比)
|
|
780
|
-
const percentBigInt = BigInt(Math.min(100, Math.max(1, sellPercent)));
|
|
781
|
-
const sellAmountsWei = sellerBalances.map((balance) => (BigInt(balance.toString()) * percentBigInt) / 100n);
|
|
782
|
-
const totalSellAmount = sellAmountsWei.reduce((sum, amt) => sum + amt, 0n);
|
|
783
|
-
// ========================================
|
|
784
|
-
// 同步签署授权
|
|
785
|
-
// ========================================
|
|
786
|
-
const authorizations = signAuthorizationsWithNonces(allWallets, nonces, delegateAddress, mainWalletIndex);
|
|
787
|
-
// ========================================
|
|
788
|
-
// 构建调用
|
|
789
|
-
// ========================================
|
|
790
|
-
const calls = [];
|
|
791
|
-
// 卖出调用
|
|
792
|
-
for (let i = 0; i < sellerWallets.length; i++) {
|
|
793
|
-
if (sellAmountsWei[i] <= 0n)
|
|
794
|
-
continue;
|
|
795
|
-
const sellCall = buildSellCallWithAmountInternal(sellerWallets[i], sellAmountsWei[i], tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
796
|
-
calls.push(sellCall);
|
|
797
|
-
}
|
|
798
|
-
// ✅ 修复:利润刮取放在卖出之后、买入之前(从第一个卖家余额转账)
|
|
799
|
-
if (profitAmount > 0n && sellerWallets.length > 0) {
|
|
800
|
-
calls.push({
|
|
801
|
-
target: sellerWallets[0].address, // 从第一个卖家扣利润
|
|
802
|
-
allowFailure: false,
|
|
803
|
-
value: 0n,
|
|
804
|
-
callData: delegateInterface.encodeFunctionData('transferAmount', [profitRecipient, profitAmount]),
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
|
-
// 买入调用
|
|
808
|
-
for (let i = 0; i < buyerWallets.length; i++) {
|
|
809
|
-
const buyCall = buildBuyCallInternal(buyerWallets[i], buyAmountPerWallet, tokenAddress, tradeType, actualRouter, fee, delegateInterface, portalInterface);
|
|
810
|
-
calls.push(buyCall);
|
|
811
|
-
}
|
|
812
|
-
// ========================================
|
|
813
|
-
// 构建交易
|
|
814
|
-
// ========================================
|
|
815
|
-
// ✅ 修复:利润使用 transferAmount 从卖家余额转账,不需要通过 msg.value 传递
|
|
816
|
-
const totalValue = 0n;
|
|
817
|
-
const signedTransaction = buildEIP7702TransactionSync(mainWallet, authorizations, calls, totalValue, nonces[mainWalletIndex], feeData, config);
|
|
818
|
-
return {
|
|
819
|
-
signedTransaction,
|
|
820
|
-
metadata: {
|
|
821
|
-
payerAddress: mainWallet.address,
|
|
822
|
-
sellerAddresses: sellerWallets.map(w => w.address),
|
|
823
|
-
buyerAddresses: buyerWallets.map(w => w.address),
|
|
824
|
-
sellerCount: sellerWallets.filter((_, i) => sellAmountsWei[i] > 0n).length,
|
|
825
|
-
buyerCount: buyerWallets.length,
|
|
826
|
-
totalBuyAmount: ethers.formatEther(totalBuyAmountWei),
|
|
827
|
-
totalSellAmount: ethers.formatUnits(totalSellAmount, tokenDecimals),
|
|
828
|
-
profitAmount: ethers.formatEther(profitAmount),
|
|
829
|
-
profitRecipient,
|
|
830
|
-
userType,
|
|
831
|
-
},
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
// ========================================
|
|
835
|
-
// 内部工具函数
|
|
836
|
-
// ========================================
|
|
837
|
-
function buildBuyCallInternal(wallet, amount, tokenAddress, tradeType, routerAddress, fee, delegateInterface, portalInterface) {
|
|
838
|
-
switch (tradeType) {
|
|
839
|
-
case 'FLAP': {
|
|
840
|
-
// ✅ FLAP: 使用 swapExactInput 买入(与 AA 模式保持一致)
|
|
841
|
-
// 原 buy(address token) ABI 与 Portal 不匹配,改用 swapExactInput
|
|
842
|
-
const portalCallData = portalInterface.encodeFunctionData('swapExactInput', [{
|
|
843
|
-
inputToken: ethers.ZeroAddress, // 0x0 = OKB (原生代币)
|
|
844
|
-
outputToken: tokenAddress,
|
|
845
|
-
inputAmount: amount,
|
|
846
|
-
minOutputAmount: 0n,
|
|
847
|
-
permitData: '0x',
|
|
848
|
-
}]);
|
|
849
|
-
const batchCalls = [{ target: FLAP_PORTAL_ADDRESS, value: amount, data: portalCallData }];
|
|
850
|
-
return {
|
|
851
|
-
target: wallet.address,
|
|
852
|
-
allowFailure: false,
|
|
853
|
-
value: 0n,
|
|
854
|
-
callData: delegateInterface.encodeFunctionData('executeBatch', [batchCalls]),
|
|
855
|
-
};
|
|
856
|
-
}
|
|
857
|
-
case 'V2': {
|
|
858
|
-
const path = [WOKB_ADDRESS, tokenAddress];
|
|
859
|
-
// ✅ 修复:使用指定的买入金额,而不是 0(使用全部余额会导致严重 bug)
|
|
860
|
-
return {
|
|
861
|
-
target: wallet.address,
|
|
862
|
-
allowFailure: false,
|
|
863
|
-
value: 0n,
|
|
864
|
-
callData: delegateInterface.encodeFunctionData('executeBuyV2', [routerAddress, path, amount]),
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
case 'V3': {
|
|
868
|
-
// ✅ 修复:使用指定的买入金额,而不是 0(使用全部余额会导致严重 bug)
|
|
869
|
-
return {
|
|
870
|
-
target: wallet.address,
|
|
871
|
-
allowFailure: false,
|
|
872
|
-
value: 0n,
|
|
873
|
-
callData: delegateInterface.encodeFunctionData('executeBuy', [
|
|
874
|
-
routerAddress, WOKB_ADDRESS, tokenAddress, fee, amount
|
|
875
|
-
]),
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
function buildSellCallWithAmountInternal(wallet, amount, tokenAddress, tradeType, routerAddress, fee, delegateInterface, portalInterface) {
|
|
881
|
-
switch (tradeType) {
|
|
882
|
-
case 'FLAP': {
|
|
883
|
-
// ✅ 使用 swapExactInput 卖出(与 AA 模式保持一致)
|
|
884
|
-
const portalCallData = portalInterface.encodeFunctionData('swapExactInput', [{
|
|
885
|
-
inputToken: tokenAddress,
|
|
886
|
-
outputToken: ethers.ZeroAddress, // 0x0 = OKB
|
|
887
|
-
inputAmount: amount,
|
|
888
|
-
minOutputAmount: 0n,
|
|
889
|
-
permitData: '0x',
|
|
890
|
-
}]);
|
|
891
|
-
const batchCalls = [{ target: FLAP_PORTAL_ADDRESS, value: 0n, data: portalCallData }];
|
|
892
|
-
return {
|
|
893
|
-
target: wallet.address,
|
|
894
|
-
allowFailure: false,
|
|
895
|
-
value: 0n,
|
|
896
|
-
callData: delegateInterface.encodeFunctionData('executeBatch', [batchCalls]),
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
case 'V2': {
|
|
900
|
-
const path = [tokenAddress, WOKB_ADDRESS];
|
|
901
|
-
return {
|
|
902
|
-
target: wallet.address,
|
|
903
|
-
allowFailure: false,
|
|
904
|
-
value: 0n,
|
|
905
|
-
// ✅ 修复:传入实际数量,避免卖出全部
|
|
906
|
-
callData: delegateInterface.encodeFunctionData('executeSellV2', [routerAddress, path, amount]),
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
|
-
case 'V3': {
|
|
910
|
-
return {
|
|
911
|
-
target: wallet.address,
|
|
912
|
-
allowFailure: false,
|
|
913
|
-
value: 0n,
|
|
914
|
-
// ✅ 修复:传入实际数量,避免卖出全部
|
|
915
|
-
callData: delegateInterface.encodeFunctionData('executeSell', [
|
|
916
|
-
routerAddress, tokenAddress, WOKB_ADDRESS, fee, amount
|
|
917
|
-
]),
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|