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,645 +0,0 @@
1
- import { Contract, Wallet, JsonRpcProvider, Interface, parseUnits } from 'ethers';
2
- import { ADDRESSES, ZERO_ADDRESS, getMulticall3Address } from '../constants.js';
3
- import { NonceManager } from '../bundle-helpers.js';
4
- import { ERC20_ABI, MULTICALL3_ABI } from '../../shared/abis/common.js';
5
- // ============================================================================
6
- // ✅ Max approval(与 BSC 策略一致:阈值判断,避免频繁重复授权)
7
- // ============================================================================
8
- const MAX_UINT256 = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
9
- const MAX_APPROVAL_THRESHOLD = MAX_UINT256 / 2n;
10
- /**
11
- * 验证合约地址是否有效(是否部署了代码)
12
- */
13
- async function validateContractAddress(provider, address, label) {
14
- try {
15
- // ✅ 先规范化地址(转为 checksum 格式),避免 ethers v6 的严格校验报错
16
- const normalizedAddress = address.toLowerCase().startsWith('0x')
17
- ? address.toLowerCase()
18
- : `0x${address.toLowerCase()}`;
19
- const code = await provider.getCode(normalizedAddress);
20
- if (code === '0x' || code.length <= 2) {
21
- throw new Error(`❌ ${label} 地址无效或未部署合约: ${address}`);
22
- }
23
- }
24
- catch (error) {
25
- if (error.message.includes('无效或未部署')) {
26
- throw error;
27
- }
28
- // ✅ 如果是 checksum 错误,提供更友好的提示
29
- if (error.message.includes('bad address checksum') || error.code === 'INVALID_ARGUMENT') {
30
- throw new Error(`❌ ${label} 地址格式错误,请使用正确的 checksum 格式: ${address}`);
31
- }
32
- throw new Error(`❌ 无法验证 ${label} 地址 ${address}: ${error.message}`);
33
- }
34
- }
35
- /**
36
- * 通用 ERC20 授权方法(内部辅助函数)
37
- * 自动检查当前授权额度,只在不足时才发送交易
38
- */
39
- async function ensureAllowance(rpcUrl, privateKey, token, owner, spender, required) {
40
- const provider = new JsonRpcProvider(rpcUrl);
41
- const signer = new Wallet(privateKey, provider);
42
- // ✅ 验证 token 和 spender 地址
43
- await validateContractAddress(provider, token, 'Token');
44
- await validateContractAddress(provider, spender, 'Spender');
45
- const erc20 = new Contract(token, ERC20_ABI, signer);
46
- // ✅ 自动检查当前授权额度,只在不足时才发送交易
47
- let current;
48
- try {
49
- current = await erc20.allowance(owner, spender);
50
- }
51
- catch (error) {
52
- throw new Error(`❌ 调用 allowance 失败(Token 可能不是 ERC20): ${error.message}`);
53
- }
54
- if (current >= required) {
55
- return {
56
- alreadyApproved: true,
57
- currentAllowance: current,
58
- requiredAllowance: required,
59
- };
60
- }
61
- const tx = await erc20.approve(spender, required);
62
- const receipt = await tx.wait();
63
- return {
64
- alreadyApproved: false,
65
- currentAllowance: current,
66
- requiredAllowance: required,
67
- txReceipt: receipt,
68
- };
69
- }
70
- /**
71
- * 检查代币授权状态 - Four.meme V1(只读,不发送交易)
72
- * @returns 是否已授权足够的额度
73
- */
74
- export async function checkSellApprovalV1(chain, rpcUrl, token, owner, amount) {
75
- const proxyAddresses = {
76
- BSC: ADDRESSES.BSC.TokenManagerV1, // FourMeme 代理合约 V1 (BSC)
77
- BASE: ADDRESSES.BASE.TokenManagerHelper3, // FourMeme 代理合约 (BASE)
78
- ARBITRUM_ONE: ADDRESSES.ARBITRUM_ONE.TokenManagerHelper3, // FourMeme 代理合约 (ARBITRUM)
79
- };
80
- const provider = new JsonRpcProvider(rpcUrl);
81
- // ✅ 验证 token 和代理合约地址
82
- await validateContractAddress(provider, token, 'Token');
83
- await validateContractAddress(provider, proxyAddresses[chain], `Four.meme V1 Proxy (${chain})`);
84
- const erc20 = new Contract(token, ERC20_ABI, provider);
85
- let current;
86
- try {
87
- current = await erc20.allowance(owner, proxyAddresses[chain]);
88
- }
89
- catch (error) {
90
- throw new Error(`❌ 调用 allowance 失败(Token 可能不是 ERC20): ${error.message}`);
91
- }
92
- return {
93
- isApproved: current >= amount,
94
- currentAllowance: current,
95
- requiredAllowance: amount,
96
- };
97
- }
98
- /**
99
- * 确保代币已授权给 Four.meme V1 代理合约
100
- * 用于 Four.meme V1 代币的卖出操作
101
- * @returns 授权结果,包含是否已授权、当前额度、交易回执等信息
102
- */
103
- export async function ensureSellApprovalV1(chain, rpcUrl, privateKey, token, owner, amount) {
104
- // ✅ 授权给 FourMeme 代理合约 V1(收费版)
105
- const proxyAddresses = {
106
- BSC: ADDRESSES.BSC.TokenManagerV1, // FourMeme 代理合约 V1 (BSC)
107
- BASE: ADDRESSES.BASE.TokenManagerHelper3, // FourMeme 代理合约 (BASE)
108
- ARBITRUM_ONE: ADDRESSES.ARBITRUM_ONE.TokenManagerHelper3, // FourMeme 代理合约 (ARBITRUM)
109
- };
110
- return await ensureAllowance(rpcUrl, privateKey, token, owner, proxyAddresses[chain], amount);
111
- }
112
- /**
113
- * 检查代币授权状态 - Four.meme V2(只读,不发送交易)
114
- * @returns 是否已授权足够的额度
115
- */
116
- export async function checkSellApprovalV2(chain, rpcUrl, token, owner, amount) {
117
- const proxyAddresses = {
118
- BSC: ADDRESSES.BSC.TokenManagerV2, // FourMeme 代理合约 V2 (BSC)
119
- BASE: ADDRESSES.BASE.TokenManagerHelper3, // FourMeme 代理合约 (BASE)
120
- ARBITRUM_ONE: ADDRESSES.ARBITRUM_ONE.TokenManagerHelper3, // FourMeme 代理合约 (ARBITRUM)
121
- };
122
- const provider = new JsonRpcProvider(rpcUrl);
123
- // ✅ 验证 token 和代理合约地址
124
- await validateContractAddress(provider, token, 'Token');
125
- await validateContractAddress(provider, proxyAddresses[chain], `Four.meme V2 Proxy (${chain})`);
126
- const erc20 = new Contract(token, ERC20_ABI, provider);
127
- let current;
128
- try {
129
- current = await erc20.allowance(owner, proxyAddresses[chain]);
130
- }
131
- catch (error) {
132
- throw new Error(`❌ 调用 allowance 失败(Token 可能不是 ERC20): ${error.message}`);
133
- }
134
- return {
135
- isApproved: current >= amount,
136
- currentAllowance: current,
137
- requiredAllowance: amount,
138
- };
139
- }
140
- /**
141
- * 确保代币已授权给 Four.meme V2 代理合约
142
- * 用于 Four.meme V2 代币的卖出操作
143
- * @returns 授权结果,包含是否已授权、当前额度、交易回执等信息
144
- */
145
- export async function ensureSellApprovalV2(chain, rpcUrl, privateKey, token, owner, amount) {
146
- // ✅ 授权给 FourMeme 代理合约 V2(收费版)
147
- const proxyAddresses = {
148
- BSC: ADDRESSES.BSC.TokenManagerV2, // FourMeme 代理合约 V2 (BSC)
149
- BASE: ADDRESSES.BASE.TokenManagerHelper3, // FourMeme 代理合约 (BASE)
150
- ARBITRUM_ONE: ADDRESSES.ARBITRUM_ONE.TokenManagerHelper3, // FourMeme 代理合约 (ARBITRUM)
151
- };
152
- return await ensureAllowance(rpcUrl, privateKey, token, owner, proxyAddresses[chain], amount);
153
- }
154
- /**
155
- * 检查代币授权状态 - Four.meme(通用,默认使用 V2)
156
- * @deprecated 建议使用明确版本的方法:checkSellApprovalV1 或 checkSellApprovalV2
157
- */
158
- export async function checkSellApproval(chain, rpcUrl, token, owner, amount) {
159
- return checkSellApprovalV2(chain, rpcUrl, token, owner, amount);
160
- }
161
- /**
162
- * 确保代币已授权给 Four.meme 代理合约(通用,默认使用 V2)
163
- * @deprecated 建议使用明确版本的方法:ensureSellApprovalV1 或 ensureSellApprovalV2
164
- */
165
- export async function ensureSellApproval(chain, rpcUrl, privateKey, token, owner, amount) {
166
- return ensureSellApprovalV2(chain, rpcUrl, privateKey, token, owner, amount);
167
- }
168
- /**
169
- * 检查代币授权状态 - Flap Protocol(只读,不发送交易)
170
- * @returns 是否已授权足够的额度
171
- */
172
- export async function checkFlapSellApproval(chain, rpcUrl, token, owner, amount) {
173
- const proxyAddresses = {
174
- BSC: ADDRESSES.BSC.FlapPortal, // Flap Portal 代理合约 (BSC)
175
- BASE: ADDRESSES.BASE.FlapPortal, // Flap Portal 代理合约 (BASE)
176
- XLAYER: ADDRESSES.XLAYER.FlapPortal, // Flap Portal 代理合约 (XLAYER)
177
- MORPH: ADDRESSES.MORPH.FlapPortal, // Flap Portal 代理合约 (MORPH)
178
- MONAD: ADDRESSES.MONAD.FlapPortal, // Flap Portal 代理合约 (MONAD)
179
- };
180
- const provider = new JsonRpcProvider(rpcUrl);
181
- // ✅ 验证 token 和代理合约地址
182
- await validateContractAddress(provider, token, 'Token');
183
- await validateContractAddress(provider, proxyAddresses[chain], `Flap Portal (${chain})`);
184
- const erc20 = new Contract(token, ERC20_ABI, provider);
185
- let current;
186
- try {
187
- current = await erc20.allowance(owner, proxyAddresses[chain]);
188
- }
189
- catch (error) {
190
- throw new Error(`❌ 调用 allowance 失败(Token 可能不是 ERC20): ${error.message}`);
191
- }
192
- return {
193
- isApproved: current >= amount,
194
- currentAllowance: current,
195
- requiredAllowance: amount,
196
- };
197
- }
198
- /**
199
- * 确保代币已授权给 Flap Protocol 代理合约
200
- * 用于 Flap Protocol 代币的卖出操作
201
- * @returns 授权结果,包含是否已授权、当前额度、交易回执等信息
202
- */
203
- export async function ensureFlapSellApproval(chain, rpcUrl, privateKey, token, owner, amount) {
204
- // ✅ 授权给 Flap Portal 代理合约(收费版)
205
- const proxyAddresses = {
206
- BSC: ADDRESSES.BSC.FlapPortal, // Flap Portal 代理合约 (BSC)
207
- BASE: ADDRESSES.BASE.FlapPortal, // Flap Portal 代理合约 (BASE)
208
- XLAYER: ADDRESSES.XLAYER.FlapPortal, // Flap Portal 代理合约 (XLAYER)
209
- MORPH: ADDRESSES.MORPH.FlapPortal, // Flap Portal 代理合约 (MORPH)
210
- MONAD: ADDRESSES.MONAD.FlapPortal, // Flap Portal 代理合约 (MONAD)
211
- };
212
- return await ensureAllowance(rpcUrl, privateKey, token, owner, proxyAddresses[chain], amount);
213
- }
214
- /**
215
- * 批量确保代币已授权给 Flap Protocol 代理合约(默认授权上限 2^256-1)
216
- * @param privateKeys 拥有者私钥数组(每个地址需自行签名)
217
- * @returns 每个地址的授权结果(包含 owner 与交易回执等信息)
218
- */
219
- export async function ensureFlapSellApprovalBatch(chain, rpcUrl, privateKeys, token, required = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')) {
220
- if (!privateKeys || privateKeys.length === 0)
221
- return [];
222
- // ✅ 并行处理所有授权
223
- const results = await Promise.all(privateKeys.map(async (pk) => {
224
- const owner = new Wallet(pk).address;
225
- const r = await ensureFlapSellApproval(chain, rpcUrl, pk, token, owner, required);
226
- return { owner, ...r };
227
- }));
228
- return results;
229
- }
230
- /**
231
- * 批量检查 Flap Protocol 授权状态(默认按上限 2^256-1 判断)
232
- * ✅ 使用 Multicall3 批量查询,减少 RPC 调用次数
233
- */
234
- export async function checkFlapSellApprovalBatch(chain, rpcUrl, token, owners, required = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')) {
235
- if (!owners || owners.length === 0)
236
- return [];
237
- const proxyAddresses = {
238
- BSC: ADDRESSES.BSC.FlapPortal,
239
- BASE: ADDRESSES.BASE.FlapPortal,
240
- XLAYER: ADDRESSES.XLAYER.FlapPortal,
241
- MORPH: ADDRESSES.MORPH.FlapPortal,
242
- MONAD: ADDRESSES.MONAD.FlapPortal,
243
- };
244
- const provider = new JsonRpcProvider(rpcUrl);
245
- // ✅ 并行验证 token 和代理合约地址
246
- await Promise.all([
247
- validateContractAddress(provider, token, 'Token'),
248
- validateContractAddress(provider, proxyAddresses[chain], `Flap Portal (${chain})`)
249
- ]);
250
- // ✅ 使用 batchCheckAllowances 批量查询(Multicall3)
251
- const allowances = await batchCheckAllowances(provider, token, owners, proxyAddresses[chain]);
252
- return owners.map((owner, i) => ({
253
- owner,
254
- isApproved: allowances[i] >= required,
255
- currentAllowance: allowances[i],
256
- requiredAllowance: required
257
- }));
258
- }
259
- /**
260
- * 使用 Multicall3 批量查询 ERC20 授权额度
261
- * @param provider - Provider 实例
262
- * @param tokenAddress - ERC20 代币地址
263
- * @param owners - 所有者地址数组
264
- * @param spender - 被授权的 spender 地址
265
- * @returns 每个地址的授权额度数组
266
- */
267
- export async function batchCheckAllowances(provider, tokenAddress, owners, spender) {
268
- const network = await provider.getNetwork();
269
- const multicall3Address = getMulticall3Address(Number(network.chainId));
270
- const multicall3 = new Contract(multicall3Address, MULTICALL3_ABI, provider);
271
- // 编码 allowance(owner, spender) 调用数据
272
- const erc20Interface = new Contract(tokenAddress, ERC20_ABI, provider).interface;
273
- const calls = owners.map(owner => ({
274
- target: tokenAddress,
275
- allowFailure: true,
276
- callData: erc20Interface.encodeFunctionData('allowance', [owner, spender])
277
- }));
278
- // 批量调用
279
- const results = await multicall3.aggregate3(calls);
280
- // 解析结果
281
- return results.map((result, index) => {
282
- if (!result.success) {
283
- return 0n;
284
- }
285
- try {
286
- const decoded = erc20Interface.decodeFunctionResult('allowance', result.returnData);
287
- return decoded[0];
288
- }
289
- catch (error) {
290
- return 0n;
291
- }
292
- });
293
- }
294
- /**
295
- * 🔧 内部辅助函数:根据链和平台自动解析 spender 地址
296
- */
297
- function resolveSpenderAddress(chain, platform) {
298
- const spenderMap = {
299
- flap: {
300
- BSC: ADDRESSES.BSC.FlapPortal,
301
- BASE: ADDRESSES.BASE.FlapPortal,
302
- XLAYER: ADDRESSES.XLAYER.FlapPortal,
303
- MORPH: ADDRESSES.MORPH.FlapPortal,
304
- MONAD: ADDRESSES.MONAD.FlapPortal,
305
- ENI: ZERO_ADDRESS,
306
- },
307
- four: {
308
- BSC: ADDRESSES.BSC.TokenManagerOriginal,
309
- BASE: ADDRESSES.BASE.TokenManagerHelper3,
310
- XLAYER: ZERO_ADDRESS,
311
- MORPH: ZERO_ADDRESS,
312
- MONAD: ZERO_ADDRESS,
313
- ENI: ZERO_ADDRESS,
314
- },
315
- 'pancake-v2': {
316
- BSC: ADDRESSES.BSC.PancakeV2Router,
317
- BASE: '0x8cFe327CEc66d1C090Dd72bd0FF11d690C33a2Eb',
318
- XLAYER: ZERO_ADDRESS,
319
- MORPH: ZERO_ADDRESS,
320
- MONAD: '0xb1bc24c34e88f7d43d5923034e3a14b24daacff9',
321
- ENI: ZERO_ADDRESS,
322
- },
323
- 'pancake-v3': {
324
- BSC: ADDRESSES.BSC.PancakeV3Router,
325
- BASE: '0x678Aa4bF4E210cf2166753e054d5b7c31cc7fa86',
326
- XLAYER: ZERO_ADDRESS,
327
- MORPH: ZERO_ADDRESS,
328
- MONAD: '0x1b81d678ffb9c0263b24a97847620c99d213eb14',
329
- ENI: ZERO_ADDRESS,
330
- },
331
- daoaas: {
332
- BSC: ZERO_ADDRESS,
333
- BASE: ZERO_ADDRESS,
334
- XLAYER: ZERO_ADDRESS,
335
- MORPH: ZERO_ADDRESS,
336
- MONAD: ZERO_ADDRESS,
337
- ENI: ADDRESSES.ENI.DaoaasPortal,
338
- },
339
- 'dswap-v2': {
340
- BSC: ZERO_ADDRESS,
341
- BASE: ZERO_ADDRESS,
342
- XLAYER: ZERO_ADDRESS,
343
- MORPH: ZERO_ADDRESS,
344
- MONAD: ZERO_ADDRESS,
345
- ENI: ADDRESSES.ENI.DswapV2Router,
346
- },
347
- };
348
- const spender = spenderMap[platform]?.[chain];
349
- if (!spender || spender === ZERO_ADDRESS) {
350
- throw new Error(`❌ 不支持的链或平台: ${chain} / ${platform}`);
351
- }
352
- return spender;
353
- }
354
- /**
355
- * ✅ 智能路由:检查单个 ERC20 授权额度(自动选择 spender)
356
- *
357
- * @param chain - 链名称 ('BSC' | 'BASE' | 'XLAYER' | 'MORPH' | 'MONAD')
358
- * @param platform - 平台名称 ('flap' | 'four' | 'pancake-v2' | 'pancake-v3')
359
- * @param rpcUrl - RPC 节点地址
360
- * @param tokenAddress - 代币合约地址
361
- * @param ownerAddress - 代币持有者地址
362
- * @returns 当前授权额度(bigint)
363
- */
364
- export async function checkAllowance(chain, platform, rpcUrl, tokenAddress, ownerAddress) {
365
- const spenderAddress = resolveSpenderAddress(chain, platform);
366
- return checkAllowanceRaw(rpcUrl, tokenAddress, ownerAddress, spenderAddress);
367
- }
368
- /**
369
- * ✅ 底层方法:检查 ERC20 代币授权额度(手动指定 spender)
370
- * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
371
- *
372
- * @param rpcUrl - RPC 节点地址
373
- * @param tokenAddress - 代币合约地址
374
- * @param ownerAddress - 代币持有者地址
375
- * @param spenderAddress - 被授权的合约地址(如 Router、Portal、TokenManager 等)
376
- * @returns 当前授权额度(bigint)
377
- */
378
- export async function checkAllowanceRaw(rpcUrl, tokenAddress, ownerAddress, spenderAddress) {
379
- const provider = new JsonRpcProvider(rpcUrl);
380
- // ✅ 规范化地址(转为小写,避免 checksum 错误)
381
- const normalizedToken = tokenAddress.toLowerCase();
382
- const normalizedOwner = ownerAddress.toLowerCase();
383
- const normalizedSpender = spenderAddress.toLowerCase();
384
- // 验证地址
385
- await validateContractAddress(provider, normalizedToken, 'Token');
386
- await validateContractAddress(provider, normalizedSpender, 'Spender');
387
- const erc20 = new Contract(normalizedToken, ERC20_ABI, provider);
388
- try {
389
- const allowance = await erc20.allowance(normalizedOwner, normalizedSpender);
390
- return allowance;
391
- }
392
- catch (error) {
393
- throw new Error(`❌ 查询授权额度失败: ${error.message}`);
394
- }
395
- }
396
- /**
397
- * ✅ 智能路由:授权 ERC20 代币(自动选择 spender,自动检查,只在不足时才发送交易)
398
- *
399
- * @param chain - 链名称 ('BSC' | 'BASE' | 'XLAYER' | 'MORPH' | 'MONAD')
400
- * @param platform - 平台名称 ('flap' | 'four' | 'pancake-v2' | 'pancake-v3')
401
- * @param rpcUrl - RPC 节点地址
402
- * @param privateKey - 私钥
403
- * @param tokenAddress - 代币合约地址
404
- * @param amount - 授权数量(bigint),传 'max' 表示最大授权
405
- * @returns 授权结果
406
- */
407
- export async function approveToken(chain, platform, rpcUrl, privateKey, tokenAddress, amount) {
408
- const spenderAddress = resolveSpenderAddress(chain, platform);
409
- return approveTokenRaw(rpcUrl, privateKey, tokenAddress, spenderAddress, amount);
410
- }
411
- /**
412
- * ✅ 底层方法:授权 ERC20 代币给指定合约(手动指定 spender)
413
- * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
414
- *
415
- * @param rpcUrl - RPC 节点地址
416
- * @param privateKey - 私钥
417
- * @param tokenAddress - 代币合约地址
418
- * @param spenderAddress - 被授权的合约地址(如 Router、Portal、TokenManager 等)
419
- * @param amount - 授权数量(bigint),传 'max' 或 ethers.MaxUint256 表示最大授权
420
- * @returns 授权结果
421
- */
422
- export async function approveTokenRaw(rpcUrl, privateKey, tokenAddress, spenderAddress, amount) {
423
- const provider = new JsonRpcProvider(rpcUrl);
424
- const signer = new Wallet(privateKey, provider);
425
- const ownerAddress = signer.address;
426
- // ✅ 规范化地址(转为小写,避免 checksum 错误)
427
- const normalizedToken = tokenAddress.toLowerCase();
428
- const normalizedSpender = spenderAddress.toLowerCase();
429
- // 验证地址
430
- await validateContractAddress(provider, normalizedToken, 'Token');
431
- await validateContractAddress(provider, normalizedSpender, 'Spender');
432
- const erc20 = new Contract(normalizedToken, ERC20_ABI, signer);
433
- const isMax = amount === 'max';
434
- const requiredAmount = isMax ? MAX_UINT256 : amount;
435
- // 检查当前授权额度
436
- let currentAllowance;
437
- try {
438
- currentAllowance = await erc20.allowance(ownerAddress, normalizedSpender);
439
- }
440
- catch (error) {
441
- throw new Error(`❌ 查询授权额度失败: ${error.message}`);
442
- }
443
- // 如果已经授权足够,直接返回
444
- // ✅ max 授权:使用阈值判断,避免每次消耗一点 allowance 都重新授权
445
- if (isMax ? (currentAllowance >= MAX_APPROVAL_THRESHOLD) : (currentAllowance >= requiredAmount)) {
446
- return {
447
- alreadyApproved: true,
448
- currentAllowance,
449
- requiredAllowance: requiredAmount
450
- };
451
- }
452
- // 发送授权交易
453
- try {
454
- const tx = await erc20.approve(normalizedSpender, requiredAmount);
455
- const receipt = await tx.wait();
456
- return {
457
- alreadyApproved: false,
458
- currentAllowance,
459
- requiredAllowance: requiredAmount,
460
- txReceipt: receipt
461
- };
462
- }
463
- catch (error) {
464
- throw new Error(`❌ 授权交易失败: ${error.message}`);
465
- }
466
- }
467
- /**
468
- * ✅ 智能路由:批量检查多个钱包的授权额度(自动选择 spender)
469
- *
470
- * @param chain - 链名称 ('BSC' | 'BASE' | 'XLAYER' | 'MORPH' | 'MONAD')
471
- * @param platform - 平台名称 ('flap' | 'four' | 'pancake-v2' | 'pancake-v3')
472
- * @param rpcUrl - RPC 节点地址
473
- * @param tokenAddress - 代币合约地址
474
- * @param ownerAddresses - 代币持有者地址数组
475
- * @returns 每个地址的授权额度数组
476
- */
477
- export async function checkAllowanceBatch(chain, platform, rpcUrl, tokenAddress, ownerAddresses) {
478
- const spenderAddress = resolveSpenderAddress(chain, platform);
479
- return checkAllowanceBatchRaw(rpcUrl, tokenAddress, ownerAddresses, spenderAddress);
480
- }
481
- /**
482
- * ✅ 底层方法:批量检查多个钱包的授权额度(手动指定 spender)
483
- * 适用于任意 spender 地址(Flap、Four、PancakeSwap V2/V3 等)
484
- *
485
- * @param rpcUrl - RPC 节点地址
486
- * @param tokenAddress - 代币合约地址
487
- * @param ownerAddresses - 代币持有者地址数组
488
- * @param spenderAddress - 被授权的合约地址
489
- * @returns 每个地址的授权额度数组
490
- */
491
- export async function checkAllowanceBatchRaw(rpcUrl, tokenAddress, ownerAddresses, spenderAddress) {
492
- const provider = new JsonRpcProvider(rpcUrl);
493
- // ✅ 规范化地址(转为小写,避免 checksum 错误)
494
- const normalizedToken = tokenAddress.toLowerCase();
495
- const normalizedOwners = ownerAddresses.map(addr => addr.toLowerCase());
496
- const normalizedSpender = spenderAddress.toLowerCase();
497
- // 验证地址
498
- await validateContractAddress(provider, normalizedToken, 'Token');
499
- await validateContractAddress(provider, normalizedSpender, 'Spender');
500
- return batchCheckAllowances(provider, normalizedToken, normalizedOwners, normalizedSpender);
501
- }
502
- export async function approveTokenBatch(params) {
503
- const { chain, platform, rpcUrl, privateKeys, tokenAddress, amounts, signOnly, gasPriceGwei, gasLimit, chainId, skipValidation } = params;
504
- const spenderAddress = resolveSpenderAddress(chain, platform);
505
- return approveTokenBatchRaw({ rpcUrl, privateKeys, tokenAddress, spenderAddress, amounts, signOnly, gasPriceGwei, gasLimit, chainId, skipValidation });
506
- }
507
- export async function approveTokenBatchRaw(params) {
508
- const { rpcUrl, privateKeys, tokenAddress, spenderAddress, amounts, signOnly, gasPriceGwei, gasLimit, chainId = 56, nonceManager: externalNonceManager, skipValidation } = params;
509
- if (privateKeys.length === 0 || amounts.length !== privateKeys.length) {
510
- throw new Error('❌ 私钥数量和授权数量必须匹配');
511
- }
512
- const provider = new JsonRpcProvider(rpcUrl);
513
- // ✅ 规范化地址(转为小写,避免 checksum 错误)
514
- const normalizedToken = tokenAddress.toLowerCase();
515
- const normalizedSpender = spenderAddress.toLowerCase();
516
- // 验证地址(如果 skipValidation=true 则跳过)
517
- if (!skipValidation) {
518
- await validateContractAddress(provider, normalizedToken, 'Token');
519
- await validateContractAddress(provider, normalizedSpender, 'Spender');
520
- }
521
- // ✅ 优化:批量创建钱包和合约实例
522
- const wallets = privateKeys.map(key => new Wallet(key, provider));
523
- const ownerAddresses = wallets.map(w => w.address);
524
- const isMaxApprovals = amounts.map(a => a === 'max');
525
- const requiredAmounts = amounts.map(amount => amount === 'max'
526
- ? MAX_UINT256
527
- : amount);
528
- // ==================== signOnly=true:只签名不提交 ====================
529
- if (signOnly) {
530
- // ✅ 使用 NonceManager 管理 nonce(和买卖交易一样)
531
- const nonceManager = externalNonceManager || new NonceManager(provider);
532
- // ✅ 并行获取:当前授权额度 + nonces + gasPrice
533
- const [currentAllowances, nonces, fetchedGasPrice] = await Promise.all([
534
- batchCheckAllowances(provider, normalizedToken, ownerAddresses, normalizedSpender),
535
- nonceManager.getNextNoncesForWallets(wallets), // ✅ 使用 NonceManager 批量获取 nonce
536
- gasPriceGwei ? Promise.resolve(parseUnits(gasPriceGwei.toString(), 'gwei')) : provider.getFeeData().then(fee => fee.gasPrice || parseUnits('3', 'gwei'))
537
- ]);
538
- const finalGasPrice = fetchedGasPrice;
539
- const finalGasLimit = BigInt(gasLimit || 100000);
540
- // ✅ ERC20 approve 函数的 ABI 编码
541
- const erc20Interface = new Interface(ERC20_ABI);
542
- // ✅ 并行签名所有需要授权的交易
543
- const signPromises = wallets.map(async (wallet, i) => {
544
- const ownerAddress = ownerAddresses[i];
545
- const currentAllowance = currentAllowances[i];
546
- const requiredAmount = requiredAmounts[i];
547
- const isMax = isMaxApprovals[i];
548
- // 如果已经授权足够,跳过
549
- // ✅ max 授权:使用阈值判断(与 BSC 一致)
550
- if (isMax ? (currentAllowance >= MAX_APPROVAL_THRESHOLD) : (currentAllowance >= requiredAmount)) {
551
- return {
552
- owner: ownerAddress,
553
- alreadyApproved: true,
554
- currentAllowance,
555
- requiredAllowance: requiredAmount,
556
- signedTx: undefined
557
- };
558
- }
559
- // 构建并签名交易
560
- const txData = erc20Interface.encodeFunctionData('approve', [normalizedSpender, requiredAmount]);
561
- const signedTx = await wallet.signTransaction({
562
- to: normalizedToken,
563
- data: txData,
564
- nonce: nonces[i], // ✅ NonceManager 已经处理好递增
565
- gasLimit: finalGasLimit,
566
- gasPrice: finalGasPrice,
567
- chainId,
568
- type: 0 // Legacy 交易
569
- });
570
- return {
571
- owner: ownerAddress,
572
- alreadyApproved: false,
573
- currentAllowance,
574
- requiredAllowance: requiredAmount,
575
- signedTx
576
- };
577
- });
578
- const results = await Promise.all(signPromises);
579
- // ✅ 提取所有签名交易(过滤掉已授权的)
580
- const signedTransactions = results
581
- .filter(r => !r.alreadyApproved && r.signedTx)
582
- .map(r => r.signedTx);
583
- const alreadyApprovedCount = results.filter(r => r.alreadyApproved).length;
584
- const needApproveCount = results.filter(r => !r.alreadyApproved).length;
585
- return {
586
- signedTransactions,
587
- results,
588
- needApproveCount,
589
- alreadyApprovedCount
590
- };
591
- }
592
- // ==================== signOnly=false(默认):直接发送交易 ====================
593
- // ✅ 优化:并行检查所有钱包的授权额度
594
- const currentAllowances = await batchCheckAllowances(provider, normalizedToken, ownerAddresses, normalizedSpender);
595
- // ✅ 优化:并行发送所有需要授权的交易
596
- const approvalPromises = wallets.map(async (wallet, i) => {
597
- const ownerAddress = ownerAddresses[i];
598
- const currentAllowance = currentAllowances[i];
599
- const requiredAmount = requiredAmounts[i];
600
- const isMax = isMaxApprovals[i];
601
- try {
602
- // 如果已经授权足够,跳过
603
- // ✅ max 授权:使用阈值判断(与 BSC 一致)
604
- if (isMax ? (currentAllowance >= MAX_APPROVAL_THRESHOLD) : (currentAllowance >= requiredAmount)) {
605
- return {
606
- owner: ownerAddress,
607
- alreadyApproved: true,
608
- currentAllowance,
609
- requiredAllowance: requiredAmount
610
- };
611
- }
612
- // 发送授权交易
613
- const erc20 = new Contract(normalizedToken, ERC20_ABI, wallet);
614
- const tx = await erc20.approve(normalizedSpender, requiredAmount);
615
- const receipt = await tx.wait();
616
- return {
617
- owner: ownerAddress,
618
- alreadyApproved: false,
619
- currentAllowance,
620
- requiredAllowance: requiredAmount,
621
- txHash: receipt.hash
622
- };
623
- }
624
- catch (error) {
625
- return {
626
- owner: ownerAddress,
627
- alreadyApproved: false,
628
- currentAllowance,
629
- requiredAllowance: requiredAmount,
630
- error: error.message
631
- };
632
- }
633
- });
634
- // ✅ 优化:并行等待所有授权交易完成
635
- const results = await Promise.all(approvalPromises);
636
- // 统计结果
637
- const approvedCount = results.filter(r => !r.alreadyApproved && !r.error).length;
638
- const errorCount = results.filter(r => r.error).length;
639
- // ✅ 只要没有错误,就算成功(包括所有钱包都已授权的情况)
640
- return {
641
- success: errorCount === 0,
642
- approvedCount,
643
- results
644
- };
645
- }