@oydual31/more-vaults-sdk 0.3.2 → 0.4.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 (39) hide show
  1. package/dist/ethers/index.cjs +1794 -315
  2. package/dist/ethers/index.cjs.map +1 -1
  3. package/dist/ethers/index.d.cts +1147 -1
  4. package/dist/ethers/index.d.ts +1147 -1
  5. package/dist/ethers/index.js +1752 -317
  6. package/dist/ethers/index.js.map +1 -1
  7. package/dist/react/index.cjs.map +1 -1
  8. package/dist/react/index.d.cts +1 -1
  9. package/dist/react/index.d.ts +1 -1
  10. package/dist/react/index.js.map +1 -1
  11. package/dist/{spokeRoutes-DK7cIW4z.d.cts → spokeRoutes-BIafSbQ3.d.cts} +13 -2
  12. package/dist/{spokeRoutes-DK7cIW4z.d.ts → spokeRoutes-BIafSbQ3.d.ts} +13 -2
  13. package/dist/viem/index.cjs +34 -28
  14. package/dist/viem/index.cjs.map +1 -1
  15. package/dist/viem/index.d.cts +1 -1
  16. package/dist/viem/index.d.ts +1 -1
  17. package/dist/viem/index.js +34 -29
  18. package/dist/viem/index.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/ethers/abis.ts +92 -0
  21. package/src/ethers/chains.ts +191 -0
  22. package/src/ethers/crossChainFlows.ts +208 -0
  23. package/src/ethers/curatorMulticall.ts +195 -0
  24. package/src/ethers/curatorStatus.ts +319 -0
  25. package/src/ethers/curatorSwaps.ts +192 -0
  26. package/src/ethers/distribution.ts +156 -0
  27. package/src/ethers/index.ts +96 -1
  28. package/src/ethers/preflight.ts +225 -1
  29. package/src/ethers/redeemFlows.ts +160 -1
  30. package/src/ethers/spokeRoutes.ts +361 -0
  31. package/src/ethers/topology.ts +240 -0
  32. package/src/ethers/types.ts +95 -0
  33. package/src/ethers/userHelpers.ts +193 -0
  34. package/src/ethers/utils.ts +28 -0
  35. package/src/viem/crossChainFlows.ts +12 -22
  36. package/src/viem/index.ts +1 -0
  37. package/src/viem/preflight.ts +3 -9
  38. package/src/viem/redeemFlows.ts +4 -5
  39. package/src/viem/utils.ts +40 -0
@@ -1,4 +1,4 @@
1
- import { Contract, ZeroAddress, Interface, AbiCoder, zeroPadValue } from 'ethers';
1
+ import { Contract, Interface, ZeroAddress, zeroPadValue, AbiCoder, JsonRpcProvider } from 'ethers';
2
2
 
3
3
  // src/ethers/chains.ts
4
4
  var CHAIN_IDS = {
@@ -6,29 +6,356 @@ var CHAIN_IDS = {
6
6
  flowEVMTestnet: 545,
7
7
  arbitrum: 42161,
8
8
  base: 8453,
9
- ethereum: 1
9
+ ethereum: 1,
10
+ optimism: 10,
11
+ sonic: 146,
12
+ bsc: 56
10
13
  };
11
14
  var LZ_EIDS = {
12
15
  flowMainnet: 30336,
13
16
  flowTestnet: 30333,
14
17
  arbitrum: 30110,
15
18
  base: 30184,
16
- ethereum: 30101
19
+ ethereum: 30101,
20
+ optimism: 30111,
21
+ sonic: 30332,
22
+ bsc: 30102
17
23
  };
18
24
  var EID_TO_CHAIN_ID = {
19
25
  [LZ_EIDS.flowMainnet]: CHAIN_IDS.flowEVMMainnet,
20
26
  [LZ_EIDS.flowTestnet]: CHAIN_IDS.flowEVMTestnet,
21
27
  [LZ_EIDS.arbitrum]: CHAIN_IDS.arbitrum,
22
28
  [LZ_EIDS.base]: CHAIN_IDS.base,
23
- [LZ_EIDS.ethereum]: CHAIN_IDS.ethereum
29
+ [LZ_EIDS.ethereum]: CHAIN_IDS.ethereum,
30
+ [LZ_EIDS.optimism]: CHAIN_IDS.optimism,
31
+ [LZ_EIDS.sonic]: CHAIN_IDS.sonic,
32
+ [LZ_EIDS.bsc]: CHAIN_IDS.bsc
24
33
  };
25
34
  var CHAIN_ID_TO_EID = {
26
35
  [CHAIN_IDS.flowEVMMainnet]: LZ_EIDS.flowMainnet,
27
36
  [CHAIN_IDS.flowEVMTestnet]: LZ_EIDS.flowTestnet,
28
37
  [CHAIN_IDS.arbitrum]: LZ_EIDS.arbitrum,
29
38
  [CHAIN_IDS.base]: LZ_EIDS.base,
30
- [CHAIN_IDS.ethereum]: LZ_EIDS.ethereum
39
+ [CHAIN_IDS.ethereum]: LZ_EIDS.ethereum,
40
+ [CHAIN_IDS.optimism]: LZ_EIDS.optimism,
41
+ [CHAIN_IDS.sonic]: LZ_EIDS.sonic,
42
+ [CHAIN_IDS.bsc]: LZ_EIDS.bsc
31
43
  };
44
+ var OFT_ROUTES = {
45
+ /**
46
+ * stgUSDC — USDC bridged via Stargate v2.
47
+ * Underlying on Eth/Arb/Base/Op: native USDC. On Flow: stgUSDC (Stargate's wrapped USDC).
48
+ */
49
+ stgUSDC: {
50
+ [
51
+ 747
52
+ /* flowEVMMainnet */
53
+ ]: { oft: "0xAF54BE5B6eEc24d6BFACf1cce4eaF680A8239398", token: "0xF1815bd50389c46847f0Bda824eC8da914045D14" },
54
+ [
55
+ 1
56
+ /* ethereum */
57
+ ]: { oft: "0xc026395860Db2d07ee33e05fE50ed7bD583189C7", token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
58
+ [
59
+ 42161
60
+ /* arbitrum */
61
+ ]: { oft: "0xe8CDF27AcD73a434D661C84887215F7598e7d0d3", token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" },
62
+ [
63
+ 8453
64
+ /* base */
65
+ ]: { oft: "0x27a16dc786820B16E5c9028b75B99F6f604b5d26", token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
66
+ [
67
+ 10
68
+ /* optimism */
69
+ ]: { oft: "0xcE8CcA271Ebc0533920C83d39F417ED6A0abB7D0", token: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85" },
70
+ [
71
+ 146
72
+ /* sonic */
73
+ ]: { oft: "0xA272fFe20cFfe769CdFc4b63088DCD2C82a2D8F9", token: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894" }
74
+ },
75
+ /**
76
+ * USDT — USDT bridged via Stargate v2.
77
+ */
78
+ USDT: {
79
+ [
80
+ 747
81
+ /* flowEVMMainnet */
82
+ ]: { oft: "0xAf5191B0De278C7286d6C7CC6ab6BB8A73bA2Cd6", token: "0x674843C06FF83502ddb4D37c2E09C01cdA38cbc8" },
83
+ [
84
+ 1
85
+ /* ethereum */
86
+ ]: { oft: "0x933597a323Eb81cAe705C5bC29985172fd5A3973", token: "0xdAC17F958D2ee523a2206206994597C13D831ec7" },
87
+ [
88
+ 42161
89
+ /* arbitrum */
90
+ ]: { oft: "0xcE8CcA271Ebc0533920C83d39F417ED6A0abB7D0", token: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9" },
91
+ [
92
+ 10
93
+ /* optimism */
94
+ ]: { oft: "0x19cFCE47eD54a88614648DC3f19A5980097007dD", token: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58" }
95
+ },
96
+ /**
97
+ * USDF — USD Flow OFT. Bridges PYUSD (Ethereum) ↔ USDF (Flow EVM).
98
+ */
99
+ USDF: {
100
+ [
101
+ 747
102
+ /* flowEVMMainnet */
103
+ ]: { oft: "0x2aabea2058b5ac2d339b163c6ab6f2b6d53aabed", token: "0x2aabea2058b5ac2d339b163c6ab6f2b6d53aabed" },
104
+ [
105
+ 1
106
+ /* ethereum */
107
+ ]: { oft: "0xfa0e06b54986ad96de87a8c56fea76fbd8d493f8", token: "0x6c3ea9036406852006290770BEdFcAbA0e23A0e8" }
108
+ },
109
+ /**
110
+ * PYUSD — PayPal USD bridged via OFTAdapter (Paxos / LayerZero).
111
+ */
112
+ PYUSD: {
113
+ [
114
+ 747
115
+ /* flowEVMMainnet */
116
+ ]: { oft: "0x26d27d5AF2F6f1c14F40013C8619d97aaf015509", token: "0x99aF3EeA856556646C98c8B9b2548Fe815240750" },
117
+ [
118
+ 42161
119
+ /* arbitrum */
120
+ ]: { oft: "0x3CD2b89C49D130C08f1d683225b2e5DeB63ff876", token: "0x46850aD61C2B7d64d08c9C754F45254596696984" }
121
+ },
122
+ /**
123
+ * WFLOW — Wrapped FLOW NativeOFTAdapter (issued by Flow Foundation).
124
+ */
125
+ WFLOW: {
126
+ [
127
+ 747
128
+ /* flowEVMMainnet */
129
+ ]: { oft: "0xd296588850bee2770136464ffdddd78c32f2a07c", token: "0xd296588850bee2770136464ffdddd78c32f2a07c" },
130
+ [
131
+ 1
132
+ /* ethereum */
133
+ ]: { oft: "0xc1b45896b5fc9422a8f779653808297bb4f546f9", token: "0x5c147e74D63B1D31AA3Fd78Eb229B65161983B2b" }
134
+ },
135
+ /**
136
+ * WETH — ETH OFT via Stargate v2. underlying = native ETH (no approval needed).
137
+ */
138
+ WETH: {
139
+ [
140
+ 747
141
+ /* flowEVMMainnet */
142
+ ]: { oft: "0x45f1A95A4D3f3836523F5c83673c797f4d4d263B", token: "0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590" },
143
+ [
144
+ 1
145
+ /* ethereum */
146
+ ]: { oft: "0x77b2043768d28E9C9aB44E1aBfC95944bcE57931", token: "0x0000000000000000000000000000000000000000" },
147
+ [
148
+ 42161
149
+ /* arbitrum */
150
+ ]: { oft: "0xA45B5130f36CDcA45667738e2a258AB09f4A5f7F", token: "0x0000000000000000000000000000000000000000" },
151
+ [
152
+ 8453
153
+ /* base */
154
+ ]: { oft: "0xdc181Bd607330aeeBEF6ea62e03e5e1Fb4B6F7C7", token: "0x0000000000000000000000000000000000000000" }
155
+ },
156
+ /**
157
+ * sUSDe — Ethena staked USDe (yield-bearing stablecoin).
158
+ */
159
+ sUSDe: {
160
+ [
161
+ 1
162
+ /* ethereum */
163
+ ]: { oft: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2", token: "0x9D39A5DE30e57443BfF2A8307A4256c8797A3497" },
164
+ [
165
+ 42161
166
+ /* arbitrum */
167
+ ]: { oft: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2", token: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2" },
168
+ [
169
+ 8453
170
+ /* base */
171
+ ]: { oft: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2", token: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2" },
172
+ [
173
+ 10
174
+ /* optimism */
175
+ ]: { oft: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2", token: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2" },
176
+ [
177
+ 56
178
+ /* bsc */
179
+ ]: { oft: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2", token: "0x211cc4dd073734da055fbf44a2b4667d5e5fe5d2" }
180
+ },
181
+ /**
182
+ * USDe — Ethena USD stablecoin.
183
+ */
184
+ USDe: {
185
+ [
186
+ 1
187
+ /* ethereum */
188
+ ]: { oft: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34", token: "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3" },
189
+ [
190
+ 42161
191
+ /* arbitrum */
192
+ ]: { oft: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34", token: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34" },
193
+ [
194
+ 8453
195
+ /* base */
196
+ ]: { oft: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34", token: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34" },
197
+ [
198
+ 10
199
+ /* optimism */
200
+ ]: { oft: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34", token: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34" },
201
+ [
202
+ 56
203
+ /* bsc */
204
+ ]: { oft: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34", token: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34" }
205
+ },
206
+ /**
207
+ * weETH — Ether.Fi liquid restaking token.
208
+ */
209
+ weETH: {
210
+ [
211
+ 1
212
+ /* ethereum */
213
+ ]: { oft: "0xcd2eb13d6831d4602d80e5db9230a57596cdca63", token: "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee" },
214
+ [
215
+ 8453
216
+ /* base */
217
+ ]: { oft: "0x04c0599ae5a44757c0af6f9ec3b93da8976c150a", token: "0x04c0599ae5a44757c0af6f9ec3b93da8976c150a" },
218
+ [
219
+ 10
220
+ /* optimism */
221
+ ]: { oft: "0x5a7facb970d094b6c7ff1df0ea68d99e6e73cbff", token: "0x5a7facb970d094b6c7ff1df0ea68d99e6e73cbff" },
222
+ [
223
+ 56
224
+ /* bsc */
225
+ ]: { oft: "0x04c0599ae5a44757c0af6f9ec3b93da8976c150a", token: "0x04c0599ae5a44757c0af6f9ec3b93da8976c150a" },
226
+ [
227
+ 146
228
+ /* sonic */
229
+ ]: { oft: "0xa3d68b74bf0528fdd07263c60d6488749044914b", token: "0xa3d68b74bf0528fdd07263c60d6488749044914b" }
230
+ },
231
+ /**
232
+ * rsETH — Kelp DAO liquid restaking token.
233
+ */
234
+ rsETH: {
235
+ [
236
+ 1
237
+ /* ethereum */
238
+ ]: { oft: "0x85d456b2dff1fd8245387c0bfb64dfb700e98ef3", token: "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7" },
239
+ [
240
+ 42161
241
+ /* arbitrum */
242
+ ]: { oft: "0x4186bfc76e2e237523cbc30fd220fe055156b41f", token: "0x4186bfc76e2e237523cbc30fd220fe055156b41f" },
243
+ [
244
+ 8453
245
+ /* base */
246
+ ]: { oft: "0x1bc71130a0e39942a7658878169764bbd8a45993", token: "0x1bc71130a0e39942a7658878169764bbd8a45993" },
247
+ [
248
+ 10
249
+ /* optimism */
250
+ ]: { oft: "0x4186bfc76e2e237523cbc30fd220fe055156b41f", token: "0x4186bfc76e2e237523cbc30fd220fe055156b41f" },
251
+ [
252
+ 146
253
+ /* sonic */
254
+ ]: { oft: "0xd75787ba9aba324420d522bda84c08c87e5099b1", token: "0xd75787ba9aba324420d522bda84c08c87e5099b1" }
255
+ },
256
+ /**
257
+ * rswETH — Swell Network liquid restaking token.
258
+ */
259
+ rswETH: {
260
+ [
261
+ 1
262
+ /* ethereum */
263
+ ]: { oft: "0x1486d39646cdee84619bd05997319545a8575079", token: "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0" },
264
+ [
265
+ 42161
266
+ /* arbitrum */
267
+ ]: { oft: "0xb1fe27b32ffb5ce54e272c096547f1e86c19e72f", token: "0xb1fe27b32ffb5ce54e272c096547f1e86c19e72f" },
268
+ [
269
+ 8453
270
+ /* base */
271
+ ]: { oft: "0x850cdf416668210ed0c36bfff5d21921c7ada3b8", token: "0x850cdf416668210ed0c36bfff5d21921c7ada3b8" }
272
+ },
273
+ /**
274
+ * USR — Resolv Labs USD stablecoin.
275
+ */
276
+ USR: {
277
+ [
278
+ 1
279
+ /* ethereum */
280
+ ]: { oft: "0xd2ee2776f34ef4e7325745b06e6d464b08d4be0e", token: "0x66a1E37c9b0eAddca17d3662D6c05F4DECf3e110" },
281
+ [
282
+ 42161
283
+ /* arbitrum */
284
+ ]: { oft: "0x2492d0006411af6c8bbb1c8afc1b0197350a79e9", token: "0x2492d0006411af6c8bbb1c8afc1b0197350a79e9" },
285
+ [
286
+ 8453
287
+ /* base */
288
+ ]: { oft: "0x35e5db674d8e93a03d814fa0ada70731efe8a4b9", token: "0x35e5db674d8e93a03d814fa0ada70731efe8a4b9" },
289
+ [
290
+ 56
291
+ /* bsc */
292
+ ]: { oft: "0x2492d0006411af6c8bbb1c8afc1b0197350a79e9", token: "0x2492d0006411af6c8bbb1c8afc1b0197350a79e9" }
293
+ },
294
+ /**
295
+ * wstUSR — Resolv Labs wrapped staked USR (yield-bearing).
296
+ */
297
+ wstUSR: {
298
+ [
299
+ 1
300
+ /* ethereum */
301
+ ]: { oft: "0xab17c1fe647c37ceb9b96d1c27dd189bf8451978", token: "0x1202F5C7b4B9E47a1A484E8B270be34dbbC75055" },
302
+ [
303
+ 42161
304
+ /* arbitrum */
305
+ ]: { oft: "0x66cfbd79257dc5217903a36293120282548e2254", token: "0x66cfbd79257dc5217903a36293120282548e2254" },
306
+ [
307
+ 8453
308
+ /* base */
309
+ ]: { oft: "0xb67675158b412d53fe6b68946483ba920b135ba1", token: "0xb67675158b412d53fe6b68946483ba920b135ba1" },
310
+ [
311
+ 56
312
+ /* bsc */
313
+ ]: { oft: "0x4254813524695def4163a169e901f3d7a1a55429", token: "0x4254813524695def4163a169e901f3d7a1a55429" }
314
+ },
315
+ /**
316
+ * USDtb — Ethena treasury-backed stablecoin.
317
+ */
318
+ USDtb: {
319
+ [
320
+ 1
321
+ /* ethereum */
322
+ ]: { oft: "0xc708b6887db46005da033501f8aebee72d191a5d", token: "0xC139190F447e929f090Edeb554D95AbB8b18aC1C" },
323
+ [
324
+ 42161
325
+ /* arbitrum */
326
+ ]: { oft: "0xc708b6887db46005da033501f8aebee72d191a5d", token: "0xc708b6887db46005da033501f8aebee72d191a5d" },
327
+ [
328
+ 8453
329
+ /* base */
330
+ ]: { oft: "0xc708b6887db46005da033501f8aebee72d191a5d", token: "0xc708b6887db46005da033501f8aebee72d191a5d" }
331
+ }
332
+ };
333
+ var UNISWAP_V3_ROUTERS = {
334
+ [8453]: "0x2626664c2603336E57B271c5C0b26F421741e481",
335
+ // Base — SwapRouter02 (no deadline)
336
+ [1]: "0xE592427A0AEce92De3Edee1F18E0157C05861564",
337
+ // Ethereum — SwapRouter
338
+ [42161]: "0xE592427A0AEce92De3Edee1F18E0157C05861564",
339
+ // Arbitrum — SwapRouter
340
+ [10]: "0xE592427A0AEce92De3Edee1F18E0157C05861564",
341
+ // Optimism — SwapRouter
342
+ [747]: "0xeEDC6Ff75e1b10B903D9013c358e446a73d35341"
343
+ // Flow EVM — FlowSwap V3 SwapRouter
344
+ };
345
+ var PUBLIC_RPCS = {
346
+ 1: ["https://ethereum-rpc.publicnode.com", "https://eth.drpc.org", "https://eth.llamarpc.com"],
347
+ 10: ["https://mainnet.optimism.io", "https://optimism-rpc.publicnode.com"],
348
+ 42161: ["https://arbitrum-one-rpc.publicnode.com", "https://arbitrum.publicnode.com"],
349
+ 8453: ["https://base-rpc.publicnode.com", "https://base.llamarpc.com", "https://mainnet.base.org"],
350
+ 747: ["https://mainnet.evm.nodes.onflow.org"],
351
+ 146: ["https://rpc.soniclabs.com"],
352
+ 56: ["https://bsc-dataseed1.binance.org", "https://bsc-dataseed2.binance.org"]
353
+ };
354
+ function createChainProvider(chainId) {
355
+ const rpcs = PUBLIC_RPCS[chainId];
356
+ if (!rpcs?.length) return null;
357
+ return new JsonRpcProvider(rpcs[0], chainId, { staticNetwork: true });
358
+ }
32
359
  var LZ_TIMEOUTS = {
33
360
  /** Poll interval between balance/event checks */
34
361
  POLL_INTERVAL: 3e4,
@@ -132,6 +459,58 @@ var LZ_ENDPOINT_ABI = [
132
459
  "function composeQueue(address from, address to, bytes32 guid, uint16 index) view returns (bytes32 messageHash)",
133
460
  "function lzCompose(address _from, address _to, bytes32 _guid, uint16 _index, bytes _message, bytes _extraData) payable"
134
461
  ];
462
+ var MULTICALL_ABI = [
463
+ "function submitActions(bytes[] actionsData) returns (uint256 nonce)",
464
+ "function executeActions(uint256 actionsNonce)",
465
+ "function getPendingActions(uint256 actionsNonce) view returns (bytes[] actionsData, uint256 pendingUntil)",
466
+ "function getCurrentNonce() view returns (uint256)",
467
+ "function vetoActions(uint256[] actionsNonces)"
468
+ ];
469
+ var DEX_ABI = [
470
+ "function executeSwap(tuple(address targetContract, address tokenIn, address tokenOut, uint256 maxAmountIn, uint256 minAmountOut, bytes swapCallData) params) returns (uint256 amountOut)",
471
+ "function executeBatchSwap(tuple(tuple(address targetContract, address tokenIn, address tokenOut, uint256 maxAmountIn, uint256 minAmountOut, bytes swapCallData)[] swaps) params) returns (uint256[] amountsOut)"
472
+ ];
473
+ var BRIDGE_FACET_ABI = [
474
+ "function executeBridging(address adapter, address token, uint256 amount, bytes bridgeSpecificParams) payable",
475
+ "function initVaultActionRequest(uint8 actionType, bytes actionCallData, uint256 amountLimit, bytes extraOptions) payable returns (bytes32 guid)",
476
+ "function executeRequest(bytes32 guid)"
477
+ ];
478
+ var ERC7540_FACET_ABI = [
479
+ "function erc7540RequestDeposit(address vault, uint256 assets) returns (uint256 requestId)",
480
+ "function erc7540RequestRedeem(address vault, uint256 shares) returns (uint256 requestId)",
481
+ "function erc7540Deposit(address vault, uint256 assets) returns (uint256 shares)",
482
+ "function erc7540Redeem(address vault, uint256 shares) returns (uint256 assets)"
483
+ ];
484
+ var ERC4626_FACET_ABI = [
485
+ "function erc4626Deposit(address vault, uint256 assets) returns (uint256 shares)",
486
+ "function erc4626Redeem(address vault, uint256 shares) returns (uint256 assets)"
487
+ ];
488
+ var CURATOR_CONFIG_ABI = [
489
+ "function curator() view returns (address)",
490
+ "function timeLockPeriod() view returns (uint256)",
491
+ "function getAvailableAssets() view returns (address[])",
492
+ "function getMaxSlippagePercent() view returns (uint256)",
493
+ "function getCrossChainAccountingManager() view returns (address)",
494
+ "function paused() view returns (bool)"
495
+ ];
496
+ var LZ_ADAPTER_ABI = [
497
+ "function quoteBridgeFee(bytes bridgeSpecificParams) view returns (uint256 nativeFee)",
498
+ "function quoteReadFee(address[] vaults, uint32[] eids, bytes _extraOptions) view returns (tuple(uint256 nativeFee, uint256 lzTokenFee) fee)"
499
+ ];
500
+ var VAULT_ANALYSIS_ABI = [
501
+ "function getAvailableAssets() view returns (address[])",
502
+ "function getDepositableAssets() view returns (address[])",
503
+ "function isAssetAvailable(address asset) view returns (bool)",
504
+ "function isAssetDepositable(address asset) view returns (bool)",
505
+ "function isDepositWhitelistEnabled() view returns (bool)",
506
+ "function getAvailableToDeposit(address depositor) view returns (uint256)",
507
+ "function moreVaultsRegistry() view returns (address)"
508
+ ];
509
+ var REGISTRY_ABI = [
510
+ "function isWhitelisted(address protocol) view returns (bool)",
511
+ "function isBridgeAllowed(address bridge) view returns (bool)",
512
+ "function getAllowedFacets() view returns (address[])"
513
+ ];
135
514
 
136
515
  // src/ethers/errors.ts
137
516
  var MoreVaultsError = class extends Error {
@@ -206,89 +585,9 @@ var WrongChainError = class extends MoreVaultsError {
206
585
  this.name = "WrongChainError";
207
586
  }
208
587
  };
209
- async function preflightAsync(provider, vault, escrow) {
210
- const config = new Contract(vault, CONFIG_ABI, provider);
211
- const bridge = new Contract(vault, BRIDGE_ABI, provider);
212
- const [ccManager, registeredEscrow, isHub, oraclesEnabled, isPaused] = await Promise.all([
213
- config.getCrossChainAccountingManager(),
214
- config.getEscrow(),
215
- config.isHub(),
216
- bridge.oraclesCrossChainAccounting(),
217
- config.paused()
218
- ]);
219
- if (ccManager === ZeroAddress) {
220
- throw new Error(
221
- `[MoreVaults] CCManager not configured on vault ${vault}. Call setCrossChainAccountingManager(ccManagerAddress) as vault owner first.`
222
- );
223
- }
224
- if (registeredEscrow === ZeroAddress) {
225
- throw new Error(
226
- `[MoreVaults] Escrow not configured for vault ${vault}. The registry must have an escrow set for this vault.`
227
- );
228
- }
229
- if (!isHub) {
230
- throw new Error(
231
- `[MoreVaults] Vault ${vault} is not a hub vault. Async flows (D4/D5/R5) only work on hub vaults.`
232
- );
233
- }
234
- if (oraclesEnabled) {
235
- throw new Error(
236
- `[MoreVaults] Vault ${vault} has oracle-based cross-chain accounting enabled. Use depositSimple/depositCrossChainOracleOn instead of async flows.`
237
- );
238
- }
239
- if (isPaused) {
240
- throw new Error(
241
- `[MoreVaults] Vault ${vault} is paused. Cannot perform any actions.`
242
- );
243
- }
244
- }
245
- async function preflightRedeemLiquidity(provider, vault, shares) {
246
- const config = new Contract(vault, CONFIG_ABI, provider);
247
- const bridge = new Contract(vault, BRIDGE_ABI, provider);
248
- const [isHub, oraclesEnabled] = await Promise.all([
249
- config.isHub(),
250
- bridge.oraclesCrossChainAccounting()
251
- ]);
252
- if (!isHub || oraclesEnabled) return;
253
- const vaultContract = new Contract(vault, VAULT_ABI, provider);
254
- const underlying = await vaultContract.asset();
255
- const underlyingContract = new Contract(underlying, ERC20_ABI, provider);
256
- const [hubLiquid, assetsNeeded] = await Promise.all([
257
- underlyingContract.balanceOf(vault),
258
- vaultContract.convertToAssets(shares)
259
- ]);
260
- if (hubLiquid < assetsNeeded) {
261
- throw new InsufficientLiquidityError(vault, hubLiquid, assetsNeeded);
262
- }
263
- }
264
- async function preflightSync(provider, vault) {
265
- const config = new Contract(vault, CONFIG_ABI, provider);
266
- const [isPaused, depositCapResult] = await Promise.all([
267
- config.paused(),
268
- config.maxDeposit(ZeroAddress).catch(() => null)
269
- ]);
270
- if (isPaused) {
271
- throw new Error(
272
- `[MoreVaults] Vault ${vault} is paused. Cannot perform any actions.`
273
- );
274
- }
275
- if (depositCapResult !== null && depositCapResult === 0n) {
276
- throw new Error(
277
- `[MoreVaults] Vault ${vault} has reached deposit capacity. No more deposits accepted.`
278
- );
279
- }
280
- }
281
-
282
- // src/ethers/chainValidation.ts
283
- async function validateWalletChain(signer, hubChainId) {
284
- if (!hubChainId) return;
285
- const network = await signer.provider?.getNetwork();
286
- if (!network) return;
287
- const current = Number(network.chainId);
288
- if (current !== hubChainId) {
289
- throw new WrongChainError(current, hubChainId);
290
- }
291
- }
588
+ var STARGATE_TYPE_ABI = [
589
+ "function stargateType() view returns (uint8)"
590
+ ];
292
591
  var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
293
592
  var MULTICALL3_ABI = [
294
593
  "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
@@ -468,8 +767,18 @@ async function getVaultStatus(provider, vault) {
468
767
  issues
469
768
  };
470
769
  }
471
-
472
- // src/ethers/depositFlows.ts
770
+ async function detectStargateOft(provider, oft) {
771
+ try {
772
+ const contract = new Contract(oft, STARGATE_TYPE_ABI, provider);
773
+ await contract.stargateType();
774
+ return true;
775
+ } catch {
776
+ return false;
777
+ }
778
+ }
779
+ var LZ_ENDPOINT = "0x1a44076050125825900e736c501f859c50fe728c";
780
+ var EMPTY_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
781
+ var RECEIVED_HASH = "0x0000000000000000000000000000000000000000000000000000000000000001";
473
782
  async function ensureAllowance2(signer, token, spender, amount) {
474
783
  const owner = await signer.getAddress();
475
784
  const erc20 = new Contract(token, ERC20_ABI, signer);
@@ -479,176 +788,34 @@ async function ensureAllowance2(signer, token, spender, amount) {
479
788
  await tx.wait();
480
789
  }
481
790
  }
482
- async function depositSimple(signer, addresses, assets, receiver) {
483
- const provider = signer.provider;
484
- await validateWalletChain(signer, addresses.hubChainId);
485
- await preflightSync(provider, addresses.vault);
486
- const vault = new Contract(addresses.vault, VAULT_ABI, signer);
487
- const underlying = await vault.asset();
488
- await ensureAllowance2(signer, underlying, addresses.vault, assets);
489
- const tx = await vault["deposit(uint256,address)"](assets, receiver);
490
- const receipt = await tx.wait();
491
- let shares = 0n;
492
- for (const log of receipt.logs) {
493
- try {
494
- const parsed = vault.interface.parseLog({
495
- topics: log.topics,
496
- data: log.data
497
- });
498
- if (parsed && parsed.name === "Transfer" && parsed.args[0] === "0x0000000000000000000000000000000000000000") {
499
- shares = parsed.args[2];
500
- break;
501
- }
502
- } catch {
503
- }
504
- }
505
- return { receipt, shares };
506
- }
507
- async function depositMultiAsset(signer, addresses, tokens, amounts, receiver, minShares) {
508
- await validateWalletChain(signer, addresses.hubChainId);
509
- for (let i = 0; i < tokens.length; i++) {
510
- await ensureAllowance2(signer, tokens[i], addresses.vault, amounts[i]);
511
- }
512
- const vault = new Contract(addresses.vault, VAULT_ABI, signer);
513
- const tx = await vault["deposit(address[],uint256[],address,uint256)"](tokens, amounts, receiver, minShares);
514
- const receipt = await tx.wait();
515
- let shares = 0n;
516
- for (const log of receipt.logs) {
517
- try {
518
- const parsed = vault.interface.parseLog({
519
- topics: log.topics,
520
- data: log.data
521
- });
522
- if (parsed && parsed.name === "Deposit") {
523
- shares = parsed.args[4];
524
- break;
525
- }
526
- } catch {
527
- }
528
- }
529
- return { receipt, shares };
530
- }
531
- var depositCrossChainOracleOn = depositSimple;
532
- async function depositAsync(signer, addresses, assets, receiver, lzFee, extraOptions = "0x") {
533
- const provider = signer.provider;
534
- const escrow = addresses.escrow ?? await new Contract(addresses.vault, ["function getEscrow() view returns (address)"], provider).getEscrow();
535
- if (escrow === ZeroAddress) throw new EscrowNotConfiguredError(addresses.vault);
536
- await validateWalletChain(signer, addresses.hubChainId);
537
- await preflightAsync(provider, addresses.vault);
538
- const vault = new Contract(addresses.vault, VAULT_ABI, signer);
539
- const underlying = await vault.asset();
540
- await ensureAllowance2(signer, underlying, escrow, assets);
791
+ async function depositFromSpoke(signer, spokeOFT, composer, hubEid, spokeEid, amount, receiver, lzFee, minMsgValue = 0n, minSharesOut = 0n, minAmountLD, extraOptions = "0x") {
792
+ await ensureAllowance2(signer, spokeOFT, spokeOFT, amount);
793
+ const oft = new Contract(spokeOFT, OFT_ABI, signer);
794
+ const refundAddress = await signer.getAddress();
795
+ const receiverBytes32 = zeroPadValue(receiver, 32);
796
+ const composerBytes32 = zeroPadValue(composer, 32);
797
+ const hopSendParam = {
798
+ dstEid: spokeEid,
799
+ to: receiverBytes32,
800
+ amountLD: 0n,
801
+ minAmountLD: minSharesOut,
802
+ extraOptions: "0x",
803
+ composeMsg: "0x",
804
+ oftCmd: "0x"
805
+ };
541
806
  const coder = AbiCoder.defaultAbiCoder();
542
- const actionCallData = coder.encode(
543
- ["uint256", "address"],
544
- [assets, receiver]
807
+ const composeMsg = coder.encode(
808
+ [
809
+ "tuple(uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd)",
810
+ "uint256"
811
+ ],
812
+ [hopSendParam, minMsgValue]
545
813
  );
546
- const bridge = new Contract(addresses.vault, BRIDGE_ABI, signer);
547
- const guid = await bridge.initVaultActionRequest.staticCall(
548
- ActionType.DEPOSIT,
549
- actionCallData,
550
- 0,
551
- extraOptions,
552
- { value: lzFee }
553
- );
554
- const tx = await bridge.initVaultActionRequest(
555
- ActionType.DEPOSIT,
556
- actionCallData,
557
- 0,
558
- // amountLimit = 0 for deposits (minAmountOut handled by cross-chain manager)
559
- extraOptions,
560
- { value: lzFee }
561
- );
562
- const receipt = await tx.wait();
563
- return { receipt, guid };
564
- }
565
- async function mintAsync(signer, addresses, shares, maxAssets, receiver, lzFee, extraOptions = "0x") {
566
- const provider = signer.provider;
567
- const escrow = addresses.escrow ?? await new Contract(addresses.vault, ["function getEscrow() view returns (address)"], provider).getEscrow();
568
- if (escrow === ZeroAddress) throw new EscrowNotConfiguredError(addresses.vault);
569
- await validateWalletChain(signer, addresses.hubChainId);
570
- await preflightAsync(provider, addresses.vault);
571
- const vault = new Contract(addresses.vault, VAULT_ABI, signer);
572
- const underlying = await vault.asset();
573
- await ensureAllowance2(signer, underlying, escrow, maxAssets);
574
- const coder = AbiCoder.defaultAbiCoder();
575
- const actionCallData = coder.encode(
576
- ["uint256", "address"],
577
- [shares, receiver]
578
- );
579
- const bridge = new Contract(addresses.vault, BRIDGE_ABI, signer);
580
- const guid = await bridge.initVaultActionRequest.staticCall(
581
- ActionType.MINT,
582
- actionCallData,
583
- maxAssets,
584
- extraOptions,
585
- { value: lzFee }
586
- );
587
- const tx = await bridge.initVaultActionRequest(
588
- ActionType.MINT,
589
- actionCallData,
590
- maxAssets,
591
- extraOptions,
592
- { value: lzFee }
593
- );
594
- const receipt = await tx.wait();
595
- return { receipt, guid };
596
- }
597
- async function smartDeposit(signer, provider, addresses, assets, receiver, extraOptions = "0x") {
598
- const vault = addresses.vault;
599
- const status = await getVaultStatus(provider, vault);
600
- if (status.mode === "paused") {
601
- throw new VaultPausedError(vault);
602
- }
603
- if (status.mode === "full") {
604
- throw new CapacityFullError(vault);
605
- }
606
- if (status.recommendedDepositFlow === "depositAsync") {
607
- const lzFee = await quoteLzFee(provider, vault, extraOptions);
608
- return depositAsync(signer, addresses, assets, receiver, lzFee, extraOptions);
609
- }
610
- return depositSimple(signer, addresses, assets, receiver);
611
- }
612
- var LZ_ENDPOINT = "0x1a44076050125825900e736c501f859c50fe728c";
613
- var EMPTY_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
614
- var RECEIVED_HASH = "0x0000000000000000000000000000000000000000000000000000000000000001";
615
- async function ensureAllowance3(signer, token, spender, amount) {
616
- const owner = await signer.getAddress();
617
- const erc20 = new Contract(token, ERC20_ABI, signer);
618
- const current = await erc20.allowance(owner, spender);
619
- if (current < amount) {
620
- const tx = await erc20.approve(spender, amount);
621
- await tx.wait();
622
- }
623
- }
624
- async function depositFromSpoke(signer, spokeOFT, composer, hubEid, spokeEid, amount, receiver, lzFee, minMsgValue = 0n, minSharesOut = 0n, minAmountLD, extraOptions = "0x") {
625
- await ensureAllowance3(signer, spokeOFT, spokeOFT, amount);
626
- const oft = new Contract(spokeOFT, OFT_ABI, signer);
627
- const refundAddress = await signer.getAddress();
628
- const receiverBytes32 = zeroPadValue(receiver, 32);
629
- const composerBytes32 = zeroPadValue(composer, 32);
630
- const hopSendParam = {
631
- dstEid: spokeEid,
632
- to: receiverBytes32,
633
- amountLD: 0n,
634
- minAmountLD: minSharesOut,
635
- extraOptions: "0x",
636
- composeMsg: "0x",
637
- oftCmd: "0x"
638
- };
639
- const coder = AbiCoder.defaultAbiCoder();
640
- const composeMsg = coder.encode(
641
- [
642
- "tuple(uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd)",
643
- "uint256"
644
- ],
645
- [hopSendParam, minMsgValue]
646
- );
647
- const sendParam = {
648
- dstEid: hubEid,
649
- to: composerBytes32,
650
- amountLD: amount,
651
- minAmountLD: minAmountLD ?? amount,
814
+ const sendParam = {
815
+ dstEid: hubEid,
816
+ to: composerBytes32,
817
+ amountLD: amount,
818
+ minAmountLD: minAmountLD ?? amount,
652
819
  extraOptions,
653
820
  composeMsg,
654
821
  oftCmd: "0x"
@@ -689,68 +856,621 @@ async function quoteDepositFromSpokeFee(provider, spokeOFT, composer, hubEid, sp
689
856
  minMsgValue
690
857
  ]
691
858
  );
692
- const sendParam = {
693
- dstEid: hubEid,
694
- to: composerBytes32,
695
- amountLD: amount,
696
- minAmountLD: minAmountLD ?? amount,
859
+ const sendParam = {
860
+ dstEid: hubEid,
861
+ to: composerBytes32,
862
+ amountLD: amount,
863
+ minAmountLD: minAmountLD ?? amount,
864
+ extraOptions,
865
+ composeMsg: composeMsgBytes,
866
+ oftCmd: "0x"
867
+ };
868
+ const oft = new Contract(spokeOFT, OFT_ABI, provider);
869
+ const fee = await oft.quoteSend(sendParam, false);
870
+ return fee.nativeFee;
871
+ }
872
+ async function quoteComposeFee(provider, vault, spokeEid, receiver) {
873
+ try {
874
+ const vaultContract = new Contract(vault, BRIDGE_ABI, provider);
875
+ const readFee = await vaultContract.quoteAccountingFee("0x");
876
+ let shareSendFee = 0n;
877
+ if (spokeEid && receiver) {
878
+ try {
879
+ const COMPOSER_ABI = ["function SHARE_OFT() view returns (address)"];
880
+ const FACTORY_ABI2 = ["function vaultComposer(address _vault) view returns (address)"];
881
+ const factory = new Contract("0x7bDB8B17604b03125eFAED33cA0c55FBf856BB0C", FACTORY_ABI2, provider);
882
+ const composerAddr = await factory.vaultComposer(vault);
883
+ const composer = new Contract(composerAddr, COMPOSER_ABI, provider);
884
+ const shareOftAddr = await composer.SHARE_OFT();
885
+ const shareOft = new Contract(shareOftAddr, OFT_ABI, provider);
886
+ const receiverBytes32 = zeroPadValue(receiver, 32);
887
+ const fee = await shareOft.quoteSend({
888
+ dstEid: spokeEid,
889
+ to: receiverBytes32,
890
+ amountLD: 1000000n,
891
+ minAmountLD: 0n,
892
+ extraOptions: "0x",
893
+ composeMsg: "0x",
894
+ oftCmd: "0x"
895
+ }, false);
896
+ shareSendFee = fee.nativeFee;
897
+ } catch {
898
+ }
899
+ }
900
+ return (readFee + shareSendFee) * 110n / 100n;
901
+ } catch {
902
+ return 500000000000000n;
903
+ }
904
+ }
905
+ async function executeCompose(signer, from, to, guid, message, fee, index = 0) {
906
+ const endpoint = new Contract(LZ_ENDPOINT, LZ_ENDPOINT_ABI, signer);
907
+ const hash = await endpoint.composeQueue(from, to, guid, index);
908
+ if (hash === EMPTY_HASH) {
909
+ throw new Error("Compose not found in queue (hash = 0). Never sent or wrong parameters.");
910
+ }
911
+ if (hash === RECEIVED_HASH) {
912
+ throw new Error("Compose already delivered \u2014 no action needed.");
913
+ }
914
+ const tx = await endpoint.lzCompose(from, to, guid, index, message, "0x", {
915
+ value: fee,
916
+ gasLimit: 5000000n
917
+ });
918
+ const receipt = await tx.wait();
919
+ return { receipt };
920
+ }
921
+ async function waitForCompose(hubProvider, composeData, receiver, pollIntervalMs = 2e4, timeoutMs = 18e5) {
922
+ const deadline = Date.now() + timeoutMs;
923
+ const composer = composeData.to.toLowerCase();
924
+ const endpoint = composeData.endpoint;
925
+ const receiverNeedle = receiver.replace(/^0x/, "").toLowerCase();
926
+ const startBlock = composeData.hubBlockStart;
927
+ const hubChainId = composeData.hubChainId;
928
+ const candidateAddresses = [];
929
+ for (const chainMap of Object.values(OFT_ROUTES)) {
930
+ const entry = chainMap[hubChainId];
931
+ if (entry) candidateAddresses.push(entry.oft.toLowerCase());
932
+ }
933
+ const stargateChecks = await Promise.all(
934
+ candidateAddresses.map(async (addr) => ({
935
+ addr,
936
+ isSg: await detectStargateOft(hubProvider, addr)
937
+ }))
938
+ );
939
+ const knownFromAddresses = stargateChecks.filter((c) => c.isSg).map((c) => c.addr);
940
+ const endpointContract = new Contract(endpoint, LZ_ENDPOINT_ABI, hubProvider);
941
+ const COMPOSE_SENT_TOPIC = "0x0c68e6a0b0fb0f33c52455a8da89b21fc640a3dd4a1b21d9bfcc8aeee4a43e84";
942
+ let attempt = 0;
943
+ let scannedUpTo = startBlock - 1n;
944
+ while (Date.now() < deadline) {
945
+ attempt++;
946
+ const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
947
+ try {
948
+ const currentBlock = BigInt(await hubProvider.getBlockNumber());
949
+ const chunkSize = 500n;
950
+ let from = scannedUpTo + 1n;
951
+ while (from <= currentBlock) {
952
+ const chunkEnd = from + chunkSize > currentBlock ? currentBlock : from + chunkSize;
953
+ try {
954
+ const logs = await hubProvider.getLogs({
955
+ address: endpoint,
956
+ topics: [COMPOSE_SENT_TOPIC],
957
+ fromBlock: `0x${from.toString(16)}`,
958
+ toBlock: `0x${chunkEnd.toString(16)}`
959
+ });
960
+ for (const log of logs) {
961
+ try {
962
+ const coder = AbiCoder.defaultAbiCoder();
963
+ const decoded = coder.decode(
964
+ ["address", "address", "bytes32", "uint16", "bytes"],
965
+ log.data
966
+ );
967
+ const logFrom = decoded[0].toLowerCase();
968
+ const logTo = decoded[1].toLowerCase();
969
+ const logGuid = decoded[2];
970
+ const logIndex = Number(decoded[3]);
971
+ const logMessage = decoded[4];
972
+ if (logTo === composer && logMessage.toLowerCase().includes(receiverNeedle)) {
973
+ const hash = await endpointContract.composeQueue(
974
+ logFrom,
975
+ composer,
976
+ logGuid,
977
+ logIndex
978
+ );
979
+ if (hash !== EMPTY_HASH && hash !== RECEIVED_HASH) {
980
+ console.log(`[${elapsed}s] Poll #${attempt} \u2014 compose found! (block ${log.blockNumber})`);
981
+ return {
982
+ ...composeData,
983
+ from: decoded[0],
984
+ to: composeData.to,
985
+ guid: logGuid,
986
+ index: logIndex,
987
+ message: logMessage
988
+ };
989
+ }
990
+ }
991
+ } catch {
992
+ }
993
+ }
994
+ } catch {
995
+ break;
996
+ }
997
+ from = chunkEnd + 1n;
998
+ }
999
+ scannedUpTo = currentBlock;
1000
+ } catch {
1001
+ }
1002
+ let guidMatchFound = false;
1003
+ for (const fromAddr of knownFromAddresses) {
1004
+ try {
1005
+ const hash = await endpointContract.composeQueue(
1006
+ fromAddr,
1007
+ composer,
1008
+ composeData.guid,
1009
+ 0
1010
+ );
1011
+ if (hash !== EMPTY_HASH && hash !== RECEIVED_HASH) {
1012
+ console.log(`[${elapsed}s] Poll #${attempt} \u2014 composeQueue confirms pending (GUID match), re-scanning for message...`);
1013
+ scannedUpTo = startBlock - 1n;
1014
+ guidMatchFound = true;
1015
+ }
1016
+ } catch {
1017
+ }
1018
+ }
1019
+ if (!guidMatchFound) {
1020
+ console.log(`[${elapsed}s] Poll #${attempt} \u2014 compose not found yet, waiting ${pollIntervalMs / 1e3}s...`);
1021
+ }
1022
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
1023
+ }
1024
+ throw new Error(
1025
+ `Timeout waiting for compose after ${timeoutMs / 6e4} min. Check LayerZero scan for composer ${composeData.to}.`
1026
+ );
1027
+ }
1028
+
1029
+ // src/ethers/preflight.ts
1030
+ async function preflightAsync(provider, vault, escrow) {
1031
+ const config = new Contract(vault, CONFIG_ABI, provider);
1032
+ const bridge = new Contract(vault, BRIDGE_ABI, provider);
1033
+ const [ccManager, registeredEscrow, isHub, oraclesEnabled, isPaused] = await Promise.all([
1034
+ config.getCrossChainAccountingManager(),
1035
+ config.getEscrow(),
1036
+ config.isHub(),
1037
+ bridge.oraclesCrossChainAccounting(),
1038
+ config.paused()
1039
+ ]);
1040
+ if (ccManager === ZeroAddress) {
1041
+ throw new Error(
1042
+ `[MoreVaults] CCManager not configured on vault ${vault}. Call setCrossChainAccountingManager(ccManagerAddress) as vault owner first.`
1043
+ );
1044
+ }
1045
+ if (registeredEscrow === ZeroAddress) {
1046
+ throw new Error(
1047
+ `[MoreVaults] Escrow not configured for vault ${vault}. The registry must have an escrow set for this vault.`
1048
+ );
1049
+ }
1050
+ if (!isHub) {
1051
+ throw new Error(
1052
+ `[MoreVaults] Vault ${vault} is not a hub vault. Async flows (D4/D5/R5) only work on hub vaults.`
1053
+ );
1054
+ }
1055
+ if (oraclesEnabled) {
1056
+ throw new Error(
1057
+ `[MoreVaults] Vault ${vault} has oracle-based cross-chain accounting enabled. Use depositSimple/depositCrossChainOracleOn instead of async flows.`
1058
+ );
1059
+ }
1060
+ if (isPaused) {
1061
+ throw new Error(
1062
+ `[MoreVaults] Vault ${vault} is paused. Cannot perform any actions.`
1063
+ );
1064
+ }
1065
+ }
1066
+ async function preflightRedeemLiquidity(provider, vault, shares) {
1067
+ const config = new Contract(vault, CONFIG_ABI, provider);
1068
+ const bridge = new Contract(vault, BRIDGE_ABI, provider);
1069
+ const [isHub, oraclesEnabled] = await Promise.all([
1070
+ config.isHub(),
1071
+ bridge.oraclesCrossChainAccounting()
1072
+ ]);
1073
+ if (!isHub || oraclesEnabled) return;
1074
+ const vaultContract = new Contract(vault, VAULT_ABI, provider);
1075
+ const underlying = await vaultContract.asset();
1076
+ const underlyingContract = new Contract(underlying, ERC20_ABI, provider);
1077
+ const [hubLiquid, assetsNeeded] = await Promise.all([
1078
+ underlyingContract.balanceOf(vault),
1079
+ vaultContract.convertToAssets(shares)
1080
+ ]);
1081
+ if (hubLiquid < assetsNeeded) {
1082
+ throw new InsufficientLiquidityError(vault, hubLiquid, assetsNeeded);
1083
+ }
1084
+ }
1085
+ async function preflightSync(provider, vault) {
1086
+ const config = new Contract(vault, CONFIG_ABI, provider);
1087
+ const [isPaused, depositCapResult] = await Promise.all([
1088
+ config.paused(),
1089
+ config.maxDeposit(ZeroAddress).catch(() => null)
1090
+ ]);
1091
+ if (isPaused) {
1092
+ throw new Error(
1093
+ `[MoreVaults] Vault ${vault} is paused. Cannot perform any actions.`
1094
+ );
1095
+ }
1096
+ if (depositCapResult !== null && depositCapResult === 0n) {
1097
+ throw new Error(
1098
+ `[MoreVaults] Vault ${vault} has reached deposit capacity. No more deposits accepted.`
1099
+ );
1100
+ }
1101
+ }
1102
+ async function preflightSpokeDeposit(spokeProvider, vault, spokeOFT, hubEid, spokeEid, amount, userAddress, lzFee) {
1103
+ const OFT_TOKEN_ABI = ["function token() view returns (address)"];
1104
+ const oftContract = new Contract(spokeOFT, OFT_TOKEN_ABI, spokeProvider);
1105
+ const spokeToken = await oftContract.token();
1106
+ const tokenContract = new Contract(spokeToken, ERC20_ABI, spokeProvider);
1107
+ const [spokeTokenBalance, spokeNativeBalance] = await Promise.all([
1108
+ tokenContract.balanceOf(userAddress),
1109
+ spokeProvider.getBalance(userAddress)
1110
+ ]);
1111
+ if (spokeTokenBalance < amount) {
1112
+ throw new Error(
1113
+ `[MoreVaults] Insufficient token balance on spoke chain.
1114
+ Need: ${amount}
1115
+ Have: ${spokeTokenBalance}
1116
+ Token: ${spokeToken}`
1117
+ );
1118
+ }
1119
+ const gasBuffer = 500000000000000n;
1120
+ if (spokeNativeBalance < lzFee + gasBuffer) {
1121
+ throw new Error(
1122
+ `[MoreVaults] Insufficient native gas on spoke chain for TX1.
1123
+ Need: ~${lzFee + gasBuffer} wei (LZ fee + gas)
1124
+ Have: ${spokeNativeBalance} wei`
1125
+ );
1126
+ }
1127
+ const isStargate = await detectStargateOft(spokeProvider, spokeOFT);
1128
+ let hubNativeBalance = 0n;
1129
+ let estimatedComposeFee = 0n;
1130
+ if (isStargate) {
1131
+ const hubChainId = EID_TO_CHAIN_ID[hubEid];
1132
+ const hubProvider = createChainProvider(hubChainId);
1133
+ if (hubProvider) {
1134
+ [hubNativeBalance, estimatedComposeFee] = await Promise.all([
1135
+ hubProvider.getBalance(userAddress),
1136
+ quoteComposeFee(hubProvider, vault, spokeEid, userAddress)
1137
+ ]);
1138
+ const hubGasBuffer = 300000000000000n;
1139
+ const totalNeeded = estimatedComposeFee + hubGasBuffer;
1140
+ if (hubNativeBalance < totalNeeded) {
1141
+ throw new Error(
1142
+ `[MoreVaults] Insufficient ETH on hub chain for TX2 (compose retry).
1143
+ This is a Stargate 2-TX flow \u2014 TX2 requires ETH on the hub chain.
1144
+ Need: ~${totalNeeded} wei (compose fee ${estimatedComposeFee} + gas)
1145
+ Have: ${hubNativeBalance} wei
1146
+ Short: ${totalNeeded - hubNativeBalance} wei
1147
+ Send ETH to ${userAddress} on chainId ${hubChainId} before depositing.`
1148
+ );
1149
+ }
1150
+ }
1151
+ }
1152
+ return {
1153
+ spokeTokenBalance,
1154
+ spokeNativeBalance,
1155
+ hubNativeBalance,
1156
+ estimatedComposeFee,
1157
+ isStargate
1158
+ };
1159
+ }
1160
+ async function preflightSpokeRedeem(route, shares, userAddress, shareBridgeFee) {
1161
+ const spokeProvider = createChainProvider(route.spokeChainId);
1162
+ const hubProvider = createChainProvider(route.hubChainId);
1163
+ if (!spokeProvider) throw new Error(`No public RPC for spoke chainId ${route.spokeChainId}`);
1164
+ if (!hubProvider) throw new Error(`No public RPC for hub chainId ${route.hubChainId}`);
1165
+ const spokeShareContract = new Contract(route.spokeShareOft, ERC20_ABI, spokeProvider);
1166
+ const [sharesOnSpoke, spokeNativeBalance, hubNativeBalance] = await Promise.all([
1167
+ spokeShareContract.balanceOf(userAddress),
1168
+ spokeProvider.getBalance(userAddress),
1169
+ hubProvider.getBalance(userAddress)
1170
+ ]);
1171
+ if (sharesOnSpoke < shares) {
1172
+ throw new Error(
1173
+ `[MoreVaults] Insufficient shares on spoke chain.
1174
+ Need: ${shares}
1175
+ Have: ${sharesOnSpoke}
1176
+ Token: ${route.spokeShareOft}`
1177
+ );
1178
+ }
1179
+ const spokeGasBuffer = 500000000000000n;
1180
+ if (spokeNativeBalance < shareBridgeFee + spokeGasBuffer) {
1181
+ throw new Error(
1182
+ `[MoreVaults] Insufficient native gas on spoke for TX1 (share bridge).
1183
+ Need: ~${shareBridgeFee + spokeGasBuffer} wei (LZ fee + gas)
1184
+ Have: ${spokeNativeBalance} wei`
1185
+ );
1186
+ }
1187
+ let estimatedAssetBridgeFee = 0n;
1188
+ try {
1189
+ const toBytes32 = `0x${userAddress.replace(/^0x/, "").padStart(64, "0")}`;
1190
+ const dummyAmount = 1000000n;
1191
+ const hubOft = new Contract(route.hubAssetOft, OFT_ABI, hubProvider);
1192
+ const feeResult = await hubOft.quoteSend({
1193
+ dstEid: route.spokeEid,
1194
+ to: toBytes32,
1195
+ amountLD: dummyAmount,
1196
+ minAmountLD: dummyAmount * 99n / 100n,
1197
+ extraOptions: "0x",
1198
+ composeMsg: "0x",
1199
+ oftCmd: route.isStargate ? "0x01" : "0x"
1200
+ }, false);
1201
+ estimatedAssetBridgeFee = feeResult.nativeFee;
1202
+ } catch {
1203
+ estimatedAssetBridgeFee = 300000000000000n;
1204
+ }
1205
+ const hubGasBuffer = 300000000000000n;
1206
+ const totalHubNeeded = estimatedAssetBridgeFee + hubGasBuffer;
1207
+ if (hubNativeBalance < totalHubNeeded) {
1208
+ throw new Error(
1209
+ `[MoreVaults] Insufficient ETH on hub chain for TX2 (redeem) + TX3 (asset bridge).
1210
+ Need: ~${totalHubNeeded} wei (LZ fee ${estimatedAssetBridgeFee} + gas)
1211
+ Have: ${hubNativeBalance} wei
1212
+ Short: ${totalHubNeeded - hubNativeBalance} wei
1213
+ Send ETH to ${userAddress} on chainId ${route.hubChainId} before redeeming.`
1214
+ );
1215
+ }
1216
+ return {
1217
+ sharesOnSpoke,
1218
+ spokeNativeBalance,
1219
+ hubNativeBalance,
1220
+ estimatedAssetBridgeFee,
1221
+ estimatedAssetsOut: 0n,
1222
+ hubLiquidBalance: 0n
1223
+ };
1224
+ }
1225
+
1226
+ // src/ethers/chainValidation.ts
1227
+ async function validateWalletChain(signer, hubChainId) {
1228
+ if (!hubChainId) return;
1229
+ const network = await signer.provider?.getNetwork();
1230
+ if (!network) return;
1231
+ const current = Number(network.chainId);
1232
+ if (current !== hubChainId) {
1233
+ throw new WrongChainError(current, hubChainId);
1234
+ }
1235
+ }
1236
+
1237
+ // src/ethers/depositFlows.ts
1238
+ async function ensureAllowance3(signer, token, spender, amount) {
1239
+ const owner = await signer.getAddress();
1240
+ const erc20 = new Contract(token, ERC20_ABI, signer);
1241
+ const current = await erc20.allowance(owner, spender);
1242
+ if (current < amount) {
1243
+ const tx = await erc20.approve(spender, amount);
1244
+ await tx.wait();
1245
+ }
1246
+ }
1247
+ async function depositSimple(signer, addresses, assets, receiver) {
1248
+ const provider = signer.provider;
1249
+ await validateWalletChain(signer, addresses.hubChainId);
1250
+ await preflightSync(provider, addresses.vault);
1251
+ const vault = new Contract(addresses.vault, VAULT_ABI, signer);
1252
+ const underlying = await vault.asset();
1253
+ await ensureAllowance3(signer, underlying, addresses.vault, assets);
1254
+ const tx = await vault["deposit(uint256,address)"](assets, receiver);
1255
+ const receipt = await tx.wait();
1256
+ let shares = 0n;
1257
+ for (const log of receipt.logs) {
1258
+ try {
1259
+ const parsed = vault.interface.parseLog({
1260
+ topics: log.topics,
1261
+ data: log.data
1262
+ });
1263
+ if (parsed && parsed.name === "Transfer" && parsed.args[0] === "0x0000000000000000000000000000000000000000") {
1264
+ shares = parsed.args[2];
1265
+ break;
1266
+ }
1267
+ } catch {
1268
+ }
1269
+ }
1270
+ return { receipt, shares };
1271
+ }
1272
+ async function depositMultiAsset(signer, addresses, tokens, amounts, receiver, minShares) {
1273
+ await validateWalletChain(signer, addresses.hubChainId);
1274
+ for (let i = 0; i < tokens.length; i++) {
1275
+ await ensureAllowance3(signer, tokens[i], addresses.vault, amounts[i]);
1276
+ }
1277
+ const vault = new Contract(addresses.vault, VAULT_ABI, signer);
1278
+ const tx = await vault["deposit(address[],uint256[],address,uint256)"](tokens, amounts, receiver, minShares);
1279
+ const receipt = await tx.wait();
1280
+ let shares = 0n;
1281
+ for (const log of receipt.logs) {
1282
+ try {
1283
+ const parsed = vault.interface.parseLog({
1284
+ topics: log.topics,
1285
+ data: log.data
1286
+ });
1287
+ if (parsed && parsed.name === "Deposit") {
1288
+ shares = parsed.args[4];
1289
+ break;
1290
+ }
1291
+ } catch {
1292
+ }
1293
+ }
1294
+ return { receipt, shares };
1295
+ }
1296
+ var depositCrossChainOracleOn = depositSimple;
1297
+ async function depositAsync(signer, addresses, assets, receiver, lzFee, extraOptions = "0x") {
1298
+ const provider = signer.provider;
1299
+ const escrow = addresses.escrow ?? await new Contract(addresses.vault, ["function getEscrow() view returns (address)"], provider).getEscrow();
1300
+ if (escrow === ZeroAddress) throw new EscrowNotConfiguredError(addresses.vault);
1301
+ await validateWalletChain(signer, addresses.hubChainId);
1302
+ await preflightAsync(provider, addresses.vault);
1303
+ const vault = new Contract(addresses.vault, VAULT_ABI, signer);
1304
+ const underlying = await vault.asset();
1305
+ await ensureAllowance3(signer, underlying, escrow, assets);
1306
+ const coder = AbiCoder.defaultAbiCoder();
1307
+ const actionCallData = coder.encode(
1308
+ ["uint256", "address"],
1309
+ [assets, receiver]
1310
+ );
1311
+ const bridge = new Contract(addresses.vault, BRIDGE_ABI, signer);
1312
+ const guid = await bridge.initVaultActionRequest.staticCall(
1313
+ ActionType.DEPOSIT,
1314
+ actionCallData,
1315
+ 0,
1316
+ extraOptions,
1317
+ { value: lzFee }
1318
+ );
1319
+ const tx = await bridge.initVaultActionRequest(
1320
+ ActionType.DEPOSIT,
1321
+ actionCallData,
1322
+ 0,
1323
+ // amountLimit = 0 for deposits (minAmountOut handled by cross-chain manager)
1324
+ extraOptions,
1325
+ { value: lzFee }
1326
+ );
1327
+ const receipt = await tx.wait();
1328
+ return { receipt, guid };
1329
+ }
1330
+ async function mintAsync(signer, addresses, shares, maxAssets, receiver, lzFee, extraOptions = "0x") {
1331
+ const provider = signer.provider;
1332
+ const escrow = addresses.escrow ?? await new Contract(addresses.vault, ["function getEscrow() view returns (address)"], provider).getEscrow();
1333
+ if (escrow === ZeroAddress) throw new EscrowNotConfiguredError(addresses.vault);
1334
+ await validateWalletChain(signer, addresses.hubChainId);
1335
+ await preflightAsync(provider, addresses.vault);
1336
+ const vault = new Contract(addresses.vault, VAULT_ABI, signer);
1337
+ const underlying = await vault.asset();
1338
+ await ensureAllowance3(signer, underlying, escrow, maxAssets);
1339
+ const coder = AbiCoder.defaultAbiCoder();
1340
+ const actionCallData = coder.encode(
1341
+ ["uint256", "address"],
1342
+ [shares, receiver]
1343
+ );
1344
+ const bridge = new Contract(addresses.vault, BRIDGE_ABI, signer);
1345
+ const guid = await bridge.initVaultActionRequest.staticCall(
1346
+ ActionType.MINT,
1347
+ actionCallData,
1348
+ maxAssets,
1349
+ extraOptions,
1350
+ { value: lzFee }
1351
+ );
1352
+ const tx = await bridge.initVaultActionRequest(
1353
+ ActionType.MINT,
1354
+ actionCallData,
1355
+ maxAssets,
697
1356
  extraOptions,
698
- composeMsg: composeMsgBytes,
699
- oftCmd: "0x"
700
- };
701
- const oft = new Contract(spokeOFT, OFT_ABI, provider);
702
- const fee = await oft.quoteSend(sendParam, false);
703
- return fee.nativeFee;
1357
+ { value: lzFee }
1358
+ );
1359
+ const receipt = await tx.wait();
1360
+ return { receipt, guid };
704
1361
  }
705
- async function quoteComposeFee(provider, vault, spokeEid, receiver) {
706
- try {
707
- const vaultContract = new Contract(vault, BRIDGE_ABI, provider);
708
- const readFee = await vaultContract.quoteAccountingFee("0x");
709
- let shareSendFee = 0n;
710
- if (spokeEid && receiver) {
711
- try {
712
- const COMPOSER_ABI = ["function SHARE_OFT() view returns (address)"];
713
- const FACTORY_ABI = ["function vaultComposer(address _vault) view returns (address)"];
714
- const factory = new Contract("0x7bDB8B17604b03125eFAED33cA0c55FBf856BB0C", FACTORY_ABI, provider);
715
- const composerAddr = await factory.vaultComposer(vault);
716
- const composer = new Contract(composerAddr, COMPOSER_ABI, provider);
717
- const shareOftAddr = await composer.SHARE_OFT();
718
- const shareOft = new Contract(shareOftAddr, OFT_ABI, provider);
719
- const receiverBytes32 = zeroPadValue(receiver, 32);
720
- const fee = await shareOft.quoteSend({
721
- dstEid: spokeEid,
722
- to: receiverBytes32,
723
- amountLD: 1000000n,
724
- minAmountLD: 0n,
725
- extraOptions: "0x",
726
- composeMsg: "0x",
727
- oftCmd: "0x"
728
- }, false);
729
- shareSendFee = fee.nativeFee;
730
- } catch {
731
- }
732
- }
733
- return (readFee + shareSendFee) * 110n / 100n;
734
- } catch {
735
- return 500000000000000n;
1362
+ async function smartDeposit(signer, provider, addresses, assets, receiver, extraOptions = "0x") {
1363
+ const vault = addresses.vault;
1364
+ const status = await getVaultStatus(provider, vault);
1365
+ if (status.mode === "paused") {
1366
+ throw new VaultPausedError(vault);
1367
+ }
1368
+ if (status.mode === "full") {
1369
+ throw new CapacityFullError(vault);
1370
+ }
1371
+ if (status.recommendedDepositFlow === "depositAsync") {
1372
+ const lzFee = await quoteLzFee(provider, vault, extraOptions);
1373
+ return depositAsync(signer, addresses, assets, receiver, lzFee, extraOptions);
736
1374
  }
1375
+ return depositSimple(signer, addresses, assets, receiver);
737
1376
  }
738
- async function executeCompose(signer, from, to, guid, message, fee, index = 0) {
739
- const endpoint = new Contract(LZ_ENDPOINT, LZ_ENDPOINT_ABI, signer);
740
- const hash = await endpoint.composeQueue(from, to, guid, index);
741
- if (hash === EMPTY_HASH) {
742
- throw new Error("Compose not found in queue (hash = 0). Never sent or wrong parameters.");
1377
+ var OMNI_FACTORY_ADDRESS = "0x7bDB8B17604b03125eFAED33cA0c55FBf856BB0C";
1378
+ var FACTORY_ABI = [
1379
+ "function localEid() view returns (uint32)",
1380
+ "function isCrossChainVault(uint32 __eid, address _vault) view returns (bool)",
1381
+ "function hubToSpokes(uint32 __eid, address _hubVault) view returns (uint32[] eids, address[] vaults)",
1382
+ "function spokeToHub(uint32 __eid, address _spokeVault) view returns (uint32 eid, address vault)"
1383
+ ];
1384
+ var DISCOVERY_CHAIN_IDS = Object.values(CHAIN_IDS).filter(
1385
+ (id) => id !== 545
1386
+ // exclude testnet
1387
+ );
1388
+ async function getVaultTopology(provider, vault, factoryAddress = OMNI_FACTORY_ADDRESS) {
1389
+ const factory = new Contract(factoryAddress, FACTORY_ABI, provider);
1390
+ const localEid = Number(await factory.localEid());
1391
+ const isHub = await factory.isCrossChainVault(localEid, vault);
1392
+ if (isHub) {
1393
+ const result = await factory.hubToSpokes(localEid, vault);
1394
+ const spokeEids = result[0];
1395
+ const localChainId2 = EID_TO_CHAIN_ID[localEid] ?? 0;
1396
+ const spokeChainIds = spokeEids.map((eid) => EID_TO_CHAIN_ID[Number(eid)]).filter((id) => id !== void 0);
1397
+ return { role: "hub", hubChainId: localChainId2, spokeChainIds };
743
1398
  }
744
- if (hash === RECEIVED_HASH) {
745
- throw new Error("Compose already delivered \u2014 no action needed.");
1399
+ const spokeResult = await factory.spokeToHub(localEid, vault);
1400
+ const hubEid = Number(spokeResult[0]);
1401
+ const hubVault = spokeResult[1];
1402
+ if (hubEid !== 0 && hubVault !== "0x0000000000000000000000000000000000000000") {
1403
+ const hubChainId = EID_TO_CHAIN_ID[hubEid] ?? 0;
1404
+ const spokeChainIds = [];
1405
+ const localChainId2 = EID_TO_CHAIN_ID[localEid];
1406
+ if (localChainId2 !== void 0) spokeChainIds.push(localChainId2);
1407
+ return { role: "spoke", hubChainId, spokeChainIds };
746
1408
  }
747
- const tx = await endpoint.lzCompose(from, to, guid, index, message, "0x", {
748
- value: fee,
749
- gasLimit: 5000000n
750
- });
751
- const receipt = await tx.wait();
752
- return { receipt };
1409
+ const localChainId = EID_TO_CHAIN_ID[localEid] ?? 0;
1410
+ return { role: "local", hubChainId: localChainId, spokeChainIds: [] };
1411
+ }
1412
+ async function getFullVaultTopology(hubChainProvider, vault, factoryAddress = OMNI_FACTORY_ADDRESS) {
1413
+ const topo = await getVaultTopology(hubChainProvider, vault, factoryAddress);
1414
+ if (topo.role !== "hub") {
1415
+ throw new Error(
1416
+ `getFullVaultTopology: provider must be connected to the hub chain (${topo.hubChainId}), but got role="${topo.role}". Connect to chainId ${topo.hubChainId} instead.`
1417
+ );
1418
+ }
1419
+ return topo;
1420
+ }
1421
+ async function discoverVaultTopology(vault, provider, factoryAddress = OMNI_FACTORY_ADDRESS) {
1422
+ let triedChainId;
1423
+ if (provider) {
1424
+ try {
1425
+ const topo = await getVaultTopology(provider, vault, factoryAddress);
1426
+ if (topo.role !== "local") {
1427
+ if (topo.role === "spoke") {
1428
+ const hubProvider = createChainProvider(topo.hubChainId);
1429
+ if (hubProvider) {
1430
+ try {
1431
+ return await getFullVaultTopology(hubProvider, vault, factoryAddress);
1432
+ } catch {
1433
+ }
1434
+ }
1435
+ }
1436
+ return topo;
1437
+ }
1438
+ const network = await provider.getNetwork();
1439
+ triedChainId = Number(network.chainId);
1440
+ } catch {
1441
+ }
1442
+ }
1443
+ for (const chainId of DISCOVERY_CHAIN_IDS) {
1444
+ if (chainId === triedChainId) continue;
1445
+ const chainProvider = createChainProvider(chainId);
1446
+ if (!chainProvider) continue;
1447
+ try {
1448
+ const topo = await getVaultTopology(chainProvider, vault, factoryAddress);
1449
+ if (topo.role === "hub") return topo;
1450
+ if (topo.role === "spoke") {
1451
+ const hubProvider = createChainProvider(topo.hubChainId);
1452
+ if (hubProvider) {
1453
+ try {
1454
+ return await getFullVaultTopology(hubProvider, vault, factoryAddress);
1455
+ } catch {
1456
+ return topo;
1457
+ }
1458
+ }
1459
+ return topo;
1460
+ }
1461
+ } catch {
1462
+ }
1463
+ }
1464
+ return { role: "local", hubChainId: 0, spokeChainIds: [] };
753
1465
  }
1466
+ function isOnHubChain(currentChainId, topology) {
1467
+ return currentChainId === topology.hubChainId;
1468
+ }
1469
+ function getAllVaultChainIds(topology) {
1470
+ return [topology.hubChainId, ...topology.spokeChainIds];
1471
+ }
1472
+
1473
+ // src/ethers/redeemFlows.ts
754
1474
  async function ensureAllowance4(signer, token, spender, amount) {
755
1475
  const owner = await signer.getAddress();
756
1476
  const erc20 = new Contract(token, ERC20_ABI, signer);
@@ -884,6 +1604,83 @@ async function bridgeAssetsToSpoke(signer, assetOFT, spokeChainEid, amount, rece
884
1604
  const receipt = await tx.wait();
885
1605
  return { receipt };
886
1606
  }
1607
+ var FACTORY_COMPOSER_ABI_RF = [
1608
+ "function vaultComposer(address _vault) view returns (address)"
1609
+ ];
1610
+ var REDEEM_COMPOSER_ABI = [
1611
+ "function SHARE_OFT() view returns (address)"
1612
+ ];
1613
+ var OFT_PEERS_ABI_RF = [
1614
+ "function peers(uint32 eid) view returns (bytes32)"
1615
+ ];
1616
+ async function quoteShareBridgeFee(spokeProvider, shareOFT, hubChainEid, amountLD, receiver) {
1617
+ const toBytes32 = zeroPadValue(receiver, 32);
1618
+ const sendParam = {
1619
+ dstEid: hubChainEid,
1620
+ to: toBytes32,
1621
+ amountLD,
1622
+ minAmountLD: amountLD,
1623
+ extraOptions: "0x",
1624
+ composeMsg: "0x",
1625
+ oftCmd: "0x"
1626
+ };
1627
+ const oft = new Contract(shareOFT, OFT_ABI, spokeProvider);
1628
+ const fee = await oft.quoteSend(sendParam, false);
1629
+ return fee.nativeFee;
1630
+ }
1631
+ async function resolveRedeemAddresses(hubProvider, vault, hubChainId, spokeChainId) {
1632
+ const hubEid = CHAIN_ID_TO_EID[hubChainId];
1633
+ const spokeEid = CHAIN_ID_TO_EID[spokeChainId];
1634
+ if (!hubEid || !spokeEid) {
1635
+ throw new Error(`No LZ EID for chainId ${!hubEid ? hubChainId : spokeChainId}`);
1636
+ }
1637
+ const vaultContract = new Contract(vault, VAULT_ABI, hubProvider);
1638
+ const factory = new Contract(OMNI_FACTORY_ADDRESS, FACTORY_COMPOSER_ABI_RF, hubProvider);
1639
+ const [hubAsset, composerAddress] = await Promise.all([
1640
+ vaultContract.asset(),
1641
+ factory.vaultComposer(vault)
1642
+ ]);
1643
+ if (composerAddress === ZeroAddress) {
1644
+ throw new Error(`[MoreVaults] No composer registered for vault ${vault} on hub chain ${hubChainId}`);
1645
+ }
1646
+ const composer = new Contract(composerAddress, REDEEM_COMPOSER_ABI, hubProvider);
1647
+ const hubShareOft = await composer.SHARE_OFT();
1648
+ const hubShareOftContract = new Contract(hubShareOft, OFT_PEERS_ABI_RF, hubProvider);
1649
+ const spokeShareOftBytes32 = await hubShareOftContract.peers(spokeEid);
1650
+ const spokeShareOft = `0x${spokeShareOftBytes32.slice(-40)}`;
1651
+ let hubAssetOft = null;
1652
+ let spokeAsset = null;
1653
+ let symbol = "";
1654
+ for (const [sym, chainMap] of Object.entries(OFT_ROUTES)) {
1655
+ const hubEntry = chainMap[hubChainId];
1656
+ const spokeEntry = chainMap[spokeChainId];
1657
+ if (!hubEntry || !spokeEntry) continue;
1658
+ if (hubEntry.token.toLowerCase() === hubAsset.toLowerCase()) {
1659
+ hubAssetOft = hubEntry.oft;
1660
+ spokeAsset = spokeEntry.token;
1661
+ symbol = sym;
1662
+ break;
1663
+ }
1664
+ }
1665
+ if (!hubAssetOft || !spokeAsset) {
1666
+ throw new Error(
1667
+ `[MoreVaults] No OFT route found for vault asset ${hubAsset} between hub chain ${hubChainId} and spoke chain ${spokeChainId}`
1668
+ );
1669
+ }
1670
+ const isStargate = await detectStargateOft(hubProvider, hubAssetOft);
1671
+ return {
1672
+ hubChainId,
1673
+ spokeChainId,
1674
+ hubEid,
1675
+ spokeEid,
1676
+ hubAsset,
1677
+ spokeShareOft,
1678
+ hubAssetOft,
1679
+ spokeAsset,
1680
+ isStargate,
1681
+ symbol
1682
+ };
1683
+ }
887
1684
  var MULTICALL3_ADDRESS2 = "0xcA11bde05977b3631167028862bE2a173976CA11";
888
1685
  var MULTICALL3_ABI2 = [
889
1686
  "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
@@ -953,11 +1750,11 @@ async function canDeposit(provider, vault, user) {
953
1750
  return { allowed: true, reason: "ok" };
954
1751
  }
955
1752
  async function getVaultMetadata(provider, vault) {
956
- const MULTICALL3_ADDRESS3 = "0xcA11bde05977b3631167028862bE2a173976CA11";
957
- const MULTICALL3_ABI3 = [
1753
+ const MULTICALL3_ADDRESS4 = "0xcA11bde05977b3631167028862bE2a173976CA11";
1754
+ const MULTICALL3_ABI4 = [
958
1755
  "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
959
1756
  ];
960
- const mc = new Contract(MULTICALL3_ADDRESS3, MULTICALL3_ABI3, provider);
1757
+ const mc = new Contract(MULTICALL3_ADDRESS4, MULTICALL3_ABI4, provider);
961
1758
  const metaIface = new Interface(METADATA_ABI);
962
1759
  const vaultIface = new Interface(VAULT_ABI);
963
1760
  const b1Calls = [
@@ -1094,6 +1891,644 @@ async function getVaultSummary(provider, vault) {
1094
1891
  ]);
1095
1892
  return { ...status, ...metadata };
1096
1893
  }
1894
+ var FACTORY_COMPOSER_ABI_UH = [
1895
+ "function vaultComposer(address _vault) view returns (address)"
1896
+ ];
1897
+ var COMPOSER_SHARE_OFT_ABI_UH = [
1898
+ "function SHARE_OFT() view returns (address)"
1899
+ ];
1900
+ var OFT_PEERS_ABI_UH = [
1901
+ "function peers(uint32 eid) view returns (bytes32)"
1902
+ ];
1903
+ async function getUserPositionMultiChain(vault, user) {
1904
+ const topo = await discoverVaultTopology(vault);
1905
+ const hubProvider = createChainProvider(topo.hubChainId);
1906
+ if (!hubProvider) throw new Error(`No public RPC for hub chainId ${topo.hubChainId}`);
1907
+ const mc = new Contract(MULTICALL3_ADDRESS2, MULTICALL3_ABI2, hubProvider);
1908
+ const vaultIface = new Interface(VAULT_ABI);
1909
+ const decimalsIface = new Interface(["function decimals() view returns (uint8)"]);
1910
+ const b1Calls = [
1911
+ { target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("balanceOf", [user]) },
1912
+ { target: vault, allowFailure: false, callData: decimalsIface.encodeFunctionData("decimals") },
1913
+ { target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("getWithdrawalRequest", [user]) }
1914
+ ];
1915
+ const [b1Raw, block] = await Promise.all([
1916
+ mc.aggregate3.staticCall(b1Calls),
1917
+ hubProvider.getBlock("latest")
1918
+ ]);
1919
+ const hubShares = vaultIface.decodeFunctionResult("balanceOf", b1Raw[0].returnData)[0];
1920
+ const decimals = Number(decimalsIface.decodeFunctionResult("decimals", b1Raw[1].returnData)[0]);
1921
+ const withdrawalResult = vaultIface.decodeFunctionResult("getWithdrawalRequest", b1Raw[2].returnData);
1922
+ const withdrawShares = withdrawalResult[0];
1923
+ const timelockEndsAt = withdrawalResult[1];
1924
+ const spokeShares = {};
1925
+ const rawSpokeShares = {};
1926
+ if (topo.spokeChainIds.length > 0) {
1927
+ let hubShareOft = null;
1928
+ try {
1929
+ const factory = new Contract(OMNI_FACTORY_ADDRESS, FACTORY_COMPOSER_ABI_UH, hubProvider);
1930
+ const composerAddress = await factory.vaultComposer(vault);
1931
+ if (composerAddress !== "0x0000000000000000000000000000000000000000") {
1932
+ const composer = new Contract(composerAddress, COMPOSER_SHARE_OFT_ABI_UH, hubProvider);
1933
+ hubShareOft = await composer.SHARE_OFT();
1934
+ }
1935
+ } catch {
1936
+ }
1937
+ if (hubShareOft) {
1938
+ const hubShareOftContract = new Contract(hubShareOft, OFT_PEERS_ABI_UH, hubProvider);
1939
+ const spokePromises = topo.spokeChainIds.map(async (spokeChainId) => {
1940
+ try {
1941
+ const spokeEid = CHAIN_ID_TO_EID[spokeChainId];
1942
+ if (!spokeEid) return { chainId: spokeChainId, balance: 0n, rawBalance: 0n };
1943
+ const spokeOftBytes32 = await hubShareOftContract.peers(spokeEid);
1944
+ const spokeOft = `0x${spokeOftBytes32.slice(-40)}`;
1945
+ if (spokeOft === "0x0000000000000000000000000000000000000000") {
1946
+ return { chainId: spokeChainId, balance: 0n, rawBalance: 0n };
1947
+ }
1948
+ const spokeProvider = createChainProvider(spokeChainId);
1949
+ if (!spokeProvider) return { chainId: spokeChainId, balance: 0n, rawBalance: 0n };
1950
+ const spokeMc = new Contract(MULTICALL3_ADDRESS2, MULTICALL3_ABI2, spokeProvider);
1951
+ const erc20Iface = new Interface(ERC20_ABI);
1952
+ const spokeDecimalsIface = new Interface(["function decimals() view returns (uint8)"]);
1953
+ const spokeCalls = [
1954
+ { target: spokeOft, allowFailure: false, callData: erc20Iface.encodeFunctionData("balanceOf", [user]) },
1955
+ { target: spokeOft, allowFailure: false, callData: spokeDecimalsIface.encodeFunctionData("decimals") }
1956
+ ];
1957
+ const spokeRaw = await spokeMc.aggregate3.staticCall(spokeCalls);
1958
+ const rawBalance = erc20Iface.decodeFunctionResult("balanceOf", spokeRaw[0].returnData)[0];
1959
+ const spokeOftDecimals = Number(spokeDecimalsIface.decodeFunctionResult("decimals", spokeRaw[1].returnData)[0]);
1960
+ let balance;
1961
+ if (spokeOftDecimals > decimals) {
1962
+ balance = rawBalance / 10n ** BigInt(spokeOftDecimals - decimals);
1963
+ } else if (spokeOftDecimals < decimals) {
1964
+ balance = rawBalance * 10n ** BigInt(decimals - spokeOftDecimals);
1965
+ } else {
1966
+ balance = rawBalance;
1967
+ }
1968
+ return { chainId: spokeChainId, balance, rawBalance };
1969
+ } catch {
1970
+ return { chainId: spokeChainId, balance: 0n, rawBalance: 0n };
1971
+ }
1972
+ });
1973
+ const results = await Promise.all(spokePromises);
1974
+ for (const { chainId, balance, rawBalance } of results) {
1975
+ spokeShares[chainId] = balance;
1976
+ rawSpokeShares[chainId] = rawBalance;
1977
+ }
1978
+ }
1979
+ }
1980
+ const totalSpokeShares = Object.values(spokeShares).reduce((sum, b) => sum + b, 0n);
1981
+ const totalShares = hubShares + totalSpokeShares;
1982
+ const oneShare = 10n ** BigInt(decimals);
1983
+ const vaultContract = new Contract(vault, VAULT_ABI, hubProvider);
1984
+ const [estimatedAssets, sharePrice] = await Promise.all([
1985
+ totalShares === 0n ? Promise.resolve(0n) : vaultContract.convertToAssets(totalShares),
1986
+ vaultContract.convertToAssets(oneShare)
1987
+ ]);
1988
+ const currentTimestamp = BigInt(block?.timestamp ?? 0);
1989
+ const pendingWithdrawal = withdrawShares === 0n ? null : {
1990
+ shares: withdrawShares,
1991
+ timelockEndsAt,
1992
+ canRedeemNow: timelockEndsAt === 0n || currentTimestamp >= timelockEndsAt
1993
+ };
1994
+ return {
1995
+ hubShares,
1996
+ spokeShares,
1997
+ rawSpokeShares,
1998
+ totalShares,
1999
+ estimatedAssets,
2000
+ sharePrice,
2001
+ decimals,
2002
+ pendingWithdrawal
2003
+ };
2004
+ }
2005
+ var MULTICALL3_ADDRESS3 = "0xcA11bde05977b3631167028862bE2a173976CA11";
2006
+ var MULTICALL3_ABI3 = [
2007
+ "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
2008
+ ];
2009
+ async function getCuratorVaultStatus(provider, vault) {
2010
+ const mc = new Contract(MULTICALL3_ADDRESS3, MULTICALL3_ABI3, provider);
2011
+ const curatorConfigIface = new Interface(CURATOR_CONFIG_ABI);
2012
+ const multicallIface = new Interface(MULTICALL_ABI);
2013
+ const calls = [
2014
+ { target: vault, allowFailure: false, callData: curatorConfigIface.encodeFunctionData("curator") },
2015
+ { target: vault, allowFailure: false, callData: curatorConfigIface.encodeFunctionData("timeLockPeriod") },
2016
+ { target: vault, allowFailure: false, callData: curatorConfigIface.encodeFunctionData("getMaxSlippagePercent") },
2017
+ { target: vault, allowFailure: false, callData: multicallIface.encodeFunctionData("getCurrentNonce") },
2018
+ { target: vault, allowFailure: false, callData: curatorConfigIface.encodeFunctionData("getAvailableAssets") },
2019
+ { target: vault, allowFailure: false, callData: curatorConfigIface.encodeFunctionData("getCrossChainAccountingManager") },
2020
+ { target: vault, allowFailure: false, callData: curatorConfigIface.encodeFunctionData("paused") }
2021
+ ];
2022
+ const results = await mc.aggregate3.staticCall(calls);
2023
+ const curator = curatorConfigIface.decodeFunctionResult("curator", results[0].returnData)[0];
2024
+ const timeLockPeriod = curatorConfigIface.decodeFunctionResult("timeLockPeriod", results[1].returnData)[0];
2025
+ const maxSlippagePercent = curatorConfigIface.decodeFunctionResult("getMaxSlippagePercent", results[2].returnData)[0];
2026
+ const currentNonce = multicallIface.decodeFunctionResult("getCurrentNonce", results[3].returnData)[0];
2027
+ const availableAssets = curatorConfigIface.decodeFunctionResult("getAvailableAssets", results[4].returnData)[0];
2028
+ const lzAdapter = curatorConfigIface.decodeFunctionResult("getCrossChainAccountingManager", results[5].returnData)[0];
2029
+ const paused = curatorConfigIface.decodeFunctionResult("paused", results[6].returnData)[0];
2030
+ return {
2031
+ curator,
2032
+ timeLockPeriod,
2033
+ maxSlippagePercent,
2034
+ currentNonce,
2035
+ availableAssets,
2036
+ lzAdapter,
2037
+ paused
2038
+ };
2039
+ }
2040
+ async function getPendingActions(provider, vault, nonce) {
2041
+ const multicallContract = new Contract(vault, MULTICALL_ABI, provider);
2042
+ const [result, block] = await Promise.all([
2043
+ multicallContract.getPendingActions(nonce),
2044
+ provider.getBlock("latest")
2045
+ ]);
2046
+ const [actionsData, pendingUntil] = result;
2047
+ const currentTimestamp = BigInt(block.timestamp);
2048
+ const isExecutable = pendingUntil > 0n && currentTimestamp >= pendingUntil;
2049
+ return {
2050
+ nonce,
2051
+ actionsData,
2052
+ pendingUntil,
2053
+ isExecutable
2054
+ };
2055
+ }
2056
+ async function isCurator(provider, vault, address) {
2057
+ const config = new Contract(vault, CURATOR_CONFIG_ABI, provider);
2058
+ const curatorAddress = await config.curator();
2059
+ return curatorAddress.toLowerCase() === address.toLowerCase();
2060
+ }
2061
+ async function getVaultAnalysis(provider, vault) {
2062
+ const analysisContract = new Contract(vault, VAULT_ANALYSIS_ABI, provider);
2063
+ const [availableRaw, depositableRaw, depositWhitelistEnabled, registryResult] = await Promise.all([
2064
+ analysisContract.getAvailableAssets(),
2065
+ analysisContract.getDepositableAssets(),
2066
+ analysisContract.isDepositWhitelistEnabled(),
2067
+ analysisContract.moreVaultsRegistry().catch(() => null)
2068
+ ]);
2069
+ const availableAddresses = availableRaw;
2070
+ const depositableAddresses = depositableRaw;
2071
+ const allAddresses = Array.from(/* @__PURE__ */ new Set([...availableAddresses, ...depositableAddresses]));
2072
+ const assetInfoMap = /* @__PURE__ */ new Map();
2073
+ if (allAddresses.length > 0) {
2074
+ const mc = new Contract(MULTICALL3_ADDRESS3, MULTICALL3_ABI3, provider);
2075
+ const metaIface = new Interface(METADATA_ABI);
2076
+ const metadataCalls = allAddresses.flatMap((addr) => [
2077
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("name") },
2078
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("symbol") },
2079
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("decimals") }
2080
+ ]);
2081
+ const metadataResults = await mc.aggregate3.staticCall(metadataCalls);
2082
+ for (let i = 0; i < allAddresses.length; i++) {
2083
+ const addr = allAddresses[i];
2084
+ const nameRes = metadataResults[i * 3];
2085
+ const symbolRes = metadataResults[i * 3 + 1];
2086
+ const decimalsRes = metadataResults[i * 3 + 2];
2087
+ const name = nameRes.success ? metaIface.decodeFunctionResult("name", nameRes.returnData)[0] : "";
2088
+ const symbol = symbolRes.success ? metaIface.decodeFunctionResult("symbol", symbolRes.returnData)[0] : "";
2089
+ const decimals = decimalsRes.success ? Number(metaIface.decodeFunctionResult("decimals", decimalsRes.returnData)[0]) : 18;
2090
+ assetInfoMap.set(addr.toLowerCase(), { address: addr, name, symbol, decimals });
2091
+ }
2092
+ }
2093
+ const registryAddress = registryResult ? registryResult : null;
2094
+ return {
2095
+ availableAssets: availableAddresses.map((a) => assetInfoMap.get(a.toLowerCase())),
2096
+ depositableAssets: depositableAddresses.map((a) => assetInfoMap.get(a.toLowerCase())),
2097
+ depositWhitelistEnabled,
2098
+ registryAddress
2099
+ };
2100
+ }
2101
+ async function checkProtocolWhitelist(provider, vault, protocols) {
2102
+ const analysisContract = new Contract(vault, VAULT_ANALYSIS_ABI, provider);
2103
+ const registry = await analysisContract.moreVaultsRegistry();
2104
+ if (protocols.length === 0) return {};
2105
+ const mc = new Contract(MULTICALL3_ADDRESS3, MULTICALL3_ABI3, provider);
2106
+ const registryIface = new Interface(REGISTRY_ABI);
2107
+ const calls = protocols.map((protocol) => ({
2108
+ target: registry,
2109
+ allowFailure: true,
2110
+ callData: registryIface.encodeFunctionData("isWhitelisted", [protocol])
2111
+ }));
2112
+ const results = await mc.aggregate3.staticCall(calls);
2113
+ const out = {};
2114
+ for (let i = 0; i < protocols.length; i++) {
2115
+ const r = results[i];
2116
+ const whitelisted = r.success ? registryIface.decodeFunctionResult("isWhitelisted", r.returnData)[0] : false;
2117
+ out[protocols[i].toLowerCase()] = whitelisted;
2118
+ }
2119
+ return out;
2120
+ }
2121
+ async function getVaultAssetBreakdown(provider, vault) {
2122
+ const analysisContract = new Contract(vault, VAULT_ANALYSIS_ABI, provider);
2123
+ const availableRaw = await analysisContract.getAvailableAssets();
2124
+ const addresses = availableRaw;
2125
+ const mc = new Contract(MULTICALL3_ADDRESS3, MULTICALL3_ABI3, provider);
2126
+ const vaultIface = new Interface(VAULT_ABI);
2127
+ const metaIface = new Interface(METADATA_ABI);
2128
+ const erc20Iface = new Interface(ERC20_ABI);
2129
+ const perAssetCalls = addresses.flatMap((addr) => [
2130
+ { target: addr, allowFailure: true, callData: erc20Iface.encodeFunctionData("balanceOf", [vault]) },
2131
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("name") },
2132
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("symbol") },
2133
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("decimals") }
2134
+ ]);
2135
+ const totalCalls = [
2136
+ ...perAssetCalls,
2137
+ { target: vault, allowFailure: true, callData: vaultIface.encodeFunctionData("totalAssets") },
2138
+ { target: vault, allowFailure: true, callData: vaultIface.encodeFunctionData("totalSupply") },
2139
+ { target: vault, allowFailure: true, callData: metaIface.encodeFunctionData("decimals") }
2140
+ ];
2141
+ const results = await mc.aggregate3.staticCall(totalCalls);
2142
+ const perAssetFields = 4;
2143
+ const assets = addresses.map((addr, i) => {
2144
+ const base = i * perAssetFields;
2145
+ const balance = results[base].success ? erc20Iface.decodeFunctionResult("balanceOf", results[base].returnData)[0] : 0n;
2146
+ const name = results[base + 1].success ? metaIface.decodeFunctionResult("name", results[base + 1].returnData)[0] : "";
2147
+ const symbol = results[base + 2].success ? metaIface.decodeFunctionResult("symbol", results[base + 2].returnData)[0] : "";
2148
+ const decimals = results[base + 3].success ? Number(metaIface.decodeFunctionResult("decimals", results[base + 3].returnData)[0]) : 18;
2149
+ return { address: addr, name, symbol, decimals, balance };
2150
+ });
2151
+ const totalsBase = addresses.length * perAssetFields;
2152
+ const totalAssets = results[totalsBase].success ? vaultIface.decodeFunctionResult("totalAssets", results[totalsBase].returnData)[0] : 0n;
2153
+ const totalSupply = results[totalsBase + 1].success ? vaultIface.decodeFunctionResult("totalSupply", results[totalsBase + 1].returnData)[0] : 0n;
2154
+ const underlyingDecimals = results[totalsBase + 2].success ? Number(metaIface.decodeFunctionResult("decimals", results[totalsBase + 2].returnData)[0]) : 6;
2155
+ return { assets, totalAssets, totalSupply, underlyingDecimals };
2156
+ }
2157
+ function encodeCuratorAction(action) {
2158
+ switch (action.type) {
2159
+ case "swap": {
2160
+ const iface = new Interface(DEX_ABI);
2161
+ return iface.encodeFunctionData("executeSwap", [
2162
+ {
2163
+ targetContract: action.params.targetContract,
2164
+ tokenIn: action.params.tokenIn,
2165
+ tokenOut: action.params.tokenOut,
2166
+ maxAmountIn: action.params.maxAmountIn,
2167
+ minAmountOut: action.params.minAmountOut,
2168
+ swapCallData: action.params.swapCallData
2169
+ }
2170
+ ]);
2171
+ }
2172
+ case "batchSwap": {
2173
+ const iface = new Interface(DEX_ABI);
2174
+ return iface.encodeFunctionData("executeBatchSwap", [
2175
+ {
2176
+ swaps: action.params.swaps.map((s) => ({
2177
+ targetContract: s.targetContract,
2178
+ tokenIn: s.tokenIn,
2179
+ tokenOut: s.tokenOut,
2180
+ maxAmountIn: s.maxAmountIn,
2181
+ minAmountOut: s.minAmountOut,
2182
+ swapCallData: s.swapCallData
2183
+ }))
2184
+ }
2185
+ ]);
2186
+ }
2187
+ case "erc4626Deposit": {
2188
+ const iface = new Interface(ERC4626_FACET_ABI);
2189
+ return iface.encodeFunctionData("erc4626Deposit", [action.vault, action.assets]);
2190
+ }
2191
+ case "erc4626Redeem": {
2192
+ const iface = new Interface(ERC4626_FACET_ABI);
2193
+ return iface.encodeFunctionData("erc4626Redeem", [action.vault, action.shares]);
2194
+ }
2195
+ case "erc7540RequestDeposit": {
2196
+ const iface = new Interface(ERC7540_FACET_ABI);
2197
+ return iface.encodeFunctionData("erc7540RequestDeposit", [action.vault, action.assets]);
2198
+ }
2199
+ case "erc7540Deposit": {
2200
+ const iface = new Interface(ERC7540_FACET_ABI);
2201
+ return iface.encodeFunctionData("erc7540Deposit", [action.vault, action.assets]);
2202
+ }
2203
+ case "erc7540RequestRedeem": {
2204
+ const iface = new Interface(ERC7540_FACET_ABI);
2205
+ return iface.encodeFunctionData("erc7540RequestRedeem", [action.vault, action.shares]);
2206
+ }
2207
+ case "erc7540Redeem": {
2208
+ const iface = new Interface(ERC7540_FACET_ABI);
2209
+ return iface.encodeFunctionData("erc7540Redeem", [action.vault, action.shares]);
2210
+ }
2211
+ default: {
2212
+ const _exhaustive = action;
2213
+ throw new Error(`[MoreVaults] Unknown CuratorAction type: ${_exhaustive.type}`);
2214
+ }
2215
+ }
2216
+ }
2217
+ function buildCuratorBatch(actions) {
2218
+ return actions.map(encodeCuratorAction);
2219
+ }
2220
+ async function submitActions(signer, vault, actions) {
2221
+ const multicallContract = new Contract(vault, MULTICALL_ABI, signer);
2222
+ const tx = await multicallContract.submitActions(actions);
2223
+ const receipt = await tx.wait();
2224
+ const nextNonce = await multicallContract.getCurrentNonce();
2225
+ const nonce = nextNonce - 1n;
2226
+ return { receipt, nonce };
2227
+ }
2228
+ async function executeActions(signer, vault, nonce) {
2229
+ const multicallContract = new Contract(vault, MULTICALL_ABI, signer);
2230
+ const tx = await multicallContract.executeActions(nonce);
2231
+ return tx.wait();
2232
+ }
2233
+ async function vetoActions(signer, vault, nonces) {
2234
+ const multicallContract = new Contract(vault, MULTICALL_ABI, signer);
2235
+ const tx = await multicallContract.vetoActions(nonces);
2236
+ return tx.wait();
2237
+ }
2238
+ var UNISWAP_V3_SWAP_ROUTER_ABI = [
2239
+ "function exactInputSingle(tuple(address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountOut)"
2240
+ ];
2241
+ var UNISWAP_V3_SWAP_ROUTER02_ABI = [
2242
+ "function exactInputSingle(tuple(address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountOut)"
2243
+ ];
2244
+ var SWAP_ROUTER02_CHAINS = /* @__PURE__ */ new Set([8453]);
2245
+ function encodeUniswapV3SwapCalldata(params) {
2246
+ const { chainId, tokenIn, tokenOut, fee, amountIn, minAmountOut, recipient } = params;
2247
+ const router = UNISWAP_V3_ROUTERS[chainId];
2248
+ if (!router) {
2249
+ throw new Error(
2250
+ `[MoreVaults] No Uniswap V3 router configured for chainId ${chainId}. Supported chains: ${Object.keys(UNISWAP_V3_ROUTERS).join(", ")}`
2251
+ );
2252
+ }
2253
+ let swapCallData;
2254
+ if (SWAP_ROUTER02_CHAINS.has(chainId)) {
2255
+ const iface = new Interface(UNISWAP_V3_SWAP_ROUTER02_ABI);
2256
+ swapCallData = iface.encodeFunctionData("exactInputSingle", [
2257
+ {
2258
+ tokenIn,
2259
+ tokenOut,
2260
+ fee,
2261
+ recipient,
2262
+ amountIn,
2263
+ amountOutMinimum: minAmountOut,
2264
+ sqrtPriceLimitX96: 0n
2265
+ }
2266
+ ]);
2267
+ } else {
2268
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + 1200);
2269
+ const iface = new Interface(UNISWAP_V3_SWAP_ROUTER_ABI);
2270
+ swapCallData = iface.encodeFunctionData("exactInputSingle", [
2271
+ {
2272
+ tokenIn,
2273
+ tokenOut,
2274
+ fee,
2275
+ recipient,
2276
+ deadline,
2277
+ amountIn,
2278
+ amountOutMinimum: minAmountOut,
2279
+ sqrtPriceLimitX96: 0n
2280
+ }
2281
+ ]);
2282
+ }
2283
+ return { targetContract: router, swapCallData };
2284
+ }
2285
+ function buildUniswapV3Swap(params) {
2286
+ const { targetContract, swapCallData } = encodeUniswapV3SwapCalldata(params);
2287
+ return {
2288
+ type: "swap",
2289
+ params: {
2290
+ targetContract,
2291
+ tokenIn: params.tokenIn,
2292
+ tokenOut: params.tokenOut,
2293
+ maxAmountIn: params.amountIn,
2294
+ minAmountOut: params.minAmountOut,
2295
+ swapCallData
2296
+ }
2297
+ };
2298
+ }
2299
+
2300
+ // src/ethers/distribution.ts
2301
+ async function getVaultDistribution(hubProvider, vault, spokeProviders) {
2302
+ const hubStatus = await getVaultStatus(hubProvider, vault);
2303
+ const hubChainId = Number((await hubProvider.getNetwork()).chainId);
2304
+ const hubTotalAssets = hubStatus.totalAssets;
2305
+ const hubLiquidBalance = hubStatus.hubLiquidBalance;
2306
+ const hubStrategyBalance = hubTotalAssets > hubLiquidBalance ? hubTotalAssets - hubLiquidBalance : 0n;
2307
+ const spokeEntries = Object.entries(spokeProviders).map(([chainIdStr, provider]) => ({
2308
+ chainId: Number(chainIdStr),
2309
+ provider
2310
+ }));
2311
+ const spokeBalances = await Promise.all(
2312
+ spokeEntries.map(async ({ chainId, provider }) => {
2313
+ try {
2314
+ const spokeStatus = await getVaultStatus(provider, vault);
2315
+ return { chainId, totalAssets: spokeStatus.totalAssets, isReachable: true };
2316
+ } catch {
2317
+ return { chainId, totalAssets: 0n, isReachable: false };
2318
+ }
2319
+ })
2320
+ );
2321
+ const reachableSpokeSum = spokeBalances.filter((s) => s.isReachable).reduce((acc, s) => acc + s.totalAssets, 0n);
2322
+ const totalActual = hubTotalAssets + reachableSpokeSum;
2323
+ return {
2324
+ hubChainId,
2325
+ hubLiquidBalance,
2326
+ hubStrategyBalance,
2327
+ hubTotalAssets,
2328
+ spokesDeployedBalance: hubStatus.spokesDeployedBalance,
2329
+ spokeBalances,
2330
+ totalActual,
2331
+ oracleAccountingEnabled: hubStatus.oracleAccountingEnabled
2332
+ };
2333
+ }
2334
+ async function getVaultDistributionWithTopology(hubProvider, vault) {
2335
+ const [hubStatus, topology] = await Promise.all([
2336
+ getVaultStatus(hubProvider, vault),
2337
+ getVaultTopology(hubProvider, vault)
2338
+ ]);
2339
+ const hubChainId = Number((await hubProvider.getNetwork()).chainId);
2340
+ const hubTotalAssets = hubStatus.totalAssets;
2341
+ const hubLiquidBalance = hubStatus.hubLiquidBalance;
2342
+ const hubStrategyBalance = hubTotalAssets > hubLiquidBalance ? hubTotalAssets - hubLiquidBalance : 0n;
2343
+ return {
2344
+ hubChainId,
2345
+ hubLiquidBalance,
2346
+ hubStrategyBalance,
2347
+ hubTotalAssets,
2348
+ spokesDeployedBalance: hubStatus.spokesDeployedBalance,
2349
+ spokeBalances: [],
2350
+ totalActual: hubTotalAssets,
2351
+ // hub-only, no spoke data
2352
+ oracleAccountingEnabled: hubStatus.oracleAccountingEnabled,
2353
+ spokeChainIds: topology.spokeChainIds
2354
+ };
2355
+ }
2356
+ var NATIVE_SYMBOL = {
2357
+ 1: "ETH",
2358
+ 10: "ETH",
2359
+ 42161: "ETH",
2360
+ 8453: "ETH",
2361
+ 747: "FLOW",
2362
+ 146: "S",
2363
+ 56: "BNB"
2364
+ };
2365
+ var SYMBOL_ABI = [
2366
+ "function symbol() view returns (string)"
2367
+ ];
2368
+ async function readTokenSymbol(provider, token, fallbackSymbol) {
2369
+ if (!provider) return fallbackSymbol;
2370
+ try {
2371
+ const contract = new Contract(token, SYMBOL_ABI, provider);
2372
+ return await contract.symbol();
2373
+ } catch {
2374
+ return fallbackSymbol;
2375
+ }
2376
+ }
2377
+ async function getInboundRoutes(hubChainId, vault, vaultAsset, userAddress) {
2378
+ const hubEid = CHAIN_ID_TO_EID[hubChainId];
2379
+ if (!hubEid) throw new Error(`No LZ EID for hub chainId ${hubChainId}`);
2380
+ const hubProvider = createChainProvider(hubChainId);
2381
+ if (!hubProvider) throw new Error(`No public RPC for hub chainId ${hubChainId}`);
2382
+ const topology = await getVaultTopology(hubProvider, vault);
2383
+ const registeredSpokes = new Set(topology.spokeChainIds);
2384
+ const results = [];
2385
+ const vaultAssetNorm = vaultAsset.toLowerCase();
2386
+ for (const [symbol, chainMap] of Object.entries(OFT_ROUTES)) {
2387
+ const hubEntry = chainMap[hubChainId];
2388
+ if (!hubEntry) continue;
2389
+ if (hubEntry.token.toLowerCase() !== vaultAssetNorm) continue;
2390
+ const oftCmd = "0x";
2391
+ const spokesToCheck = Object.keys(chainMap).map(Number).filter((id) => id !== hubChainId && registeredSpokes.has(id));
2392
+ await Promise.allSettled(
2393
+ spokesToCheck.map(async (spokeChainId) => {
2394
+ const spokeEntry = chainMap[spokeChainId];
2395
+ if (!spokeEntry) return;
2396
+ const spokeProvider = createChainProvider(spokeChainId);
2397
+ if (!spokeProvider) return;
2398
+ try {
2399
+ const receiverBytes32 = "0x" + userAddress.replace(/^0x/, "").toLowerCase().padStart(64, "0");
2400
+ const spokeOft = new Contract(spokeEntry.oft, OFT_ABI, spokeProvider);
2401
+ const [feeResult, sourceTokenSymbol] = await Promise.all([
2402
+ spokeOft.quoteSend(
2403
+ {
2404
+ dstEid: hubEid,
2405
+ to: receiverBytes32,
2406
+ amountLD: 1000000n,
2407
+ minAmountLD: 0n,
2408
+ extraOptions: "0x",
2409
+ composeMsg: "0x",
2410
+ oftCmd
2411
+ },
2412
+ false
2413
+ ),
2414
+ readTokenSymbol(spokeProvider, spokeEntry.token, symbol)
2415
+ ]);
2416
+ results.push({
2417
+ symbol,
2418
+ spokeChainId,
2419
+ depositType: "oft-compose",
2420
+ spokeOft: spokeEntry.oft,
2421
+ spokeToken: spokeEntry.token,
2422
+ sourceTokenSymbol,
2423
+ hubOft: hubEntry.oft,
2424
+ oftCmd,
2425
+ lzFeeEstimate: feeResult.nativeFee,
2426
+ nativeSymbol: NATIVE_SYMBOL[spokeChainId] ?? "ETH"
2427
+ });
2428
+ } catch {
2429
+ }
2430
+ })
2431
+ );
2432
+ }
2433
+ const [asyncMode, ...hubOftEntries] = await Promise.all([
2434
+ isAsyncMode(hubProvider, vault),
2435
+ ...Object.entries(OFT_ROUTES).map(async ([sym, chainMap]) => {
2436
+ const hubEntry = chainMap[hubChainId];
2437
+ if (!hubEntry || hubEntry.token.toLowerCase() !== vaultAssetNorm) return null;
2438
+ return { symbol: sym, hubEntry };
2439
+ })
2440
+ ]);
2441
+ const hubOftEntry = hubOftEntries.find((e) => e !== null) ?? null;
2442
+ if (hubOftEntry) {
2443
+ const { symbol, hubEntry } = hubOftEntry;
2444
+ const [sourceTokenSymbol, lzFeeEstimate] = await Promise.all([
2445
+ readTokenSymbol(hubProvider, hubEntry.token, symbol),
2446
+ asyncMode ? quoteLzFee(hubProvider, vault) : Promise.resolve(0n)
2447
+ ]);
2448
+ results.unshift({
2449
+ symbol,
2450
+ spokeChainId: hubChainId,
2451
+ depositType: asyncMode ? "direct-async" : "direct",
2452
+ spokeOft: null,
2453
+ spokeToken: hubEntry.token,
2454
+ sourceTokenSymbol,
2455
+ hubOft: null,
2456
+ oftCmd: "0x",
2457
+ lzFeeEstimate,
2458
+ nativeSymbol: NATIVE_SYMBOL[hubChainId] ?? "ETH"
2459
+ });
2460
+ }
2461
+ return results;
2462
+ }
2463
+ async function getUserBalancesForRoutes(routes, userAddress) {
2464
+ return Promise.all(
2465
+ routes.map(async (route) => {
2466
+ const provider = createChainProvider(route.spokeChainId);
2467
+ if (!provider) return { ...route, userBalance: 0n };
2468
+ try {
2469
+ let userBalance;
2470
+ if (route.spokeToken.toLowerCase() === ZeroAddress.toLowerCase()) {
2471
+ userBalance = await provider.getBalance(userAddress);
2472
+ } else {
2473
+ const erc20 = new Contract(route.spokeToken, ERC20_ABI, provider);
2474
+ userBalance = await erc20.balanceOf(userAddress);
2475
+ }
2476
+ return { ...route, userBalance };
2477
+ } catch {
2478
+ return { ...route, userBalance: 0n };
2479
+ }
2480
+ })
2481
+ );
2482
+ }
2483
+ async function getOutboundRoutes(hubChainId, vault) {
2484
+ const hubEid = CHAIN_ID_TO_EID[hubChainId];
2485
+ if (!hubEid) throw new Error(`No LZ EID for hub chainId ${hubChainId}`);
2486
+ const hubProvider = createChainProvider(hubChainId);
2487
+ if (!hubProvider) throw new Error(`No public RPC for hub chainId ${hubChainId}`);
2488
+ const topology = await getVaultTopology(hubProvider, vault);
2489
+ const routes = [
2490
+ {
2491
+ chainId: hubChainId,
2492
+ routeType: "hub",
2493
+ eid: hubEid,
2494
+ nativeSymbol: NATIVE_SYMBOL[hubChainId] ?? "ETH"
2495
+ }
2496
+ ];
2497
+ for (const spokeChainId of topology.spokeChainIds) {
2498
+ const eid = CHAIN_ID_TO_EID[spokeChainId];
2499
+ if (!eid) continue;
2500
+ routes.push({
2501
+ chainId: spokeChainId,
2502
+ routeType: "spoke",
2503
+ eid,
2504
+ nativeSymbol: NATIVE_SYMBOL[spokeChainId] ?? "ETH"
2505
+ });
2506
+ }
2507
+ return routes;
2508
+ }
2509
+ async function quoteRouteDepositFee(route, hubChainId, amount, userAddress) {
2510
+ if (route.depositType === "direct") return 0n;
2511
+ const hubEid = CHAIN_ID_TO_EID[hubChainId];
2512
+ if (!hubEid) throw new Error(`No LZ EID for hub chainId ${hubChainId}`);
2513
+ if (!route.spokeOft) throw new Error("Route is oft-compose but spokeOft is null");
2514
+ const spokeProvider = createChainProvider(route.spokeChainId);
2515
+ if (!spokeProvider) throw new Error(`No public RPC for spoke chainId ${route.spokeChainId}`);
2516
+ const receiverBytes32 = "0x" + userAddress.replace(/^0x/, "").toLowerCase().padStart(64, "0");
2517
+ const spokeOft = new Contract(route.spokeOft, OFT_ABI, spokeProvider);
2518
+ const feeResult = await spokeOft.quoteSend(
2519
+ {
2520
+ dstEid: hubEid,
2521
+ to: receiverBytes32,
2522
+ amountLD: amount,
2523
+ minAmountLD: 0n,
2524
+ extraOptions: "0x",
2525
+ composeMsg: "0x",
2526
+ oftCmd: route.oftCmd
2527
+ },
2528
+ false
2529
+ );
2530
+ return feeResult.nativeFee;
2531
+ }
1097
2532
 
1098
2533
  // src/ethers/wagmiCompat.ts
1099
2534
  function asSdkSigner(signer) {
@@ -1101,6 +2536,6 @@ function asSdkSigner(signer) {
1101
2536
  return signer;
1102
2537
  }
1103
2538
 
1104
- export { ActionType, BRIDGE_ABI, CCManagerNotConfiguredError, CHAIN_IDS, CHAIN_ID_TO_EID, CONFIG_ABI, CapacityFullError, EID_TO_CHAIN_ID, ERC20_ABI, EscrowNotConfiguredError, InsufficientLiquidityError, LZ_EIDS, LZ_ENDPOINT_ABI, LZ_TIMEOUTS, METADATA_ABI, MissingEscrowAddressError, MoreVaultsError, NotHubVaultError, NotWhitelistedError, OFT_ABI, VAULT_ABI, VaultPausedError, WrongChainError, asSdkSigner, bridgeAssetsToSpoke, bridgeSharesToHub, canDeposit, depositAsync, depositCrossChainOracleOn, depositFromSpoke, depositFromSpokeAsync, depositMultiAsset, depositSimple, ensureAllowance, executeCompose, getAsyncRequestStatus, getAsyncRequestStatusLabel, getMaxWithdrawable, getUserBalances, getUserPosition, getVaultMetadata, getVaultStatus, getVaultSummary, getWithdrawalRequest, isAsyncMode, mintAsync, preflightAsync, preflightRedeemLiquidity, preflightSync, previewDeposit, previewRedeem, quoteComposeFee, quoteDepositFromSpokeFee, quoteLzFee, redeemAsync, redeemShares, requestRedeem, smartDeposit, smartRedeem, withdrawAssets };
2539
+ export { ActionType, BRIDGE_ABI, BRIDGE_FACET_ABI, CCManagerNotConfiguredError, CHAIN_IDS, CHAIN_ID_TO_EID, CONFIG_ABI, CURATOR_CONFIG_ABI, CapacityFullError, DEX_ABI, EID_TO_CHAIN_ID, ERC20_ABI, ERC4626_FACET_ABI, ERC7540_FACET_ABI, EscrowNotConfiguredError, InsufficientLiquidityError, LZ_ADAPTER_ABI, LZ_EIDS, LZ_ENDPOINT_ABI, LZ_TIMEOUTS, METADATA_ABI, MULTICALL_ABI, MissingEscrowAddressError, MoreVaultsError, NATIVE_SYMBOL, NotHubVaultError, NotWhitelistedError, OFT_ABI, OFT_ROUTES, OMNI_FACTORY_ADDRESS, REGISTRY_ABI, UNISWAP_V3_ROUTERS, VAULT_ABI, VAULT_ANALYSIS_ABI, VaultPausedError, WrongChainError, asSdkSigner, bridgeAssetsToSpoke, bridgeSharesToHub, buildCuratorBatch, buildUniswapV3Swap, canDeposit, checkProtocolWhitelist, depositAsync, depositCrossChainOracleOn, depositFromSpoke, depositFromSpokeAsync, depositMultiAsset, depositSimple, detectStargateOft, discoverVaultTopology, encodeCuratorAction, encodeUniswapV3SwapCalldata, ensureAllowance, executeActions, executeCompose, getAllVaultChainIds, getAsyncRequestStatus, getAsyncRequestStatusLabel, getCuratorVaultStatus, getFullVaultTopology, getInboundRoutes, getMaxWithdrawable, getOutboundRoutes, getPendingActions, getUserBalances, getUserBalancesForRoutes, getUserPosition, getUserPositionMultiChain, getVaultAnalysis, getVaultAssetBreakdown, getVaultDistribution, getVaultDistributionWithTopology, getVaultMetadata, getVaultStatus, getVaultSummary, getVaultTopology, getWithdrawalRequest, isAsyncMode, isCurator, isOnHubChain, mintAsync, preflightAsync, preflightRedeemLiquidity, preflightSpokeDeposit, preflightSpokeRedeem, preflightSync, previewDeposit, previewRedeem, quoteComposeFee, quoteDepositFromSpokeFee, quoteLzFee, quoteRouteDepositFee, quoteShareBridgeFee, redeemAsync, redeemShares, requestRedeem, resolveRedeemAddresses, smartDeposit, smartRedeem, submitActions, vetoActions, waitForCompose, withdrawAssets };
1105
2540
  //# sourceMappingURL=index.js.map
1106
2541
  //# sourceMappingURL=index.js.map