four-flap-meme-sdk 2.0.0 → 2.1.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 (205) hide show
  1. package/dist/__tests__/subpath-exports.test.js +34 -0
  2. package/dist/chains/index.d.ts +13 -0
  3. package/dist/chains/index.js +13 -0
  4. package/dist/chains/xlayer/eip7702/index.d.ts +2 -0
  5. package/dist/flap/index.d.ts +10 -0
  6. package/dist/flap/index.js +8 -0
  7. package/dist/shared/constants/index.d.ts +2 -0
  8. package/dist/shared/index.d.ts +2 -0
  9. package/package.json +66 -1
  10. package/dist/chains/bsc/four/disperse.d.ts +0 -12
  11. package/dist/chains/bsc/four/disperse.js +0 -470
  12. package/dist/chains/bsc/four/pairwise.d.ts +0 -7
  13. package/dist/chains/bsc/four/pairwise.js +0 -308
  14. package/dist/chains/bsc/four/submit/blockrazor.d.ts +0 -18
  15. package/dist/chains/bsc/four/submit/blockrazor.js +0 -86
  16. package/dist/chains/bsc/four/submit/direct.d.ts +0 -66
  17. package/dist/chains/bsc/four/submit/direct.js +0 -452
  18. package/dist/chains/bsc/four/submit/helpers.d.ts +0 -18
  19. package/dist/chains/bsc/four/submit/helpers.js +0 -57
  20. package/dist/chains/bsc/four/submit/index.d.ts +0 -12
  21. package/dist/chains/bsc/four/submit/index.js +0 -11
  22. package/dist/chains/bsc/four/submit/merkle.d.ts +0 -18
  23. package/dist/chains/bsc/four/submit/merkle.js +0 -74
  24. package/dist/chains/bsc/four/submit/types.d.ts +0 -143
  25. package/dist/chains/bsc/four/swap/index.d.ts +0 -32
  26. package/dist/chains/bsc/four/swap/index.js +0 -829
  27. package/dist/chains/bsc/four/swap/types.d.ts +0 -70
  28. package/dist/chains/bsc/four/swap/types.js +0 -1
  29. package/dist/chains/bsc/four/sweep.d.ts +0 -13
  30. package/dist/chains/bsc/four/sweep.js +0 -788
  31. package/dist/chains/bsc/four/utils/index.d.ts +0 -20
  32. package/dist/chains/bsc/four/utils/index.js +0 -1558
  33. package/dist/chains/bsc/four/utils/types.d.ts +0 -1
  34. package/dist/chains/bsc/four/utils/types.js +0 -1
  35. package/dist/chains/bsc/pancake/bundle-buy-first/index.d.ts +0 -8
  36. package/dist/chains/bsc/pancake/bundle-buy-first/index.js +0 -907
  37. package/dist/chains/bsc/pancake/bundle-buy-first/types.d.ts +0 -73
  38. package/dist/chains/bsc/pancake/bundle-buy-first/types.js +0 -1
  39. package/dist/chains/bsc/pancake/bundle-swap/helpers.d.ts +0 -102
  40. package/dist/chains/bsc/pancake/bundle-swap/helpers.js +0 -572
  41. package/dist/chains/bsc/pancake/bundle-swap/index.d.ts +0 -50
  42. package/dist/chains/bsc/pancake/bundle-swap/index.js +0 -1066
  43. package/dist/chains/bsc/pancake/bundle-swap/types.d.ts +0 -202
  44. package/dist/chains/bsc/pancake/bundle-swap/types.js +0 -3
  45. package/dist/chains/xlayer/eip7702/bundle-swap/index.d.ts +0 -72
  46. package/dist/chains/xlayer/eip7702/bundle-swap/index.js +0 -921
  47. package/dist/chains/xlayer/eip7702/bundle-swap/types.d.ts +0 -65
  48. package/dist/chains/xlayer/eip7702/bundle-swap/types.js +0 -1
  49. package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.d.ts +0 -128
  50. package/dist/chains/xlayer/eip7702/multi-hop-transfer/index.js +0 -857
  51. package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.d.ts +0 -85
  52. package/dist/chains/xlayer/eip7702/multi-hop-transfer/types.js +0 -1
  53. package/dist/chains/xlayer/eip7702/volume/index.d.ts +0 -96
  54. package/dist/chains/xlayer/eip7702/volume/index.js +0 -793
  55. package/dist/chains/xlayer/eip7702/volume/types.d.ts +0 -124
  56. package/dist/chains/xlayer/eip7702/volume/types.js +0 -1
  57. package/dist/chains/xlayer/eoa/types-core.d.ts +0 -363
  58. package/dist/chains/xlayer/eoa/types-core.js +0 -53
  59. package/dist/chains/xlayer/eoa/types-create.d.ts +0 -413
  60. package/dist/chains/xlayer/eoa/types-create.js +0 -9
  61. package/dist/chains/xlayer/eoa/types-volume.d.ts +0 -209
  62. package/dist/chains/xlayer/eoa/types-volume.js +0 -13
  63. package/dist/dex/direct-router/index.d.ts +0 -70
  64. package/dist/dex/direct-router/index.js +0 -1410
  65. package/dist/dex/direct-router/types.d.ts +0 -81
  66. package/dist/dex/direct-router/types.js +0 -1
  67. package/dist/shared/abis/TaxToken.json +0 -969
  68. package/dist/shared/abis/TokenManager.json +0 -836
  69. package/dist/shared/abis/TokenManager2.json +0 -136
  70. package/dist/shared/abis/TokenManagerHelper3.json +0 -993
  71. package/dist/shared/abis 2/TaxToken.json +0 -105
  72. package/dist/shared/abis 2/TokenManager.json +0 -836
  73. package/dist/shared/abis 2/TokenManager2.json +0 -60
  74. package/dist/shared/abis 2/TokenManagerHelper3.json +0 -993
  75. package/dist/shared/abis 2/common.d.ts +0 -85
  76. package/dist/shared/abis 2/common.js +0 -254
  77. package/dist/shared/abis 2/index.d.ts +0 -8
  78. package/dist/shared/abis 2/index.js +0 -8
  79. package/dist/shared/clients 2/blockrazor.d.ts +0 -314
  80. package/dist/shared/clients 2/blockrazor.js +0 -596
  81. package/dist/shared/clients 2/club48.d.ts +0 -154
  82. package/dist/shared/clients 2/club48.js +0 -331
  83. package/dist/shared/clients 2/emitservice.d.ts +0 -47
  84. package/dist/shared/clients 2/emitservice.js +0 -44
  85. package/dist/shared/clients 2/four.d.ts +0 -132
  86. package/dist/shared/clients 2/four.js +0 -281
  87. package/dist/shared/clients 2/merkle.d.ts +0 -210
  88. package/dist/shared/clients 2/merkle.js +0 -400
  89. package/dist/shared/flap/__tests__/curve.test.d.ts +0 -1
  90. package/dist/shared/flap/__tests__/curve.test.js +0 -85
  91. package/dist/shared/flap/portal/index.d.ts +0 -12
  92. package/dist/shared/flap/portal/index.js +0 -11
  93. package/dist/shared/flap/portal/portal.d.ts +0 -47
  94. package/dist/shared/flap/portal/portal.js +0 -218
  95. package/dist/shared/flap/portal/types.d.ts +0 -227
  96. package/dist/shared/flap/portal/types.js +0 -80
  97. package/dist/shared/flap/portal/writer.d.ts +0 -121
  98. package/dist/shared/flap/portal/writer.js +0 -265
  99. package/dist/shared/flap/portal-bundle-merkle/core/index.d.ts +0 -18
  100. package/dist/shared/flap/portal-bundle-merkle/core/index.js +0 -938
  101. package/dist/shared/flap/portal-bundle-merkle/core/types.d.ts +0 -1
  102. package/dist/shared/flap/portal-bundle-merkle/core/types.js +0 -1
  103. package/dist/shared/flap/portal-bundle-merkle/swap/index.d.ts +0 -42
  104. package/dist/shared/flap/portal-bundle-merkle/swap/index.js +0 -1448
  105. package/dist/shared/flap/portal-bundle-merkle/swap/types.d.ts +0 -84
  106. package/dist/shared/flap/portal-bundle-merkle/swap/types.js +0 -1
  107. package/dist/shared/flap/portal-bundle-merkle/utils/index.d.ts +0 -17
  108. package/dist/shared/flap/portal-bundle-merkle/utils/index.js +0 -1024
  109. package/dist/shared/flap/portal-bundle-merkle/utils/types.d.ts +0 -16
  110. package/dist/shared/flap/portal-bundle-merkle/utils/types.js +0 -1
  111. package/dist/shared/flap/portal-bundle-merkle/utils-helpers.d.ts +0 -100
  112. package/dist/shared/flap/portal-bundle-merkle/utils-helpers.js +0 -133
  113. package/dist/shared/flap 2/__tests__/curve.test.d.ts +0 -1
  114. package/dist/shared/flap 2/__tests__/curve.test.js +0 -85
  115. package/dist/shared/flap 2/abi.d.ts +0 -4
  116. package/dist/shared/flap 2/abi.js +0 -4
  117. package/dist/shared/flap 2/constants.d.ts +0 -128
  118. package/dist/shared/flap 2/constants.js +0 -143
  119. package/dist/shared/flap 2/curve.d.ts +0 -33
  120. package/dist/shared/flap 2/curve.js +0 -84
  121. package/dist/shared/flap 2/errors.d.ts +0 -37
  122. package/dist/shared/flap 2/errors.js +0 -114
  123. package/dist/shared/flap 2/index.d.ts +0 -22
  124. package/dist/shared/flap 2/index.js +0 -33
  125. package/dist/shared/flap 2/ipfs.d.ts +0 -21
  126. package/dist/shared/flap 2/ipfs.js +0 -38
  127. package/dist/shared/flap 2/meta.d.ts +0 -30
  128. package/dist/shared/flap 2/meta.js +0 -195
  129. package/dist/shared/flap 2/permit.d.ts +0 -16
  130. package/dist/shared/flap 2/permit.js +0 -67
  131. package/dist/shared/flap 2/pinata.d.ts +0 -40
  132. package/dist/shared/flap 2/pinata.js +0 -106
  133. package/dist/shared/flap 2/portal-bundle-merkle/config.d.ts +0 -79
  134. package/dist/shared/flap 2/portal-bundle-merkle/config.js +0 -133
  135. package/dist/shared/flap 2/portal-bundle-merkle/core.d.ts +0 -18
  136. package/dist/shared/flap 2/portal-bundle-merkle/core.js +0 -938
  137. package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.d.ts +0 -125
  138. package/dist/shared/flap 2/portal-bundle-merkle/create-to-dex.js +0 -665
  139. package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.d.ts +0 -88
  140. package/dist/shared/flap 2/portal-bundle-merkle/curve-to-dex.js +0 -446
  141. package/dist/shared/flap 2/portal-bundle-merkle/index.d.ts +0 -14
  142. package/dist/shared/flap 2/portal-bundle-merkle/index.js +0 -26
  143. package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.d.ts +0 -28
  144. package/dist/shared/flap 2/portal-bundle-merkle/pancake-proxy.js +0 -701
  145. package/dist/shared/flap 2/portal-bundle-merkle/private.d.ts +0 -17
  146. package/dist/shared/flap 2/portal-bundle-merkle/private.js +0 -549
  147. package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.d.ts +0 -65
  148. package/dist/shared/flap 2/portal-bundle-merkle/swap-buy-first.js +0 -831
  149. package/dist/shared/flap 2/portal-bundle-merkle/swap.d.ts +0 -201
  150. package/dist/shared/flap 2/portal-bundle-merkle/swap.js +0 -1359
  151. package/dist/shared/flap 2/portal-bundle-merkle/types.d.ts +0 -358
  152. package/dist/shared/flap 2/portal-bundle-merkle/types.js +0 -1
  153. package/dist/shared/flap 2/portal-bundle-merkle/utils.d.ts +0 -89
  154. package/dist/shared/flap 2/portal-bundle-merkle/utils.js +0 -963
  155. package/dist/shared/flap 2/portal-bundle.d.ts +0 -119
  156. package/dist/shared/flap 2/portal-bundle.js +0 -584
  157. package/dist/shared/flap 2/portal.d.ts +0 -392
  158. package/dist/shared/flap 2/portal.js +0 -559
  159. package/dist/shared/flap 2/vanity.d.ts +0 -48
  160. package/dist/shared/flap 2/vanity.js +0 -110
  161. package/dist/shared/flap 2/vault.d.ts +0 -240
  162. package/dist/shared/flap 2/vault.js +0 -366
  163. package/dist/shared/four 2/index.d.ts +0 -7
  164. package/dist/shared/four 2/index.js +0 -22
  165. package/dist/shared/four 2/tax-token.d.ts +0 -176
  166. package/dist/shared/four 2/tax-token.js +0 -302
  167. package/dist/shared/index 2.js +0 -10
  168. package/dist/shared/index.d 2.ts +0 -10
  169. package/dist/types/distribute.d.ts +0 -72
  170. package/dist/types/distribute.js +0 -1
  171. package/dist/types 2/errors.d.ts +0 -27
  172. package/dist/types 2/errors.js +0 -34
  173. package/dist/utils/__tests__/errors.test.d.ts +0 -1
  174. package/dist/utils/__tests__/errors.test.js +0 -76
  175. package/dist/utils/airdrop-sweep-types.d.ts +0 -1
  176. package/dist/utils/airdrop-sweep-types.js +0 -1
  177. package/dist/utils/erc20/index.d.ts +0 -242
  178. package/dist/utils/erc20/index.js +0 -645
  179. package/dist/utils/erc20/types.d.ts +0 -77
  180. package/dist/utils/erc20/types.js +0 -1
  181. package/dist/utils/erc20-types.d.ts +0 -1
  182. package/dist/utils/erc20-types.js +0 -1
  183. package/dist/utils/holders-maker/helpers.d.ts +0 -43
  184. package/dist/utils/holders-maker/helpers.js +0 -371
  185. package/dist/utils/holders-maker/index.d.ts +0 -26
  186. package/dist/utils/holders-maker/index.js +0 -218
  187. package/dist/utils/holders-maker/types.d.ts +0 -72
  188. package/dist/utils/holders-maker/types.js +0 -4
  189. package/dist/utils/holders-maker-types.d.ts +0 -1
  190. package/dist/utils/holders-maker-types.js +0 -1
  191. package/dist/utils/lp-inspect/index.d.ts +0 -44
  192. package/dist/utils/lp-inspect/index.js +0 -937
  193. package/dist/utils/lp-inspect/types.d.ts +0 -100
  194. package/dist/utils/lp-inspect/types.js +0 -1
  195. package/dist/utils/lp-inspect-types.d.ts +0 -1
  196. package/dist/utils/lp-inspect-types.js +0 -1
  197. package/dist/utils/private-sale-types.d.ts +0 -1
  198. package/dist/utils/private-sale-types.js +0 -1
  199. package/dist/utils/quote-helpers/index.d.ts +0 -107
  200. package/dist/utils/quote-helpers/index.js +0 -346
  201. package/dist/utils/quote-helpers/types.d.ts +0 -16
  202. package/dist/utils/quote-helpers/types.js +0 -1
  203. package/dist/utils/quote-helpers-types.d.ts +0 -1
  204. package/dist/utils/quote-helpers-types.js +0 -1
  205. /package/dist/{chains/bsc/four/submit/types.js → __tests__/subpath-exports.test.d.ts} +0 -0
@@ -1,937 +0,0 @@
1
- import { Contract, JsonRpcProvider, Interface, formatUnits, formatEther } from 'ethers';
2
- import { ADDRESSES, ZERO_ADDRESS } from '../constants.js';
3
- import { MULTICALL3_ABI, V2_FACTORY_ABI, V2_PAIR_ABI, V3_FACTORY_ABI, ERC20_ABI } from '../../shared/abis/common.js';
4
- import { Helper3 } from '../../contracts/helper3.js';
5
- import { FlapPortal } from '../../shared/flap/portal.js';
6
- // ============================================================================
7
- // 类型定义
8
- // ============================================================================
9
- /** DEX 配置 */
10
- /** 链配置 */
11
- /** 单个交易对信息 */
12
- /** V3 池子信息(带 fee) */
13
- /** DEX 池子信息 */
14
- /** 最佳池子推荐 */
15
- /** LP 平台类型 */
16
- /** 查询选项 */
17
- // ============================================================================
18
- // 链配置
19
- // ============================================================================
20
- /** 默认 V3 费率档位(合并 PancakeSwap 和 Uniswap 的所有档位) */
21
- const DEFAULT_V3_FEE_TIERS = [100, 500, 2500, 3000, 10000];
22
- /** 链 DEX 配置 */
23
- const CHAIN_DEX_CONFIGS = {
24
- BSC: {
25
- wrappedNative: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
26
- wrappedNativeSymbol: 'WBNB',
27
- stableCoins: [
28
- { address: '0x55d398326f99059fF775485246999027B3197955', symbol: 'USDT', decimals: 18 },
29
- { address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', symbol: 'USDC', decimals: 18 },
30
- ],
31
- dexes: {
32
- PANCAKESWAP: {
33
- name: 'PancakeSwap',
34
- v2Factory: '0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73',
35
- v3Factory: '0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865',
36
- enabled: true,
37
- },
38
- IROSWAP: {
39
- name: 'IROSwap',
40
- v2Factory: ADDRESSES.BSC.IroSwapV2Factory,
41
- v2Router: ADDRESSES.BSC.IroSwapV2Router,
42
- enabled: true,
43
- },
44
- }
45
- },
46
- MONAD: {
47
- wrappedNative: '0x3bd359c1119da7da1d913d1c4d2b7c461115433a',
48
- wrappedNativeSymbol: 'WMON',
49
- stableCoins: [
50
- { address: '0x754704bc059f8c67012fed69bc8a327a5aafb603', symbol: 'USDC', decimals: 6 },
51
- ],
52
- dexes: {
53
- PANCAKESWAP: {
54
- name: 'PancakeSwap',
55
- v2Factory: '0x44c90b66eb4c4f814cea6aaf2ac71ca88fa77308',
56
- v3Factory: '0x40ce898c43df68c8c4229cd5ce21d3ce1f3ddcf1',
57
- enabled: true,
58
- },
59
- UNISWAP: {
60
- name: 'Uniswap',
61
- v2Factory: '0x182a927119d56008d921126764bf884221b10f59',
62
- v3Factory: '0x204faca1764b154221e35c0d20abb3c525710498',
63
- v2Router: '0x4b2ab38dbf28d31d467aa8993f6c2585981d6804',
64
- v3Router: '0xd6145b2d3f379919e8cdeda7b97e37c4b2ca9c40',
65
- quoterV2: '0x661e93cca42afacb172121ef892830ca3b70f08d',
66
- enabled: true,
67
- }
68
- }
69
- },
70
- // ✅ 新增 XLayer 配置
71
- XLAYER: {
72
- wrappedNative: '0xe538905cf8410324e03a5a23c1c177a474d59b2b',
73
- wrappedNativeSymbol: 'WOKB',
74
- stableCoins: [
75
- { address: '0x1e4a5963abfd975d8c9021ce480b42188849d41d', symbol: 'USDT', decimals: 6 },
76
- { address: '0x74b7f16337b8972027f6196a17a631ac6de26d22', symbol: 'USDC', decimals: 6 },
77
- // ✅ XLayer: USD₮0 / USDT0(常见外盘配对)
78
- { address: '0x779ded0c9e1022225f8e0630b35a9b54be713736', symbol: 'USDT0', decimals: 6 },
79
- ],
80
- dexes: {
81
- POTATOSWAP: {
82
- name: 'PotatoSwap',
83
- v2Factory: '0x630DB8E822805c82Ca40a54daE02dd5aC31f7fcF', // V2 Factory
84
- v2Router: '0x881fb2f98c13d521009464e7d1cbf16e1b394e8e', // ✅ V2 Router (标准 Uniswap V2 风格)
85
- v3Router: '0xB45D0149249488333E3F3f9F359807F4b810C1FC', // SwapRouter02 (V3 风格)
86
- v3Factory: '0xa1415fAe79c4B196d087F02b8aD5a622B8A827E5', // V3 Factory
87
- quoterV2: '0x5A6f3723346aF54a4D0693bfC1718D64d4915C3e', // QuoterV2
88
- enabled: true,
89
- },
90
- DYORSWAP: {
91
- name: 'DYORSwap',
92
- v2Factory: '0x2CcaDb1e437AA9cDc741574bDa154686B1F04C09', // ✅ V2 Factory
93
- v2Router: '0xfb001fbbace32f09cb6d3c449b935183de53ee96', // DYORSwap Router (SwapRouter02 风格)
94
- enabled: true, // ✅ 已启用
95
- }
96
- }
97
- },
98
- ENI: {
99
- wrappedNative: '0x6d1e851446f4d004ae2a72f9afed85e8829a205e',
100
- wrappedNativeSymbol: 'WEGAS',
101
- stableCoins: [
102
- { address: '0xDC1a8A35b0BaA3229b13f348ED708a2fd50b5e3a', symbol: 'USDT', decimals: 18 },
103
- { address: '0xaFF944b96c1BAEA587159ec446280E468B32ee15', symbol: 'USDC', decimals: 18 },
104
- ],
105
- dexes: {
106
- DSWAP: {
107
- name: 'DSWAP',
108
- v2Factory: '0x548c0e26ce90b333c07abb6d55546304d46d269d',
109
- v2Router: '0x97ed8be49d9a8b86247090aa41e908e76b8fcf22',
110
- v3Factory: '0xa97c5a70Be5B81f573a688F656E7bE569B492A56',
111
- v3Router: '0xcdE487F5B460a516f31FaC520D4e18BA1C8cda49',
112
- enabled: true,
113
- },
114
- IROSWAP: {
115
- name: 'IROSwap',
116
- v2Factory: ADDRESSES.ENI.IroSwapV2Factory,
117
- v2Router: ADDRESSES.ENI.IroSwapV2Router,
118
- enabled: true,
119
- },
120
- }
121
- }
122
- };
123
- // ============================================================================
124
- // 动态配置 API
125
- // ============================================================================
126
- /** 运行时配置存储(内存) */
127
- const runtimeConfigs = {};
128
- /**
129
- * 从 Router 合约获取 Factory 地址
130
- * 支持 Uniswap V2 Router 和 V3 SwapRouter02
131
- */
132
- export async function getFactoryFromRouter(routerAddress, rpcUrl, routerType = 'v2') {
133
- const provider = new JsonRpcProvider(rpcUrl);
134
- const result = {};
135
- // V2 Router ABI: factory()
136
- const V2_ROUTER_ABI = ['function factory() view returns (address)'];
137
- // V3 SwapRouter02 ABI: factory() 和 factoryV2()
138
- const V3_ROUTER_ABI = [
139
- 'function factory() view returns (address)',
140
- 'function factoryV2() view returns (address)',
141
- ];
142
- try {
143
- if (routerType === 'v2') {
144
- const router = new Contract(routerAddress, V2_ROUTER_ABI, provider);
145
- result.v2Factory = await router.factory();
146
- }
147
- else {
148
- // V3 SwapRouter02 可能同时有 V2 和 V3 Factory
149
- const router = new Contract(routerAddress, V3_ROUTER_ABI, provider);
150
- // ✅ 并行获取 V3 和 V2 Factory
151
- const [v3Factory, v2Factory] = await Promise.all([
152
- router.factory().catch(() => undefined),
153
- router.factoryV2().catch(() => undefined)
154
- ]);
155
- if (v3Factory)
156
- result.v3Factory = v3Factory;
157
- if (v2Factory)
158
- result.v2Factory = v2Factory;
159
- }
160
- }
161
- catch (error) {
162
- console.error(`❌ 获取 Factory 地址失败:`, error);
163
- }
164
- return result;
165
- }
166
- /**
167
- * 动态注册 DYORSwap(从 Router 获取 Factory)
168
- */
169
- export async function registerDYORSwap(rpcUrl) {
170
- const DYORSWAP_ROUTER = '0xfb001fbbace32f09cb6d3c449b935183de53ee96';
171
- try {
172
- // 从 Router 获取 Factory 地址
173
- const factories = await getFactoryFromRouter(DYORSWAP_ROUTER, rpcUrl, 'v3');
174
- if (factories.v2Factory || factories.v3Factory) {
175
- const chainConfig = getChainConfig('XLAYER');
176
- if (chainConfig && chainConfig.dexes.DYORSWAP) {
177
- if (factories.v2Factory) {
178
- chainConfig.dexes.DYORSWAP.v2Factory = factories.v2Factory;
179
- }
180
- if (factories.v3Factory) {
181
- chainConfig.dexes.DYORSWAP.v3Factory = factories.v3Factory;
182
- }
183
- chainConfig.dexes.DYORSWAP.enabled = true;
184
- return true;
185
- }
186
- }
187
- return false;
188
- }
189
- catch (error) {
190
- console.error('❌ 注册 DYORSwap 失败:', error);
191
- return false;
192
- }
193
- }
194
- /** 注册新的 DEX */
195
- export function registerDex(chain, dexKey, config) {
196
- const chainConfig = getChainConfig(chain);
197
- if (chainConfig) {
198
- chainConfig.dexes[dexKey] = config;
199
- }
200
- }
201
- /** 注册新的稳定币 */
202
- export function registerStableCoin(chain, coin) {
203
- const chainConfig = getChainConfig(chain);
204
- if (chainConfig) {
205
- // 检查是否已存在
206
- const exists = chainConfig.stableCoins.some(c => c.address.toLowerCase() === coin.address.toLowerCase());
207
- if (!exists) {
208
- chainConfig.stableCoins.push(coin);
209
- }
210
- }
211
- }
212
- /** 获取链配置(优先运行时配置) */
213
- export function getChainConfig(chain) {
214
- if (runtimeConfigs[chain]) {
215
- return runtimeConfigs[chain];
216
- }
217
- if (CHAIN_DEX_CONFIGS[chain]) {
218
- // 深拷贝到运行时配置
219
- runtimeConfigs[chain] = JSON.parse(JSON.stringify(CHAIN_DEX_CONFIGS[chain]));
220
- return runtimeConfigs[chain];
221
- }
222
- return undefined;
223
- }
224
- /** 获取支持的 DEX 列表 */
225
- export function getSupportedDexes(chain) {
226
- const config = getChainConfig(chain);
227
- if (!config)
228
- return [];
229
- return Object.entries(config.dexes).map(([key, dex]) => ({
230
- key,
231
- name: dex.name,
232
- enabled: dex.enabled,
233
- }));
234
- }
235
- /** 获取支持的稳定币列表 */
236
- export function getSupportedStableCoins(chain) {
237
- const config = getChainConfig(chain);
238
- if (!config)
239
- return [];
240
- return config.stableCoins;
241
- }
242
- /** 获取所有报价代币(包装原生 + 稳定币) */
243
- export function getQuoteTokens(chain) {
244
- const config = getChainConfig(chain);
245
- if (!config)
246
- return [];
247
- return [
248
- { address: config.wrappedNative, symbol: config.wrappedNativeSymbol, decimals: 18, isNative: true },
249
- ...config.stableCoins.map(c => ({ ...c, isNative: false })),
250
- ];
251
- }
252
- // ============================================================================
253
- // ABI 别名(从公共模块导入)
254
- // ============================================================================
255
- const I_UNIV2_FACTORY_ABI = V2_FACTORY_ABI;
256
- const I_UNIV2_PAIR_ABI = V2_PAIR_ABI;
257
- const I_ERC20_ABI = ERC20_ABI;
258
- const I_UNIV3_FACTORY_ABI = V3_FACTORY_ABI;
259
- // ============================================================================
260
- // 常量(从公共模块导入)
261
- // ============================================================================
262
- const MULTICALL3_ADDRESS = ADDRESSES.BSC.Multicall3;
263
- /** Provider 缓存 */
264
- const providerCache = new Map();
265
- function getProvider(rpcUrl) {
266
- if (!providerCache.has(rpcUrl)) {
267
- providerCache.set(rpcUrl, new JsonRpcProvider(rpcUrl));
268
- }
269
- return providerCache.get(rpcUrl);
270
- }
271
- /** 精度缓存 */
272
- const decimalsCache = new Map();
273
- async function getTokenDecimals(tokenAddress, provider) {
274
- const key = tokenAddress.toLowerCase();
275
- if (decimalsCache.has(key)) {
276
- return decimalsCache.get(key);
277
- }
278
- try {
279
- const contract = new Contract(tokenAddress, I_ERC20_ABI, provider);
280
- const decimals = await contract.decimals();
281
- const result = Number(decimals);
282
- decimalsCache.set(key, result);
283
- return result;
284
- }
285
- catch {
286
- return 18;
287
- }
288
- }
289
- /** 格式化余额 */
290
- function formatBalance(balance, format, decimals = 18) {
291
- return format ? formatUnits(balance, decimals) : balance.toString();
292
- }
293
- /** 标准化流动性值(转换为统一精度用于比较) */
294
- function normalizeLiquidity(amount, decimals) {
295
- // 将所有值标准化到 18 位精度进行比较
296
- if (decimals < 18) {
297
- return amount * BigInt(10 ** (18 - decimals));
298
- }
299
- else if (decimals > 18) {
300
- return amount / BigInt(10 ** (decimals - 18));
301
- }
302
- return amount;
303
- }
304
- // ============================================================================
305
- // 主查询函数
306
- // ============================================================================
307
- export async function inspectTokenLP(token, opts) {
308
- const provider = getProvider(opts.rpcUrl);
309
- const shouldFormat = opts.formatBalances !== false;
310
- const multicall3 = opts.multicall3 || MULTICALL3_ADDRESS;
311
- const v3FeeTiers = opts.v3FeeTiers || DEFAULT_V3_FEE_TIERS;
312
- // 初始化返回结构
313
- const result = {
314
- platform: 'UNKNOWN',
315
- allPools: [],
316
- bestPools: [],
317
- };
318
- // 获取链配置
319
- let chainConfig = getChainConfig(opts.chain);
320
- if (!chainConfig) {
321
- console.warn(`[LP Inspect] Unknown chain: ${opts.chain}`);
322
- return result;
323
- }
324
- // 应用自定义配置
325
- if (opts.customDexConfig) {
326
- chainConfig = { ...chainConfig, ...opts.customDexConfig };
327
- }
328
- // 查询代币精度和总供应量
329
- let tokenDecimals;
330
- let totalSupplyRaw;
331
- try {
332
- const tokenContract = new Contract(token, [
333
- 'function decimals() view returns (uint8)',
334
- 'function totalSupply() view returns (uint256)'
335
- ], provider);
336
- const [decimals, supply] = await Promise.all([
337
- tokenContract.decimals().catch(() => 18),
338
- tokenContract.totalSupply().catch(() => undefined)
339
- ]);
340
- tokenDecimals = Number(decimals);
341
- totalSupplyRaw = supply ? BigInt(supply) : undefined;
342
- result.decimals = tokenDecimals;
343
- if (totalSupplyRaw !== undefined) {
344
- result.totalSupplyRaw = totalSupplyRaw;
345
- result.totalSupply = formatBalance(totalSupplyRaw, shouldFormat, tokenDecimals);
346
- }
347
- }
348
- catch {
349
- tokenDecimals = 18;
350
- }
351
- // ========================================
352
- // 1. 内盘检测:Flap
353
- // ========================================
354
- if (opts.flapChain) {
355
- try {
356
- const flap = new FlapPortal({ chain: opts.flapChain, rpcUrl: opts.rpcUrl });
357
- // ✅ 优先尝试 getTokenV7(包含 taxRate),失败则 fallback 到 getTokenV5
358
- let st = null;
359
- let taxRate;
360
- try {
361
- const stV7 = await flap.getTokenV7(token);
362
- st = stV7;
363
- // taxRate 是 bigint,需要转换为 number
364
- taxRate = typeof stV7.taxRate === 'bigint' ? Number(stV7.taxRate) : Number(stV7.taxRate ?? 0);
365
- if (opts.debug)
366
- console.log('[LP Inspect] Flap V7 taxRate:', taxRate);
367
- }
368
- catch {
369
- // V7 不支持,fallback 到 V5
370
- st = await flap.getTokenV5(token);
371
- if (opts.debug)
372
- console.log('[LP Inspect] Flap V7 failed, using V5');
373
- }
374
- if (st && st.status !== undefined && Number(st.status) !== 4) {
375
- result.platform = 'FLAP';
376
- let quoteDecimals = 18;
377
- let quoteSymbol = chainConfig.wrappedNativeSymbol;
378
- if (st.quoteTokenAddress && st.quoteTokenAddress.toLowerCase() !== ZERO_ADDRESS) {
379
- quoteDecimals = await getTokenDecimals(st.quoteTokenAddress, provider);
380
- // 查找符号
381
- const stableCoin = chainConfig.stableCoins.find(c => c.address.toLowerCase() === st.quoteTokenAddress.toLowerCase());
382
- quoteSymbol = stableCoin?.symbol || 'TOKEN';
383
- }
384
- result.flap = {
385
- quoteToken: st.quoteTokenAddress,
386
- quoteSymbol,
387
- quoteDecimals,
388
- reserveNative: formatBalance(st.reserve, shouldFormat, quoteDecimals),
389
- circulatingSupply: formatBalance(st.circulatingSupply, shouldFormat, tokenDecimals ?? 18),
390
- price: formatBalance(st.price, shouldFormat),
391
- taxRate, // ✅ 包含税率
392
- };
393
- // ✅ 同时设置顶层税率,方便统一读取
394
- if (taxRate && taxRate > 0) {
395
- result.taxRate = taxRate;
396
- }
397
- return result;
398
- }
399
- }
400
- catch (err) {
401
- if (opts.debug)
402
- console.log('[LP Inspect] Flap check failed:', err);
403
- }
404
- }
405
- // ========================================
406
- // 1b. 内盘检测:DAOaaS Portal(ENI 链)
407
- // ========================================
408
- if (opts.chain === 'ENI') {
409
- try {
410
- const portalAddress = ADDRESSES.ENI.DaoaasPortal;
411
- const portalAbi = [
412
- 'function getTokenState(address token) view returns (uint256)',
413
- 'function getPrice(address token) view returns (uint256)',
414
- 'function getOKAmountBySale(address token, uint256 tokenAmount) view returns (uint256)',
415
- ];
416
- const portal = new Contract(portalAddress, portalAbi, provider);
417
- const status = Number(await portal.getTokenState(token));
418
- // status: 0=不存在, 1=交易中(内盘), 2=已毕业(外盘)
419
- if (status === 1) {
420
- result.platform = 'DAOAAS';
421
- const price = await portal.getPrice(token);
422
- const tokenContract = new Contract(token, [
423
- 'function totalSupply() view returns (uint256)',
424
- 'function balanceOf(address) view returns (uint256)',
425
- ], provider);
426
- const [totalSupply, portalTokenBalance] = await Promise.all([
427
- tokenContract.totalSupply(),
428
- tokenContract.balanceOf(portalAddress),
429
- ]);
430
- const circulatingSupply = totalSupply - portalTokenBalance;
431
- let reserveNative = '0';
432
- try {
433
- if (circulatingSupply > 0n) {
434
- const saleValue = await portal.getOKAmountBySale(token, circulatingSupply);
435
- reserveNative = formatBalance(saleValue, shouldFormat, 18);
436
- }
437
- }
438
- catch {
439
- try {
440
- const egasBalance = await provider.getBalance(portalAddress);
441
- reserveNative = formatBalance(egasBalance, shouldFormat, 18);
442
- }
443
- catch { /* ignore */ }
444
- }
445
- result.daoaas = {
446
- quoteToken: ZERO_ADDRESS,
447
- quoteSymbol: 'EGAS',
448
- quoteDecimals: 18,
449
- reserveNative,
450
- circulatingSupply: formatBalance(circulatingSupply, shouldFormat, tokenDecimals ?? 18),
451
- price: formatEther(price),
452
- status,
453
- };
454
- return result;
455
- }
456
- // status === 2 (Graduated): 已毕业到 DSWAP V2,继续走外盘检测
457
- }
458
- catch (err) {
459
- if (opts.debug)
460
- console.log('[LP Inspect] DAOaaS Portal check failed:', err);
461
- }
462
- }
463
- // ========================================
464
- // 2. 内盘检测:Four.meme(仅 BSC)
465
- // ========================================
466
- if (opts.chain === 'BSC') {
467
- try {
468
- const helper = Helper3.connectByChain('BSC', opts.rpcUrl);
469
- const info = await helper.getTokenInfo(token);
470
- if (info) {
471
- const tokenManager = info.tokenManager;
472
- const versionNum = Number(info.version ?? 0);
473
- const liquidityAdded = Boolean(info.liquidityAdded);
474
- const isValid = tokenManager && tokenManager !== ZERO_ADDRESS && (versionNum === 1 || versionNum === 2);
475
- if (isValid && !liquidityAdded) {
476
- result.platform = 'FOUR';
477
- const reserveNative = info.funds !== undefined ? formatBalance(info.funds, shouldFormat) : undefined;
478
- const offerTokens = info.offers !== undefined ? formatBalance(info.offers, shouldFormat) : undefined;
479
- const lastPrice = info.lastPrice !== undefined ? formatBalance(info.lastPrice, shouldFormat) : undefined;
480
- result.four = { helper: ADDRESSES.BSC.TokenManagerHelper3, reserveNative, offerTokens, lastPrice };
481
- return result;
482
- }
483
- }
484
- }
485
- catch (err) {
486
- if (opts.debug)
487
- console.log('[LP Inspect] Four.meme check failed:', err);
488
- }
489
- }
490
- // ========================================
491
- // 3. 外盘检测:多 DEX 批量查询
492
- // ========================================
493
- const enabledDexes = Object.entries(chainConfig.dexes).filter(([_, dex]) => dex.enabled);
494
- // 构建报价代币列表
495
- const quoteTokens = [
496
- { address: chainConfig.wrappedNative, symbol: chainConfig.wrappedNativeSymbol, decimals: 18 },
497
- ...chainConfig.stableCoins,
498
- ];
499
- // 所有最佳池子候选
500
- const allBestPoolCandidates = [];
501
- // 并行查询所有 DEX
502
- const dexPromises = enabledDexes.map(async ([dexKey, dexConfig]) => {
503
- const dexPoolInfo = {
504
- dexName: dexConfig.name,
505
- dexKey,
506
- v2Pairs: [],
507
- v3Pools: [],
508
- };
509
- // V2 查询
510
- if (dexConfig.v2Factory) {
511
- try {
512
- const v2Pairs = await queryV2Pairs(token, dexConfig.v2Factory, quoteTokens, provider, multicall3, tokenDecimals ?? 18, shouldFormat, opts.debug);
513
- dexPoolInfo.v2Pairs = v2Pairs;
514
- // 添加到最佳池子候选
515
- for (const pair of v2Pairs) {
516
- if (pair.reserveQuoteRaw && pair.reserveQuoteRaw > 0n) {
517
- allBestPoolCandidates.push({
518
- dex: dexConfig.name,
519
- dexKey,
520
- version: 'v2',
521
- quoteToken: pair.quoteToken,
522
- quoteSymbol: pair.quoteSymbol,
523
- quoteDecimals: pair.quoteDecimals,
524
- pairAddress: pair.pairAddress,
525
- liquidity: pair.reserveQuote,
526
- liquidityRaw: normalizeLiquidity(pair.reserveQuoteRaw, pair.quoteDecimals),
527
- reserveToken: pair.reserveToken, // ✅ 新增
528
- reserveTokenRaw: pair.reserveTokenRaw, // ✅ 新增
529
- });
530
- }
531
- }
532
- }
533
- catch (err) {
534
- if (opts.debug)
535
- console.log(`[LP Inspect] ${dexConfig.name} V2 query failed:`, err);
536
- }
537
- }
538
- // V3 查询
539
- if (dexConfig.v3Factory) {
540
- try {
541
- const v3Pools = await queryV3Pools(token, dexConfig.v3Factory, quoteTokens, v3FeeTiers, provider, multicall3, tokenDecimals ?? 18, shouldFormat, opts.debug);
542
- dexPoolInfo.v3Pools = v3Pools;
543
- // 添加到最佳池子候选
544
- for (const pool of v3Pools) {
545
- if (pool.reserveQuoteRaw && pool.reserveQuoteRaw > 0n) {
546
- allBestPoolCandidates.push({
547
- dex: dexConfig.name,
548
- dexKey,
549
- version: 'v3',
550
- fee: pool.fee,
551
- quoteToken: pool.quoteToken,
552
- quoteSymbol: pool.quoteSymbol,
553
- quoteDecimals: pool.quoteDecimals,
554
- pairAddress: pool.pairAddress,
555
- liquidity: pool.reserveQuote,
556
- liquidityRaw: normalizeLiquidity(pool.reserveQuoteRaw, pool.quoteDecimals),
557
- reserveToken: pool.reserveToken, // ✅ 新增
558
- reserveTokenRaw: pool.reserveTokenRaw, // ✅ 新增
559
- });
560
- }
561
- }
562
- }
563
- catch (err) {
564
- if (opts.debug)
565
- console.log(`[LP Inspect] ${dexConfig.name} V3 query failed:`, err);
566
- }
567
- }
568
- return dexPoolInfo;
569
- });
570
- // 等待所有 DEX 查询完成
571
- const dexResults = await Promise.all(dexPromises);
572
- // 过滤出有流动性的 DEX
573
- result.allPools = dexResults.filter(dex => dex.v2Pairs.length > 0 || dex.v3Pools.length > 0);
574
- // 按流动性排序最佳池子,并计算 poolRatio
575
- result.bestPools = allBestPoolCandidates
576
- .sort((a, b) => {
577
- // 降序排列
578
- if (b.liquidityRaw > a.liquidityRaw)
579
- return 1;
580
- if (b.liquidityRaw < a.liquidityRaw)
581
- return -1;
582
- return 0;
583
- })
584
- .slice(0, 10) // 最多返回 10 个
585
- .map(pool => {
586
- // ✅ 计算池子代币占比
587
- if (pool.reserveTokenRaw && totalSupplyRaw && totalSupplyRaw > 0n) {
588
- // 计算百分比:(reserveTokenRaw / totalSupplyRaw) * 100
589
- // 为了保持精度,先乘以 10000,再除以 totalSupply,得到万分比
590
- const ratioBps = (pool.reserveTokenRaw * 10000n) / totalSupplyRaw;
591
- const ratioPercent = Number(ratioBps) / 100; // 转为百分比
592
- pool.poolRatio = `${ratioPercent.toFixed(2)}%`;
593
- }
594
- return pool;
595
- });
596
- // ✅ 同时为 allPools 中的每个池子计算 poolRatio
597
- if (totalSupplyRaw && totalSupplyRaw > 0n) {
598
- for (const dexPool of result.allPools) {
599
- for (const pair of dexPool.v2Pairs) {
600
- if (pair.reserveTokenRaw) {
601
- const ratioBps = (pair.reserveTokenRaw * 10000n) / totalSupplyRaw;
602
- const ratioPercent = Number(ratioBps) / 100;
603
- pair.poolRatio = `${ratioPercent.toFixed(2)}%`;
604
- }
605
- }
606
- for (const pool of dexPool.v3Pools) {
607
- if (pool.reserveTokenRaw) {
608
- const ratioBps = (pool.reserveTokenRaw * 10000n) / totalSupplyRaw;
609
- const ratioPercent = Number(ratioBps) / 100;
610
- pool.poolRatio = `${ratioPercent.toFixed(2)}%`;
611
- }
612
- }
613
- }
614
- }
615
- // 确定平台类型
616
- result.platform = determinePlatform(result.allPools);
617
- // ========================================
618
- // 4. 外盘税率查询(仅 Flap 链)
619
- // ========================================
620
- // 对于已毕业到外盘的代币,尝试从 Flap Portal 获取税率
621
- if (opts.flapChain && !result.flap && result.platform !== 'UNKNOWN') {
622
- try {
623
- const flap = new FlapPortal({ chain: opts.flapChain, rpcUrl: opts.rpcUrl });
624
- const stV7 = await flap.getTokenV7(token);
625
- // taxRate 是 bigint,需要转换为 number
626
- const taxRate = typeof stV7.taxRate === 'bigint' ? Number(stV7.taxRate) : Number(stV7.taxRate ?? 0);
627
- if (taxRate > 0) {
628
- result.taxRate = taxRate;
629
- if (opts.debug)
630
- console.log('[LP Inspect] 外盘税率:', taxRate, 'bps');
631
- }
632
- }
633
- catch (err) {
634
- // V7 不支持或代币不是 Flap 创建的,忽略
635
- if (opts.debug)
636
- console.log('[LP Inspect] 外盘税率查询失败(可忽略):', err);
637
- }
638
- }
639
- return result;
640
- }
641
- // ============================================================================
642
- // V2 批量查询
643
- // ============================================================================
644
- async function queryV2Pairs(token, factoryAddress, quoteTokens, provider, multicall3, tokenDecimals, shouldFormat, debug) {
645
- const results = [];
646
- const factoryIface = new Interface(I_UNIV2_FACTORY_ABI);
647
- const pairIface = new Interface(I_UNIV2_PAIR_ABI);
648
- const mcIface = new Interface(MULTICALL3_ABI);
649
- // 第一步:批量查询所有 getPair
650
- const pairCalls = quoteTokens.map(qt => ({
651
- target: factoryAddress,
652
- callData: factoryIface.encodeFunctionData('getPair', [token, qt.address]),
653
- quoteToken: qt,
654
- }));
655
- try {
656
- const aggData = mcIface.encodeFunctionData('aggregate', [
657
- pairCalls.map(c => ({ target: c.target, callData: c.callData }))
658
- ]);
659
- const res = await provider.call({ to: multicall3, data: aggData });
660
- const [, returnData] = mcIface.decodeFunctionResult('aggregate', res);
661
- // 解析 pair 地址
662
- const validPairs = [];
663
- for (let i = 0; i < pairCalls.length; i++) {
664
- try {
665
- const decoded = factoryIface.decodeFunctionResult('getPair', returnData[i]);
666
- const pairAddress = decoded[0];
667
- if (pairAddress && pairAddress.toLowerCase() !== ZERO_ADDRESS) {
668
- validPairs.push({ pairAddress, quoteToken: pairCalls[i].quoteToken });
669
- }
670
- }
671
- catch {
672
- // 解码失败,跳过
673
- }
674
- }
675
- if (validPairs.length === 0)
676
- return results;
677
- // 第二步:批量查询 getReserves
678
- const reserveCalls = validPairs.flatMap(p => [
679
- { target: p.pairAddress, callData: pairIface.encodeFunctionData('token0', []) },
680
- { target: p.pairAddress, callData: pairIface.encodeFunctionData('token1', []) },
681
- { target: p.pairAddress, callData: pairIface.encodeFunctionData('getReserves', []) },
682
- ]);
683
- const aggData2 = mcIface.encodeFunctionData('aggregate', [reserveCalls]);
684
- const res2 = await provider.call({ to: multicall3, data: aggData2 });
685
- const [, returnData2] = mcIface.decodeFunctionResult('aggregate', res2);
686
- // 解析储备量
687
- for (let i = 0; i < validPairs.length; i++) {
688
- try {
689
- const idx = i * 3;
690
- const token0 = pairIface.decodeFunctionResult('token0', returnData2[idx])[0];
691
- const token1 = pairIface.decodeFunctionResult('token1', returnData2[idx + 1])[0];
692
- const [r0, r1] = pairIface.decodeFunctionResult('getReserves', returnData2[idx + 2]);
693
- const p = validPairs[i];
694
- const isToken0 = token0.toLowerCase() === token.toLowerCase();
695
- const reserveToken = isToken0 ? r0 : r1;
696
- const reserveQuote = isToken0 ? r1 : r0;
697
- results.push({
698
- quoteToken: p.quoteToken.address,
699
- quoteSymbol: p.quoteToken.symbol,
700
- quoteDecimals: p.quoteToken.decimals,
701
- pairAddress: p.pairAddress,
702
- reserveToken: formatBalance(reserveToken, shouldFormat, tokenDecimals),
703
- reserveQuote: formatBalance(reserveQuote, shouldFormat, p.quoteToken.decimals),
704
- reserveQuoteRaw: reserveQuote,
705
- reserveTokenRaw: reserveToken, // ✅ 新增
706
- });
707
- }
708
- catch (err) {
709
- if (debug)
710
- console.log('[V2] Reserve decode error:', err);
711
- }
712
- }
713
- }
714
- catch (err) {
715
- if (debug)
716
- console.log('[V2] Multicall failed:', err);
717
- // Fallback: 逐个查询
718
- return queryV2PairsFallback(token, factoryAddress, quoteTokens, provider, tokenDecimals, shouldFormat);
719
- }
720
- return results;
721
- }
722
- async function queryV2PairsFallback(token, factoryAddress, quoteTokens, provider, tokenDecimals, shouldFormat) {
723
- const factory = new Contract(factoryAddress, I_UNIV2_FACTORY_ABI, provider);
724
- // ✅ 并行查询所有 quoteToken 的交易对
725
- const pairResults = await Promise.all(quoteTokens.map(async (qt) => {
726
- try {
727
- const pairAddress = await factory.getPair(token, qt.address);
728
- if (!pairAddress || pairAddress.toLowerCase() === ZERO_ADDRESS) {
729
- return null;
730
- }
731
- const pair = new Contract(pairAddress, I_UNIV2_PAIR_ABI, provider);
732
- // ✅ 并行获取 token0、token1 和 reserves
733
- const [token0, token1, reserves] = await Promise.all([
734
- pair.token0(),
735
- pair.token1(),
736
- pair.getReserves()
737
- ]);
738
- const [r0, r1] = reserves;
739
- const isToken0 = token0.toLowerCase() === token.toLowerCase();
740
- const reserveToken = isToken0 ? r0 : r1;
741
- const reserveQuote = isToken0 ? r1 : r0;
742
- return {
743
- quoteToken: qt.address,
744
- quoteSymbol: qt.symbol,
745
- quoteDecimals: qt.decimals,
746
- pairAddress,
747
- reserveToken: formatBalance(reserveToken, shouldFormat, tokenDecimals),
748
- reserveQuote: formatBalance(reserveQuote, shouldFormat, qt.decimals),
749
- reserveQuoteRaw: reserveQuote,
750
- reserveTokenRaw: reserveToken,
751
- };
752
- }
753
- catch {
754
- return null;
755
- }
756
- }));
757
- // 过滤掉 null 结果
758
- return pairResults.filter((r) => r !== null);
759
- }
760
- // ============================================================================
761
- // V3 批量查询
762
- // ============================================================================
763
- async function queryV3Pools(token, factoryAddress, quoteTokens, feeTiers, provider, multicall3, tokenDecimals, shouldFormat, debug) {
764
- const results = [];
765
- const factoryIface = new Interface(I_UNIV3_FACTORY_ABI);
766
- const erc20Iface = new Interface(I_ERC20_ABI);
767
- const mcIface = new Interface(MULTICALL3_ABI);
768
- // 构建所有查询组合
769
- const poolCalls = [];
770
- for (const qt of quoteTokens) {
771
- for (const fee of feeTiers) {
772
- // V3 要求 token0 < token1
773
- const tokenLower = token.toLowerCase();
774
- const quoteLower = qt.address.toLowerCase();
775
- const [token0, token1] = tokenLower < quoteLower
776
- ? [tokenLower, quoteLower]
777
- : [quoteLower, tokenLower];
778
- poolCalls.push({
779
- target: factoryAddress,
780
- callData: factoryIface.encodeFunctionData('getPool', [token0, token1, fee]),
781
- quoteToken: qt,
782
- fee,
783
- });
784
- }
785
- }
786
- try {
787
- // 第一步:批量查询所有 getPool
788
- const aggData = mcIface.encodeFunctionData('aggregate', [
789
- poolCalls.map(c => ({ target: c.target, callData: c.callData }))
790
- ]);
791
- const res = await provider.call({ to: multicall3, data: aggData });
792
- const [, returnData] = mcIface.decodeFunctionResult('aggregate', res);
793
- // 解析有效的池子
794
- const validPools = [];
795
- for (let i = 0; i < poolCalls.length; i++) {
796
- try {
797
- const decoded = factoryIface.decodeFunctionResult('getPool', returnData[i]);
798
- const poolAddress = decoded[0];
799
- if (poolAddress && poolAddress.toLowerCase() !== ZERO_ADDRESS) {
800
- validPools.push({
801
- poolAddress,
802
- quoteToken: poolCalls[i].quoteToken,
803
- fee: poolCalls[i].fee,
804
- });
805
- }
806
- }
807
- catch {
808
- // 解码失败,跳过
809
- }
810
- }
811
- if (validPools.length === 0)
812
- return results;
813
- // 第二步:批量查询余额
814
- const balanceCalls = validPools.flatMap(p => [
815
- { target: token, callData: erc20Iface.encodeFunctionData('balanceOf', [p.poolAddress]) },
816
- { target: p.quoteToken.address, callData: erc20Iface.encodeFunctionData('balanceOf', [p.poolAddress]) },
817
- ]);
818
- const aggData2 = mcIface.encodeFunctionData('aggregate', [balanceCalls]);
819
- const res2 = await provider.call({ to: multicall3, data: aggData2 });
820
- const [, returnData2] = mcIface.decodeFunctionResult('aggregate', res2);
821
- // 解析余额
822
- for (let i = 0; i < validPools.length; i++) {
823
- try {
824
- const idx = i * 2;
825
- const tokenBalance = erc20Iface.decodeFunctionResult('balanceOf', returnData2[idx])[0];
826
- const quoteBalance = erc20Iface.decodeFunctionResult('balanceOf', returnData2[idx + 1])[0];
827
- const p = validPools[i];
828
- results.push({
829
- quoteToken: p.quoteToken.address,
830
- quoteSymbol: p.quoteToken.symbol,
831
- quoteDecimals: p.quoteToken.decimals,
832
- pairAddress: p.poolAddress,
833
- reserveToken: formatBalance(tokenBalance, shouldFormat, tokenDecimals),
834
- reserveQuote: formatBalance(quoteBalance, shouldFormat, p.quoteToken.decimals),
835
- reserveQuoteRaw: quoteBalance,
836
- reserveTokenRaw: tokenBalance, // ✅ 新增
837
- fee: p.fee,
838
- });
839
- }
840
- catch (err) {
841
- if (debug)
842
- console.log('[V3] Balance decode error:', err);
843
- }
844
- }
845
- }
846
- catch (err) {
847
- if (debug)
848
- console.log('[V3] Multicall failed:', err);
849
- // Fallback: 逐个查询
850
- return queryV3PoolsFallback(token, factoryAddress, quoteTokens, feeTiers, provider, tokenDecimals, shouldFormat);
851
- }
852
- return results;
853
- }
854
- async function queryV3PoolsFallback(token, factoryAddress, quoteTokens, feeTiers, provider, tokenDecimals, shouldFormat) {
855
- const factory = new Contract(factoryAddress, I_UNIV3_FACTORY_ABI, provider);
856
- const tokenContract = new Contract(token, I_ERC20_ABI, provider);
857
- // ✅ 构建所有查询组合
858
- const queries = [];
859
- for (const qt of quoteTokens) {
860
- const tokenLower = token.toLowerCase();
861
- const quoteLower = qt.address.toLowerCase();
862
- const [token0, token1] = tokenLower < quoteLower
863
- ? [tokenLower, quoteLower]
864
- : [quoteLower, tokenLower];
865
- for (const fee of feeTiers) {
866
- queries.push({ qt, fee, token0, token1 });
867
- }
868
- }
869
- // ✅ 并行查询所有池子
870
- const poolResults = await Promise.all(queries.map(async ({ qt, fee, token0, token1 }) => {
871
- try {
872
- const poolAddress = await factory.getPool(token0, token1, fee);
873
- if (!poolAddress || poolAddress.toLowerCase() === ZERO_ADDRESS) {
874
- return null;
875
- }
876
- const quoteContract = new Contract(qt.address, I_ERC20_ABI, provider);
877
- // ✅ 并行获取余额
878
- const [tokenBalance, quoteBalance] = await Promise.all([
879
- tokenContract.balanceOf(poolAddress),
880
- quoteContract.balanceOf(poolAddress),
881
- ]);
882
- return {
883
- quoteToken: qt.address,
884
- quoteSymbol: qt.symbol,
885
- quoteDecimals: qt.decimals,
886
- pairAddress: poolAddress,
887
- reserveToken: formatBalance(tokenBalance, shouldFormat, tokenDecimals),
888
- reserveQuote: formatBalance(quoteBalance, shouldFormat, qt.decimals),
889
- reserveQuoteRaw: quoteBalance,
890
- reserveTokenRaw: tokenBalance,
891
- fee,
892
- };
893
- }
894
- catch {
895
- return null;
896
- }
897
- }));
898
- // 过滤掉 null 结果
899
- return poolResults.filter((r) => r !== null);
900
- }
901
- // ============================================================================
902
- // 平台类型判断
903
- // ============================================================================
904
- function determinePlatform(allPools) {
905
- if (allPools.length === 0)
906
- return 'UNKNOWN';
907
- // 统计有流动性的 DEX
908
- const dexesWithLiquidity = allPools.filter(dex => dex.v2Pairs.length > 0 || dex.v3Pools.length > 0);
909
- if (dexesWithLiquidity.length > 1) {
910
- return 'MULTI_DEX';
911
- }
912
- if (dexesWithLiquidity.length === 1) {
913
- const dex = dexesWithLiquidity[0];
914
- const hasV2 = dex.v2Pairs.length > 0;
915
- const hasV3 = dex.v3Pools.length > 0;
916
- if (dex.dexKey === 'PANCAKESWAP') {
917
- if (hasV2 && hasV3)
918
- return 'MULTI_DEX';
919
- if (hasV3)
920
- return 'PANCAKESWAP_V3';
921
- return 'PANCAKESWAP_V2';
922
- }
923
- if (dex.dexKey === 'UNISWAP') {
924
- if (hasV2 && hasV3)
925
- return 'MULTI_DEX';
926
- if (hasV3)
927
- return 'UNISWAP_V3';
928
- return 'UNISWAP_V2';
929
- }
930
- if (dex.dexKey === 'IROSWAP') {
931
- return 'IROSWAP_V2';
932
- }
933
- // 其他 DEX
934
- return 'MULTI_DEX';
935
- }
936
- return 'UNKNOWN';
937
- }