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.
Files changed (220) hide show
  1. package/dist/__tests__/subpath-exports.test.js +64 -0
  2. package/dist/chains/bsc/iro.d.ts +5 -0
  3. package/dist/chains/bsc/iro.js +4 -0
  4. package/dist/chains/eni/flat-aliases.d.ts +10 -0
  5. package/dist/chains/eni/flat-aliases.js +8 -0
  6. package/dist/chains/eni/index.d.ts +1 -0
  7. package/dist/chains/eni/index.js +1 -0
  8. package/dist/chains/index.d.ts +13 -0
  9. package/dist/chains/index.js +13 -0
  10. package/dist/chains/xlayer/eip7702/flat-aliases.d.ts +13 -0
  11. package/dist/chains/xlayer/eip7702/flat-aliases.js +10 -0
  12. package/dist/chains/xlayer/eip7702/index.d.ts +1 -0
  13. package/dist/chains/xlayer/eip7702/index.js +1 -0
  14. package/dist/chains/xlayer/index.d.ts +3 -2
  15. package/dist/chains/xlayer/index.js +4 -7
  16. package/dist/flap/index.d.ts +10 -0
  17. package/dist/flap/index.js +8 -0
  18. package/dist/merkle/index.d.ts +12 -0
  19. package/dist/merkle/index.js +11 -0
  20. package/dist/shared/constants/index.d.ts +2 -0
  21. package/dist/shared/index.d.ts +2 -0
  22. package/dist/vanity/index.d.ts +5 -0
  23. package/dist/vanity/index.js +5 -0
  24. package/package.json +93 -2
  25. package/dist/chains/bsc/four/disperse.d.ts +0 -12
  26. package/dist/chains/bsc/four/disperse.js +0 -470
  27. package/dist/chains/bsc/four/pairwise.d.ts +0 -7
  28. package/dist/chains/bsc/four/pairwise.js +0 -308
  29. package/dist/chains/bsc/four/submit/blockrazor.d.ts +0 -18
  30. package/dist/chains/bsc/four/submit/blockrazor.js +0 -86
  31. package/dist/chains/bsc/four/submit/direct.d.ts +0 -66
  32. package/dist/chains/bsc/four/submit/direct.js +0 -452
  33. package/dist/chains/bsc/four/submit/helpers.d.ts +0 -18
  34. package/dist/chains/bsc/four/submit/helpers.js +0 -57
  35. package/dist/chains/bsc/four/submit/index.d.ts +0 -12
  36. package/dist/chains/bsc/four/submit/index.js +0 -11
  37. package/dist/chains/bsc/four/submit/merkle.d.ts +0 -18
  38. package/dist/chains/bsc/four/submit/merkle.js +0 -74
  39. package/dist/chains/bsc/four/submit/types.d.ts +0 -143
  40. package/dist/chains/bsc/four/swap/index.d.ts +0 -32
  41. package/dist/chains/bsc/four/swap/index.js +0 -829
  42. package/dist/chains/bsc/four/swap/types.d.ts +0 -70
  43. package/dist/chains/bsc/four/swap/types.js +0 -1
  44. package/dist/chains/bsc/four/sweep.d.ts +0 -13
  45. package/dist/chains/bsc/four/sweep.js +0 -788
  46. package/dist/chains/bsc/four/utils/index.d.ts +0 -20
  47. package/dist/chains/bsc/four/utils/index.js +0 -1558
  48. package/dist/chains/bsc/four/utils/types.d.ts +0 -1
  49. package/dist/chains/bsc/four/utils/types.js +0 -1
  50. package/dist/chains/bsc/pancake/bundle-buy-first/index.d.ts +0 -8
  51. package/dist/chains/bsc/pancake/bundle-buy-first/index.js +0 -907
  52. package/dist/chains/bsc/pancake/bundle-buy-first/types.d.ts +0 -73
  53. package/dist/chains/bsc/pancake/bundle-buy-first/types.js +0 -1
  54. package/dist/chains/bsc/pancake/bundle-swap/helpers.d.ts +0 -102
  55. package/dist/chains/bsc/pancake/bundle-swap/helpers.js +0 -572
  56. package/dist/chains/bsc/pancake/bundle-swap/index.d.ts +0 -50
  57. package/dist/chains/bsc/pancake/bundle-swap/index.js +0 -1066
  58. package/dist/chains/bsc/pancake/bundle-swap/types.d.ts +0 -202
  59. package/dist/chains/bsc/pancake/bundle-swap/types.js +0 -3
  60. package/dist/chains/xlayer/eip7702/bundle-swap/index.d.ts +0 -72
  61. package/dist/chains/xlayer/eip7702/bundle-swap/index.js +0 -921
  62. package/dist/chains/xlayer/eip7702/bundle-swap/types.d.ts +0 -65
  63. package/dist/chains/xlayer/eip7702/bundle-swap/types.js +0 -1
  64. package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.d.ts +0 -128
  65. package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.js +0 -857
  66. package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.d.ts +0 -85
  67. package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.js +0 -1
  68. package/dist/chains/xlayer/eip7702/volume/index.d.ts +0 -96
  69. package/dist/chains/xlayer/eip7702/volume/index.js +0 -793
  70. package/dist/chains/xlayer/eip7702/volume/types.d.ts +0 -124
  71. package/dist/chains/xlayer/eip7702/volume/types.js +0 -1
  72. package/dist/chains/xlayer/eoa/types-core.d.ts +0 -363
  73. package/dist/chains/xlayer/eoa/types-core.js +0 -53
  74. package/dist/chains/xlayer/eoa/types-create.d.ts +0 -413
  75. package/dist/chains/xlayer/eoa/types-create.js +0 -9
  76. package/dist/chains/xlayer/eoa/types-volume.d.ts +0 -209
  77. package/dist/chains/xlayer/eoa/types-volume.js +0 -13
  78. package/dist/dex/direct-router/index.d.ts +0 -70
  79. package/dist/dex/direct-router/index.js +0 -1410
  80. package/dist/dex/direct-router/types.d.ts +0 -81
  81. package/dist/dex/direct-router/types.js +0 -1
  82. package/dist/shared/abis/TaxToken.json +0 -969
  83. package/dist/shared/abis/TokenManager.json +0 -836
  84. package/dist/shared/abis/TokenManager2.json +0 -136
  85. package/dist/shared/abis/TokenManagerHelper3.json +0 -993
  86. package/dist/shared/abis 2/TaxToken.json +0 -105
  87. package/dist/shared/abis 2/TokenManager.json +0 -836
  88. package/dist/shared/abis 2/TokenManager2.json +0 -60
  89. package/dist/shared/abis 2/TokenManagerHelper3.json +0 -993
  90. package/dist/shared/abis 2/common.d.ts +0 -85
  91. package/dist/shared/abis 2/common.js +0 -254
  92. package/dist/shared/abis 2/index.d.ts +0 -8
  93. package/dist/shared/abis 2/index.js +0 -8
  94. package/dist/shared/clients 2/blockrazor.d.ts +0 -314
  95. package/dist/shared/clients 2/blockrazor.js +0 -596
  96. package/dist/shared/clients 2/club48.d.ts +0 -154
  97. package/dist/shared/clients 2/club48.js +0 -331
  98. package/dist/shared/clients 2/emitservice.d.ts +0 -47
  99. package/dist/shared/clients 2/emitservice.js +0 -44
  100. package/dist/shared/clients 2/four.d.ts +0 -132
  101. package/dist/shared/clients 2/four.js +0 -281
  102. package/dist/shared/clients 2/merkle.d.ts +0 -210
  103. package/dist/shared/clients 2/merkle.js +0 -400
  104. package/dist/shared/flap/__tests__/curve.test.d.ts +0 -1
  105. package/dist/shared/flap/__tests__/curve.test.js +0 -85
  106. package/dist/shared/flap/portal/index.d.ts +0 -12
  107. package/dist/shared/flap/portal/index.js +0 -11
  108. package/dist/shared/flap/portal/portal.d.ts +0 -47
  109. package/dist/shared/flap/portal/portal.js +0 -218
  110. package/dist/shared/flap/portal/types.d.ts +0 -227
  111. package/dist/shared/flap/portal/types.js +0 -80
  112. package/dist/shared/flap/portal/writer.d.ts +0 -121
  113. package/dist/shared/flap/portal/writer.js +0 -265
  114. package/dist/shared/flap/portal-bundle-merkle/core/index.d.ts +0 -18
  115. package/dist/shared/flap/portal-bundle-merkle/core/index.js +0 -938
  116. package/dist/shared/flap/portal-bundle-merkle/core/types.d.ts +0 -1
  117. package/dist/shared/flap/portal-bundle-merkle/core/types.js +0 -1
  118. package/dist/shared/flap/portal-bundle-merkle/swap/index.d.ts +0 -42
  119. package/dist/shared/flap/portal-bundle-merkle/swap/index.js +0 -1448
  120. package/dist/shared/flap/portal-bundle-merkle/swap/types.d.ts +0 -84
  121. package/dist/shared/flap/portal-bundle-merkle/swap/types.js +0 -1
  122. package/dist/shared/flap/portal-bundle-merkle/utils/index.d.ts +0 -17
  123. package/dist/shared/flap/portal-bundle-merkle/utils/index.js +0 -1024
  124. package/dist/shared/flap/portal-bundle-merkle/utils/types.d.ts +0 -16
  125. package/dist/shared/flap/portal-bundle-merkle/utils/types.js +0 -1
  126. package/dist/shared/flap/portal-bundle-merkle/utils-helpers.d.ts +0 -100
  127. package/dist/shared/flap/portal-bundle-merkle/utils-helpers.js +0 -133
  128. package/dist/shared/flap 2/__tests__/curve.test.d.ts +0 -1
  129. package/dist/shared/flap 2/__tests__/curve.test.js +0 -85
  130. package/dist/shared/flap 2/abi.d.ts +0 -4
  131. package/dist/shared/flap 2/abi.js +0 -4
  132. package/dist/shared/flap 2/constants.d.ts +0 -128
  133. package/dist/shared/flap 2/constants.js +0 -143
  134. package/dist/shared/flap 2/curve.d.ts +0 -33
  135. package/dist/shared/flap 2/curve.js +0 -84
  136. package/dist/shared/flap 2/errors.d.ts +0 -37
  137. package/dist/shared/flap 2/errors.js +0 -114
  138. package/dist/shared/flap 2/index.d.ts +0 -22
  139. package/dist/shared/flap 2/index.js +0 -33
  140. package/dist/shared/flap 2/ipfs.d.ts +0 -21
  141. package/dist/shared/flap 2/ipfs.js +0 -38
  142. package/dist/shared/flap 2/meta.d.ts +0 -30
  143. package/dist/shared/flap 2/meta.js +0 -195
  144. package/dist/shared/flap 2/permit.d.ts +0 -16
  145. package/dist/shared/flap 2/permit.js +0 -67
  146. package/dist/shared/flap 2/pinata.d.ts +0 -40
  147. package/dist/shared/flap 2/pinata.js +0 -106
  148. package/dist/shared/flap 2/portal-bundle-merkle/config.d.ts +0 -79
  149. package/dist/shared/flap 2/portal-bundle-merkle/config.js +0 -133
  150. package/dist/shared/flap 2/portal-bundle-merkle/core.d.ts +0 -18
  151. package/dist/shared/flap 2/portal-bundle-merkle/core.js +0 -938
  152. package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.d.ts +0 -125
  153. package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.js +0 -665
  154. package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.d.ts +0 -88
  155. package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.js +0 -446
  156. package/dist/shared/flap 2/portal-bundle-merkle/index.d.ts +0 -14
  157. package/dist/shared/flap 2/portal-bundle-merkle/index.js +0 -26
  158. package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.d.ts +0 -28
  159. package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.js +0 -701
  160. package/dist/shared/flap 2/portal-bundle-merkle/private.d.ts +0 -17
  161. package/dist/shared/flap 2/portal-bundle-merkle/private.js +0 -549
  162. package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.d.ts +0 -65
  163. package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.js +0 -831
  164. package/dist/shared/flap 2/portal-bundle-merkle/swap.d.ts +0 -201
  165. package/dist/shared/flap 2/portal-bundle-merkle/swap.js +0 -1359
  166. package/dist/shared/flap 2/portal-bundle-merkle/types.d.ts +0 -358
  167. package/dist/shared/flap 2/portal-bundle-merkle/types.js +0 -1
  168. package/dist/shared/flap 2/portal-bundle-merkle/utils.d.ts +0 -89
  169. package/dist/shared/flap 2/portal-bundle-merkle/utils.js +0 -963
  170. package/dist/shared/flap 2/portal-bundle.d.ts +0 -119
  171. package/dist/shared/flap 2/portal-bundle.js +0 -584
  172. package/dist/shared/flap 2/portal.d.ts +0 -392
  173. package/dist/shared/flap 2/portal.js +0 -559
  174. package/dist/shared/flap 2/vanity.d.ts +0 -48
  175. package/dist/shared/flap 2/vanity.js +0 -110
  176. package/dist/shared/flap 2/vault.d.ts +0 -240
  177. package/dist/shared/flap 2/vault.js +0 -366
  178. package/dist/shared/four 2/index.d.ts +0 -7
  179. package/dist/shared/four 2/index.js +0 -22
  180. package/dist/shared/four 2/tax-token.d.ts +0 -176
  181. package/dist/shared/four 2/tax-token.js +0 -302
  182. package/dist/shared/index 2.js +0 -10
  183. package/dist/shared/index.d 2.ts +0 -10
  184. package/dist/types/distribute.d.ts +0 -72
  185. package/dist/types/distribute.js +0 -1
  186. package/dist/types 2/errors.d.ts +0 -27
  187. package/dist/types 2/errors.js +0 -34
  188. package/dist/utils/__tests__/errors.test.d.ts +0 -1
  189. package/dist/utils/__tests__/errors.test.js +0 -76
  190. package/dist/utils/airdrop-sweep-types.d.ts +0 -1
  191. package/dist/utils/airdrop-sweep-types.js +0 -1
  192. package/dist/utils/erc20/index.d.ts +0 -242
  193. package/dist/utils/erc20/index.js +0 -645
  194. package/dist/utils/erc20/types.d.ts +0 -77
  195. package/dist/utils/erc20/types.js +0 -1
  196. package/dist/utils/erc20-types.d.ts +0 -1
  197. package/dist/utils/erc20-types.js +0 -1
  198. package/dist/utils/holders-maker/helpers.d.ts +0 -43
  199. package/dist/utils/holders-maker/helpers.js +0 -371
  200. package/dist/utils/holders-maker/index.d.ts +0 -26
  201. package/dist/utils/holders-maker/index.js +0 -218
  202. package/dist/utils/holders-maker/types.d.ts +0 -72
  203. package/dist/utils/holders-maker/types.js +0 -4
  204. package/dist/utils/holders-maker-types.d.ts +0 -1
  205. package/dist/utils/holders-maker-types.js +0 -1
  206. package/dist/utils/lp-inspect/index.d.ts +0 -44
  207. package/dist/utils/lp-inspect/index.js +0 -937
  208. package/dist/utils/lp-inspect/types.d.ts +0 -100
  209. package/dist/utils/lp-inspect/types.js +0 -1
  210. package/dist/utils/lp-inspect-types.d.ts +0 -1
  211. package/dist/utils/lp-inspect-types.js +0 -1
  212. package/dist/utils/private-sale-types.d.ts +0 -1
  213. package/dist/utils/private-sale-types.js +0 -1
  214. package/dist/utils/quote-helpers/index.d.ts +0 -107
  215. package/dist/utils/quote-helpers/index.js +0 -346
  216. package/dist/utils/quote-helpers/types.d.ts +0 -16
  217. package/dist/utils/quote-helpers/types.js +0 -1
  218. package/dist/utils/quote-helpers-types.d.ts +0 -1
  219. package/dist/utils/quote-helpers-types.js +0 -1
  220. /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
- }