@oydual31/more-vaults-sdk 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -175,6 +175,10 @@ All three modules expose the same logical features. Choose based on your stack.
175
175
  | `buildUniswapV3Swap`, `encodeUniswapV3SwapCalldata` | Yes | Yes | — |
176
176
  | `quoteCuratorBridgeFee`, `executeCuratorBridge` | Yes | Yes | `useCuratorBridgeQuote`, `useExecuteBridge` |
177
177
  | `findBridgeRoute`, `encodeBridgeParams` | Yes | Yes | — |
178
+ | `getSubVaultPositions`, `getVaultPortfolio` | Yes | Yes | `useSubVaultPositions`, `useVaultPortfolio` |
179
+ | `getSubVaultInfo`, `detectSubVaultType` | Yes | Yes | — |
180
+ | `getERC7540RequestStatus` | Yes | Yes | `useERC7540RequestStatus` |
181
+ | `previewSubVaultDeposit`, `previewSubVaultRedeem` | Yes | Yes | — |
178
182
  | `detectStargateOft` | Yes | Yes | — |
179
183
  | `preflightSync`, `preflightAsync` | Yes | Yes | — |
180
184
  | `preflightSpokeDeposit`, `preflightSpokeRedeem` | Yes | Yes | — |
@@ -717,6 +721,49 @@ const txHash = await executeCuratorBridge(
717
721
  )
718
722
  ```
719
723
 
724
+ ### Sub-vault operations
725
+
726
+ Curators invest vault assets into ERC4626/ERC7540 sub-vaults (Aave, Morpho, etc.) to generate yield.
727
+
728
+ ```ts
729
+ import {
730
+ getSubVaultPositions,
731
+ getVaultPortfolio,
732
+ getSubVaultInfo,
733
+ detectSubVaultType,
734
+ getERC7540RequestStatus,
735
+ previewSubVaultDeposit,
736
+ } from '@oydual31/more-vaults-sdk/viem'
737
+
738
+ // Full portfolio: liquid assets + sub-vault positions
739
+ const portfolio = await getVaultPortfolio(publicClient, VAULT)
740
+ // portfolio.liquidAssets — AssetBalance[] (tokens held directly)
741
+ // portfolio.subVaultPositions — SubVaultPosition[] (shares + underlying value)
742
+ // portfolio.totalValue — total in vault underlying units
743
+ // portfolio.lockedAssets — locked in pending ERC7540 requests
744
+
745
+ // Active sub-vault positions with current values
746
+ const positions = await getSubVaultPositions(publicClient, VAULT)
747
+ for (const p of positions) {
748
+ console.log(`${p.symbol}: ${p.sharesBalance} shares = ${p.underlyingValue} ${p.underlyingSymbol}`)
749
+ }
750
+
751
+ // Analyze a target sub-vault before investing
752
+ const info = await getSubVaultInfo(publicClient, VAULT, MORPHO_VAULT)
753
+ // info.type — 'erc4626' or 'erc7540'
754
+ // info.maxDeposit — capacity remaining
755
+ // info.isWhitelisted — must be true to invest
756
+
757
+ // Preview: how many shares would a 1000 USDC deposit yield?
758
+ const shares = await previewSubVaultDeposit(publicClient, MORPHO_VAULT, parseUnits('1000', 6))
759
+
760
+ // For ERC7540 async sub-vaults: check if requests are ready
761
+ const status = await getERC7540RequestStatus(publicClient, VAULT, ASYNC_VAULT)
762
+ if (status.canFinalizeDeposit) {
763
+ // Curator can now call erc7540Deposit via submitActions
764
+ }
765
+ ```
766
+
720
767
  ---
721
768
 
722
769
  ## Vault topology & distribution
@@ -893,6 +940,9 @@ Import from `@oydual31/more-vaults-sdk/react`. Requires wagmi v2 + @tanstack/rea
893
940
  | `useVetoActions()` | Guardian: cancel pending actions |
894
941
  | `useCuratorBridgeQuote()` | Quote LayerZero fee for curator bridge |
895
942
  | `useExecuteBridge()` | Execute curator bridge operation |
943
+ | `useSubVaultPositions()` | Active sub-vault positions with values |
944
+ | `useVaultPortfolio()` | Full portfolio: liquid + deployed + locked |
945
+ | `useERC7540RequestStatus()` | Pending/claimable ERC7540 request status |
896
946
 
897
947
  ### React example
898
948
 
@@ -188,6 +188,98 @@ interface VaultAssetBreakdown {
188
188
  /** Vault underlying token decimals */
189
189
  underlyingDecimals: number;
190
190
  }
191
+ /**
192
+ * A single active sub-vault position held by the curator vault.
193
+ * Covers both ERC4626 (synchronous) and ERC7540 (asynchronous) sub-vaults.
194
+ */
195
+ interface SubVaultPosition {
196
+ /** Sub-vault contract address */
197
+ address: Address;
198
+ /** Protocol type of the sub-vault */
199
+ type: 'erc4626' | 'erc7540';
200
+ /** Name of the sub-vault share token */
201
+ name: string;
202
+ /** Symbol of the sub-vault share token */
203
+ symbol: string;
204
+ /** Decimals of the sub-vault share token */
205
+ decimals: number;
206
+ /** Shares of the sub-vault held by the curator vault */
207
+ sharesBalance: bigint;
208
+ /** Current value of the shares in terms of the sub-vault's underlying asset */
209
+ underlyingValue: bigint;
210
+ /** Underlying asset address of the sub-vault */
211
+ underlyingAsset: Address;
212
+ /** Symbol of the sub-vault's underlying asset */
213
+ underlyingSymbol: string;
214
+ /** Decimals of the sub-vault's underlying asset */
215
+ underlyingDecimals: number;
216
+ }
217
+ /**
218
+ * Metadata and capability snapshot for a potential sub-vault investment target.
219
+ */
220
+ interface SubVaultInfo {
221
+ /** Sub-vault contract address */
222
+ address: Address;
223
+ /** Protocol type: ERC4626 (sync) or ERC7540 (async) */
224
+ type: 'erc4626' | 'erc7540';
225
+ /** Sub-vault share token name */
226
+ name: string;
227
+ /** Sub-vault share token symbol */
228
+ symbol: string;
229
+ /** Sub-vault share token decimals */
230
+ decimals: number;
231
+ /** Underlying asset address */
232
+ underlyingAsset: Address;
233
+ /** Underlying asset symbol */
234
+ underlyingSymbol: string;
235
+ /** Underlying asset decimals */
236
+ underlyingDecimals: number;
237
+ /** Maximum amount the curator vault can deposit (from maxDeposit(vault)) */
238
+ maxDeposit: bigint;
239
+ /** Whether the sub-vault is whitelisted in the global MoreVaults registry */
240
+ isWhitelisted: boolean;
241
+ }
242
+ /**
243
+ * Status of pending and claimable ERC7540 async requests for a sub-vault.
244
+ * Uses requestId = 0 (the standard default for non-batch ERC7540 vaults).
245
+ */
246
+ interface ERC7540RequestStatus {
247
+ /** Sub-vault address these statuses belong to */
248
+ subVault: Address;
249
+ /** Assets in a pending deposit request (not yet claimable) */
250
+ pendingDeposit: bigint;
251
+ /** Assets ready to be claimed/finalized as shares */
252
+ claimableDeposit: bigint;
253
+ /** Shares in a pending redeem request (not yet claimable) */
254
+ pendingRedeem: bigint;
255
+ /** Assets ready to be claimed after redeem fulfillment */
256
+ claimableRedeem: bigint;
257
+ /** True if claimableDeposit > 0 — vault can call erc7540Deposit to finalize */
258
+ canFinalizeDeposit: boolean;
259
+ /** True if claimableRedeem > 0 — vault can call erc7540Redeem to finalize */
260
+ canFinalizeRedeem: boolean;
261
+ }
262
+ /**
263
+ * Full portfolio view for a curator vault combining liquid and invested assets.
264
+ */
265
+ interface VaultPortfolio {
266
+ /** Liquid ERC20 asset balances held directly by the vault (excludes sub-vault share tokens) */
267
+ liquidAssets: AssetBalance[];
268
+ /** Active positions in ERC4626/ERC7540 sub-vaults */
269
+ subVaultPositions: SubVaultPosition[];
270
+ /**
271
+ * Approximate total value in underlying terms:
272
+ * liquid underlying balance + sum of sub-vault convertToAssets values.
273
+ * For the authoritative total, use `totalAssets` from the vault contract.
274
+ */
275
+ totalValue: bigint;
276
+ /** totalAssets() from the vault — authoritative AUM figure */
277
+ totalAssets: bigint;
278
+ /** totalSupply() of vault shares */
279
+ totalSupply: bigint;
280
+ /** Assets locked in pending ERC7540 requests (lockedTokensAmountOfAsset) */
281
+ lockedAssets: bigint;
282
+ }
191
283
 
192
284
  /**
193
285
  * Wait for a transaction receipt with generous timeout and retry logic.
@@ -947,4 +1039,4 @@ declare function quoteCuratorBridgeFee(publicClient: PublicClient, vault: Addres
947
1039
  */
948
1040
  declare function executeCuratorBridge(walletClient: WalletClient, publicClient: PublicClient, vault: Address, token: Address, params: CuratorBridgeParams): Promise<Hash>;
949
1041
 
950
- export { getInboundRoutes as $, type AsyncRequestResult as A, type BatchSwapParams as B, type ComposeData as C, type DepositResult as D, type VaultStatus as E, type VaultSummary as F, type VaultTopology as G, canDeposit as H, type InboundRoute as I, detectStargateOft as J, discoverVaultTopology as K, encodeBridgeParams as L, type MaxWithdrawable as M, NATIVE_SYMBOL as N, OMNI_FACTORY_ADDRESS as O, type PendingAction as P, ensureAllowance as Q, type RedeemResult as R, type SpokeDepositResult as S, executeCuratorBridge as T, type UserBalances as U, type VaultAddresses as V, findBridgeRoute as W, getAllVaultChainIds as X, getAsyncRequestStatus as Y, getAsyncRequestStatusLabel as Z, getFullVaultTopology as _, type CuratorVaultStatus as a, getMaxWithdrawable as a0, getOutboundRoutes as a1, getUserBalances as a2, getUserBalancesForRoutes as a3, getUserPosition as a4, getUserPositionMultiChain as a5, getVaultDistribution as a6, getVaultDistributionWithTopology as a7, getVaultMetadata as a8, getVaultStatus as a9, getVaultSummary as aa, getVaultTopology as ab, isAsyncMode as ac, isOnHubChain as ad, previewDeposit as ae, previewRedeem as af, quoteCuratorBridgeFee as ag, quoteLzFee as ah, quoteRouteDepositFee as ai, waitForAsyncRequest as aj, waitForTx as ak, type VaultAnalysis as b, type VaultAssetBreakdown as c, type CuratorAction as d, type SubmitActionsResult as e, ActionType as f, type ActionTypeValue as g, type AssetBalance as h, type AssetInfo as i, type AsyncRequestFinalResult as j, type AsyncRequestStatus as k, type AsyncRequestStatusInfo as l, type BridgeParams as m, type CrossChainRequestInfo as n, type CuratorBridgeParams as o, type DepositBlockReason as p, type DepositEligibility as q, type InboundRouteWithBalance as r, type MultiChainUserPosition as s, type OutboundRoute as t, type SpokeBalance as u, type SwapParams as v, type UserPosition as w, type VaultDistribution as x, type VaultMetadata as y, type VaultMode as z };
1042
+ export { getAllVaultChainIds as $, type AsyncRequestResult as A, type BatchSwapParams as B, type ComposeData as C, type DepositResult as D, type ERC7540RequestStatus as E, type VaultDistribution as F, type VaultMetadata as G, type VaultMode as H, type InboundRoute as I, type VaultStatus as J, type VaultSummary as K, type VaultTopology as L, type MaxWithdrawable as M, NATIVE_SYMBOL as N, OMNI_FACTORY_ADDRESS as O, type PendingAction as P, canDeposit as Q, type RedeemResult as R, type SpokeDepositResult as S, detectStargateOft as T, type UserBalances as U, type VaultAddresses as V, discoverVaultTopology as W, encodeBridgeParams as X, ensureAllowance as Y, executeCuratorBridge as Z, findBridgeRoute as _, type CuratorVaultStatus as a, getAsyncRequestStatus as a0, getAsyncRequestStatusLabel as a1, getFullVaultTopology as a2, getInboundRoutes as a3, getMaxWithdrawable as a4, getOutboundRoutes as a5, getUserBalances as a6, getUserBalancesForRoutes as a7, getUserPosition as a8, getUserPositionMultiChain as a9, getVaultDistribution as aa, getVaultDistributionWithTopology as ab, getVaultMetadata as ac, getVaultStatus as ad, getVaultSummary as ae, getVaultTopology as af, isAsyncMode as ag, isOnHubChain as ah, previewDeposit as ai, previewRedeem as aj, quoteCuratorBridgeFee as ak, quoteLzFee as al, quoteRouteDepositFee as am, waitForAsyncRequest as an, waitForTx as ao, type VaultAnalysis as b, type VaultAssetBreakdown as c, type CuratorAction as d, type SubmitActionsResult as e, type SubVaultInfo as f, type SubVaultPosition as g, type VaultPortfolio as h, ActionType as i, type ActionTypeValue as j, type AssetBalance as k, type AssetInfo as l, type AsyncRequestFinalResult as m, type AsyncRequestStatus as n, type AsyncRequestStatusInfo as o, type BridgeParams as p, type CrossChainRequestInfo as q, type CuratorBridgeParams as r, type DepositBlockReason as s, type DepositEligibility as t, type InboundRouteWithBalance as u, type MultiChainUserPosition as v, type OutboundRoute as w, type SpokeBalance as x, type SwapParams as y, type UserPosition as z };
@@ -188,6 +188,98 @@ interface VaultAssetBreakdown {
188
188
  /** Vault underlying token decimals */
189
189
  underlyingDecimals: number;
190
190
  }
191
+ /**
192
+ * A single active sub-vault position held by the curator vault.
193
+ * Covers both ERC4626 (synchronous) and ERC7540 (asynchronous) sub-vaults.
194
+ */
195
+ interface SubVaultPosition {
196
+ /** Sub-vault contract address */
197
+ address: Address;
198
+ /** Protocol type of the sub-vault */
199
+ type: 'erc4626' | 'erc7540';
200
+ /** Name of the sub-vault share token */
201
+ name: string;
202
+ /** Symbol of the sub-vault share token */
203
+ symbol: string;
204
+ /** Decimals of the sub-vault share token */
205
+ decimals: number;
206
+ /** Shares of the sub-vault held by the curator vault */
207
+ sharesBalance: bigint;
208
+ /** Current value of the shares in terms of the sub-vault's underlying asset */
209
+ underlyingValue: bigint;
210
+ /** Underlying asset address of the sub-vault */
211
+ underlyingAsset: Address;
212
+ /** Symbol of the sub-vault's underlying asset */
213
+ underlyingSymbol: string;
214
+ /** Decimals of the sub-vault's underlying asset */
215
+ underlyingDecimals: number;
216
+ }
217
+ /**
218
+ * Metadata and capability snapshot for a potential sub-vault investment target.
219
+ */
220
+ interface SubVaultInfo {
221
+ /** Sub-vault contract address */
222
+ address: Address;
223
+ /** Protocol type: ERC4626 (sync) or ERC7540 (async) */
224
+ type: 'erc4626' | 'erc7540';
225
+ /** Sub-vault share token name */
226
+ name: string;
227
+ /** Sub-vault share token symbol */
228
+ symbol: string;
229
+ /** Sub-vault share token decimals */
230
+ decimals: number;
231
+ /** Underlying asset address */
232
+ underlyingAsset: Address;
233
+ /** Underlying asset symbol */
234
+ underlyingSymbol: string;
235
+ /** Underlying asset decimals */
236
+ underlyingDecimals: number;
237
+ /** Maximum amount the curator vault can deposit (from maxDeposit(vault)) */
238
+ maxDeposit: bigint;
239
+ /** Whether the sub-vault is whitelisted in the global MoreVaults registry */
240
+ isWhitelisted: boolean;
241
+ }
242
+ /**
243
+ * Status of pending and claimable ERC7540 async requests for a sub-vault.
244
+ * Uses requestId = 0 (the standard default for non-batch ERC7540 vaults).
245
+ */
246
+ interface ERC7540RequestStatus {
247
+ /** Sub-vault address these statuses belong to */
248
+ subVault: Address;
249
+ /** Assets in a pending deposit request (not yet claimable) */
250
+ pendingDeposit: bigint;
251
+ /** Assets ready to be claimed/finalized as shares */
252
+ claimableDeposit: bigint;
253
+ /** Shares in a pending redeem request (not yet claimable) */
254
+ pendingRedeem: bigint;
255
+ /** Assets ready to be claimed after redeem fulfillment */
256
+ claimableRedeem: bigint;
257
+ /** True if claimableDeposit > 0 — vault can call erc7540Deposit to finalize */
258
+ canFinalizeDeposit: boolean;
259
+ /** True if claimableRedeem > 0 — vault can call erc7540Redeem to finalize */
260
+ canFinalizeRedeem: boolean;
261
+ }
262
+ /**
263
+ * Full portfolio view for a curator vault combining liquid and invested assets.
264
+ */
265
+ interface VaultPortfolio {
266
+ /** Liquid ERC20 asset balances held directly by the vault (excludes sub-vault share tokens) */
267
+ liquidAssets: AssetBalance[];
268
+ /** Active positions in ERC4626/ERC7540 sub-vaults */
269
+ subVaultPositions: SubVaultPosition[];
270
+ /**
271
+ * Approximate total value in underlying terms:
272
+ * liquid underlying balance + sum of sub-vault convertToAssets values.
273
+ * For the authoritative total, use `totalAssets` from the vault contract.
274
+ */
275
+ totalValue: bigint;
276
+ /** totalAssets() from the vault — authoritative AUM figure */
277
+ totalAssets: bigint;
278
+ /** totalSupply() of vault shares */
279
+ totalSupply: bigint;
280
+ /** Assets locked in pending ERC7540 requests (lockedTokensAmountOfAsset) */
281
+ lockedAssets: bigint;
282
+ }
191
283
 
192
284
  /**
193
285
  * Wait for a transaction receipt with generous timeout and retry logic.
@@ -947,4 +1039,4 @@ declare function quoteCuratorBridgeFee(publicClient: PublicClient, vault: Addres
947
1039
  */
948
1040
  declare function executeCuratorBridge(walletClient: WalletClient, publicClient: PublicClient, vault: Address, token: Address, params: CuratorBridgeParams): Promise<Hash>;
949
1041
 
950
- export { getInboundRoutes as $, type AsyncRequestResult as A, type BatchSwapParams as B, type ComposeData as C, type DepositResult as D, type VaultStatus as E, type VaultSummary as F, type VaultTopology as G, canDeposit as H, type InboundRoute as I, detectStargateOft as J, discoverVaultTopology as K, encodeBridgeParams as L, type MaxWithdrawable as M, NATIVE_SYMBOL as N, OMNI_FACTORY_ADDRESS as O, type PendingAction as P, ensureAllowance as Q, type RedeemResult as R, type SpokeDepositResult as S, executeCuratorBridge as T, type UserBalances as U, type VaultAddresses as V, findBridgeRoute as W, getAllVaultChainIds as X, getAsyncRequestStatus as Y, getAsyncRequestStatusLabel as Z, getFullVaultTopology as _, type CuratorVaultStatus as a, getMaxWithdrawable as a0, getOutboundRoutes as a1, getUserBalances as a2, getUserBalancesForRoutes as a3, getUserPosition as a4, getUserPositionMultiChain as a5, getVaultDistribution as a6, getVaultDistributionWithTopology as a7, getVaultMetadata as a8, getVaultStatus as a9, getVaultSummary as aa, getVaultTopology as ab, isAsyncMode as ac, isOnHubChain as ad, previewDeposit as ae, previewRedeem as af, quoteCuratorBridgeFee as ag, quoteLzFee as ah, quoteRouteDepositFee as ai, waitForAsyncRequest as aj, waitForTx as ak, type VaultAnalysis as b, type VaultAssetBreakdown as c, type CuratorAction as d, type SubmitActionsResult as e, ActionType as f, type ActionTypeValue as g, type AssetBalance as h, type AssetInfo as i, type AsyncRequestFinalResult as j, type AsyncRequestStatus as k, type AsyncRequestStatusInfo as l, type BridgeParams as m, type CrossChainRequestInfo as n, type CuratorBridgeParams as o, type DepositBlockReason as p, type DepositEligibility as q, type InboundRouteWithBalance as r, type MultiChainUserPosition as s, type OutboundRoute as t, type SpokeBalance as u, type SwapParams as v, type UserPosition as w, type VaultDistribution as x, type VaultMetadata as y, type VaultMode as z };
1042
+ export { getAllVaultChainIds as $, type AsyncRequestResult as A, type BatchSwapParams as B, type ComposeData as C, type DepositResult as D, type ERC7540RequestStatus as E, type VaultDistribution as F, type VaultMetadata as G, type VaultMode as H, type InboundRoute as I, type VaultStatus as J, type VaultSummary as K, type VaultTopology as L, type MaxWithdrawable as M, NATIVE_SYMBOL as N, OMNI_FACTORY_ADDRESS as O, type PendingAction as P, canDeposit as Q, type RedeemResult as R, type SpokeDepositResult as S, detectStargateOft as T, type UserBalances as U, type VaultAddresses as V, discoverVaultTopology as W, encodeBridgeParams as X, ensureAllowance as Y, executeCuratorBridge as Z, findBridgeRoute as _, type CuratorVaultStatus as a, getAsyncRequestStatus as a0, getAsyncRequestStatusLabel as a1, getFullVaultTopology as a2, getInboundRoutes as a3, getMaxWithdrawable as a4, getOutboundRoutes as a5, getUserBalances as a6, getUserBalancesForRoutes as a7, getUserPosition as a8, getUserPositionMultiChain as a9, getVaultDistribution as aa, getVaultDistributionWithTopology as ab, getVaultMetadata as ac, getVaultStatus as ad, getVaultSummary as ae, getVaultTopology as af, isAsyncMode as ag, isOnHubChain as ah, previewDeposit as ai, previewRedeem as aj, quoteCuratorBridgeFee as ak, quoteLzFee as al, quoteRouteDepositFee as am, waitForAsyncRequest as an, waitForTx as ao, type VaultAnalysis as b, type VaultAssetBreakdown as c, type CuratorAction as d, type SubmitActionsResult as e, type SubVaultInfo as f, type SubVaultPosition as g, type VaultPortfolio as h, ActionType as i, type ActionTypeValue as j, type AssetBalance as k, type AssetInfo as l, type AsyncRequestFinalResult as m, type AsyncRequestStatus as n, type AsyncRequestStatusInfo as o, type BridgeParams as p, type CrossChainRequestInfo as q, type CuratorBridgeParams as r, type DepositBlockReason as s, type DepositEligibility as t, type InboundRouteWithBalance as u, type MultiChainUserPosition as v, type OutboundRoute as w, type SpokeBalance as x, type SwapParams as y, type UserPosition as z };
@@ -513,6 +513,23 @@ var REGISTRY_ABI = [
513
513
  "function isBridgeAllowed(address bridge) view returns (bool)",
514
514
  "function getAllowedFacets() view returns (address[])"
515
515
  ];
516
+ var SUB_VAULT_ABI = [
517
+ // ConfigurationFacet reads — called on the MoreVaults diamond proxy
518
+ "function tokensHeld(bytes32 id) view returns (address[])",
519
+ "function lockedTokensAmountOfAsset(address asset) view returns (uint256)",
520
+ // ERC4626 standard reads — called on the sub-vault contract
521
+ "function convertToAssets(uint256 shares) view returns (uint256)",
522
+ "function convertToShares(uint256 assets) view returns (uint256)",
523
+ "function previewDeposit(uint256 assets) view returns (uint256)",
524
+ "function previewRedeem(uint256 shares) view returns (uint256)",
525
+ "function maxDeposit(address receiver) view returns (uint256)",
526
+ "function maxRedeem(address owner) view returns (uint256)",
527
+ // ERC7540 async reads — called on the sub-vault contract
528
+ "function pendingDepositRequest(uint256 requestId, address controller) view returns (uint256)",
529
+ "function claimableDepositRequest(uint256 requestId, address controller) view returns (uint256)",
530
+ "function pendingRedeemRequest(uint256 requestId, address controller) view returns (uint256)",
531
+ "function claimableRedeemRequest(uint256 requestId, address controller) view returns (uint256)"
532
+ ];
516
533
 
517
534
  // src/ethers/errors.ts
518
535
  var MoreVaultsError = class extends Error {
@@ -1752,11 +1769,11 @@ async function canDeposit(provider, vault, user) {
1752
1769
  return { allowed: true, reason: "ok" };
1753
1770
  }
1754
1771
  async function getVaultMetadata(provider, vault) {
1755
- const MULTICALL3_ADDRESS4 = "0xcA11bde05977b3631167028862bE2a173976CA11";
1756
- const MULTICALL3_ABI4 = [
1772
+ const MULTICALL3_ADDRESS5 = "0xcA11bde05977b3631167028862bE2a173976CA11";
1773
+ const MULTICALL3_ABI5 = [
1757
1774
  "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
1758
1775
  ];
1759
- const mc = new ethers.Contract(MULTICALL3_ADDRESS4, MULTICALL3_ABI4, provider);
1776
+ const mc = new ethers.Contract(MULTICALL3_ADDRESS5, MULTICALL3_ABI5, provider);
1760
1777
  const metaIface = new ethers.Interface(METADATA_ABI);
1761
1778
  const vaultIface = new ethers.Interface(VAULT_ABI);
1762
1779
  const b1Calls = [
@@ -2367,6 +2384,233 @@ async function executeCuratorBridge(signer, vault, token, params) {
2367
2384
  );
2368
2385
  return tx.wait();
2369
2386
  }
2387
+ var MULTICALL3_ADDRESS4 = "0xcA11bde05977b3631167028862bE2a173976CA11";
2388
+ var MULTICALL3_ABI4 = [
2389
+ "function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
2390
+ ];
2391
+ var ERC4626_ID = ethers.ethers.keccak256(ethers.ethers.toUtf8Bytes("ERC4626_ID"));
2392
+ var ERC7540_ID = ethers.ethers.keccak256(ethers.ethers.toUtf8Bytes("ERC7540_ID"));
2393
+ async function getSubVaultPositions(provider, vault) {
2394
+ const subVaultIface = new ethers.Interface(SUB_VAULT_ABI);
2395
+ const vaultContract = new ethers.Contract(vault, SUB_VAULT_ABI, provider);
2396
+ const [erc4626Raw, erc7540Raw] = await Promise.all([
2397
+ vaultContract.tokensHeld(ERC4626_ID).catch(() => []),
2398
+ vaultContract.tokensHeld(ERC7540_ID).catch(() => [])
2399
+ ]);
2400
+ const allSubVaults = [
2401
+ ...erc4626Raw.map((a) => ({ address: a, type: "erc4626" })),
2402
+ ...erc7540Raw.map((a) => ({ address: a, type: "erc7540" }))
2403
+ ];
2404
+ if (allSubVaults.length === 0) return [];
2405
+ const mc = new ethers.Contract(MULTICALL3_ADDRESS4, MULTICALL3_ABI4, provider);
2406
+ const erc20Iface = new ethers.Interface(ERC20_ABI);
2407
+ const metaIface = new ethers.Interface(METADATA_ABI);
2408
+ const vaultIface = new ethers.Interface(VAULT_ABI);
2409
+ const PER_SV = 5;
2410
+ const subVaultCalls = allSubVaults.flatMap(({ address: sv }) => [
2411
+ { target: sv, allowFailure: true, callData: erc20Iface.encodeFunctionData("balanceOf", [vault]) },
2412
+ { target: sv, allowFailure: true, callData: vaultIface.encodeFunctionData("asset") },
2413
+ { target: sv, allowFailure: true, callData: metaIface.encodeFunctionData("name") },
2414
+ { target: sv, allowFailure: true, callData: metaIface.encodeFunctionData("symbol") },
2415
+ { target: sv, allowFailure: true, callData: metaIface.encodeFunctionData("decimals") }
2416
+ ]);
2417
+ const subVaultResults = await mc.aggregate3.staticCall(subVaultCalls);
2418
+ const partials = allSubVaults.map(({ address: sv, type }, i) => {
2419
+ const base = i * PER_SV;
2420
+ const sharesBalance = subVaultResults[base].success ? erc20Iface.decodeFunctionResult("balanceOf", subVaultResults[base].returnData)[0] : 0n;
2421
+ const underlyingAsset = subVaultResults[base + 1].success ? vaultIface.decodeFunctionResult("asset", subVaultResults[base + 1].returnData)[0] : ethers.ethers.ZeroAddress;
2422
+ const name = subVaultResults[base + 2].success ? metaIface.decodeFunctionResult("name", subVaultResults[base + 2].returnData)[0] : "";
2423
+ const symbol = subVaultResults[base + 3].success ? metaIface.decodeFunctionResult("symbol", subVaultResults[base + 3].returnData)[0] : "";
2424
+ const decimals = subVaultResults[base + 4].success ? Number(metaIface.decodeFunctionResult("decimals", subVaultResults[base + 4].returnData)[0]) : 18;
2425
+ return { address: sv, type, sharesBalance, underlyingAsset, name, symbol, decimals };
2426
+ });
2427
+ const active = partials.filter((p) => p.sharesBalance > 0n);
2428
+ if (active.length === 0) return [];
2429
+ const PER_ACTIVE = 4;
2430
+ const activeCalls = active.flatMap(({ address: sv, sharesBalance, underlyingAsset }) => [
2431
+ { target: sv, allowFailure: true, callData: subVaultIface.encodeFunctionData("convertToAssets", [sharesBalance]) },
2432
+ { target: underlyingAsset, allowFailure: true, callData: metaIface.encodeFunctionData("name") },
2433
+ { target: underlyingAsset, allowFailure: true, callData: metaIface.encodeFunctionData("symbol") },
2434
+ { target: underlyingAsset, allowFailure: true, callData: metaIface.encodeFunctionData("decimals") }
2435
+ ]);
2436
+ const activeResults = await mc.aggregate3.staticCall(activeCalls);
2437
+ return active.map((p, i) => {
2438
+ const base = i * PER_ACTIVE;
2439
+ const underlyingValue = activeResults[base].success ? subVaultIface.decodeFunctionResult("convertToAssets", activeResults[base].returnData)[0] : 0n;
2440
+ const underlyingSymbol = activeResults[base + 2].success ? metaIface.decodeFunctionResult("symbol", activeResults[base + 2].returnData)[0] : "";
2441
+ const underlyingDecimals = activeResults[base + 3].success ? Number(metaIface.decodeFunctionResult("decimals", activeResults[base + 3].returnData)[0]) : 18;
2442
+ return {
2443
+ address: p.address,
2444
+ type: p.type,
2445
+ name: p.name,
2446
+ symbol: p.symbol,
2447
+ decimals: p.decimals,
2448
+ sharesBalance: p.sharesBalance,
2449
+ underlyingValue,
2450
+ underlyingAsset: p.underlyingAsset,
2451
+ underlyingSymbol,
2452
+ underlyingDecimals
2453
+ };
2454
+ });
2455
+ }
2456
+ async function detectSubVaultType(provider, subVault) {
2457
+ const mc = new ethers.Contract(MULTICALL3_ADDRESS4, MULTICALL3_ABI4, provider);
2458
+ const subVaultIface = new ethers.Interface(SUB_VAULT_ABI);
2459
+ const calls = [
2460
+ {
2461
+ target: subVault,
2462
+ allowFailure: true,
2463
+ callData: subVaultIface.encodeFunctionData("pendingDepositRequest", [0n, ethers.ethers.ZeroAddress])
2464
+ },
2465
+ {
2466
+ target: subVault,
2467
+ allowFailure: true,
2468
+ callData: subVaultIface.encodeFunctionData("convertToAssets", [0n])
2469
+ }
2470
+ ];
2471
+ const results = await mc.aggregate3.staticCall(calls);
2472
+ if (results[0].success) return "erc7540";
2473
+ if (results[1].success) return "erc4626";
2474
+ return null;
2475
+ }
2476
+ async function getSubVaultInfo(provider, vault, subVault) {
2477
+ const mc = new ethers.Contract(MULTICALL3_ADDRESS4, MULTICALL3_ABI4, provider);
2478
+ const subVaultIface = new ethers.Interface(SUB_VAULT_ABI);
2479
+ const metaIface = new ethers.Interface(METADATA_ABI);
2480
+ const vaultIface = new ethers.Interface(VAULT_ABI);
2481
+ new ethers.Interface(REGISTRY_ABI);
2482
+ new ethers.Interface(VAULT_ANALYSIS_ABI);
2483
+ const [type, basicResults] = await Promise.all([
2484
+ detectSubVaultType(provider, subVault),
2485
+ mc.aggregate3.staticCall([
2486
+ { target: subVault, allowFailure: true, callData: metaIface.encodeFunctionData("name") },
2487
+ { target: subVault, allowFailure: true, callData: metaIface.encodeFunctionData("symbol") },
2488
+ { target: subVault, allowFailure: true, callData: metaIface.encodeFunctionData("decimals") },
2489
+ { target: subVault, allowFailure: true, callData: vaultIface.encodeFunctionData("asset") },
2490
+ { target: subVault, allowFailure: true, callData: subVaultIface.encodeFunctionData("maxDeposit", [vault]) }
2491
+ ])
2492
+ ]);
2493
+ const name = basicResults[0].success ? metaIface.decodeFunctionResult("name", basicResults[0].returnData)[0] : "";
2494
+ const symbol = basicResults[1].success ? metaIface.decodeFunctionResult("symbol", basicResults[1].returnData)[0] : "";
2495
+ const decimals = basicResults[2].success ? Number(metaIface.decodeFunctionResult("decimals", basicResults[2].returnData)[0]) : 18;
2496
+ const underlying = basicResults[3].success ? vaultIface.decodeFunctionResult("asset", basicResults[3].returnData)[0] : ethers.ethers.ZeroAddress;
2497
+ const maxDeposit = basicResults[4].success ? subVaultIface.decodeFunctionResult("maxDeposit", basicResults[4].returnData)[0] : 0n;
2498
+ const [underlyingResults, registryRaw] = await Promise.all([
2499
+ mc.aggregate3.staticCall([
2500
+ { target: underlying, allowFailure: true, callData: metaIface.encodeFunctionData("symbol") },
2501
+ { target: underlying, allowFailure: true, callData: metaIface.encodeFunctionData("decimals") }
2502
+ ]),
2503
+ new ethers.Contract(vault, VAULT_ANALYSIS_ABI, provider).moreVaultsRegistry().catch(() => null)
2504
+ ]);
2505
+ const underlyingSymbol = underlyingResults[0].success ? metaIface.decodeFunctionResult("symbol", underlyingResults[0].returnData)[0] : "";
2506
+ const underlyingDecimals = underlyingResults[1].success ? Number(metaIface.decodeFunctionResult("decimals", underlyingResults[1].returnData)[0]) : 18;
2507
+ let isWhitelisted = false;
2508
+ if (registryRaw) {
2509
+ const whitelistResult = await new ethers.Contract(registryRaw, REGISTRY_ABI, provider).isWhitelisted(subVault).catch(() => false);
2510
+ isWhitelisted = whitelistResult;
2511
+ }
2512
+ return {
2513
+ address: subVault,
2514
+ type: type ?? "erc4626",
2515
+ name,
2516
+ symbol,
2517
+ decimals,
2518
+ underlyingAsset: underlying,
2519
+ underlyingSymbol,
2520
+ underlyingDecimals,
2521
+ maxDeposit,
2522
+ isWhitelisted
2523
+ };
2524
+ }
2525
+ async function getERC7540RequestStatus(provider, vault, subVault) {
2526
+ const mc = new ethers.Contract(MULTICALL3_ADDRESS4, MULTICALL3_ABI4, provider);
2527
+ const subVaultIface = new ethers.Interface(SUB_VAULT_ABI);
2528
+ const calls = [
2529
+ { target: subVault, allowFailure: true, callData: subVaultIface.encodeFunctionData("pendingDepositRequest", [0n, vault]) },
2530
+ { target: subVault, allowFailure: true, callData: subVaultIface.encodeFunctionData("claimableDepositRequest", [0n, vault]) },
2531
+ { target: subVault, allowFailure: true, callData: subVaultIface.encodeFunctionData("pendingRedeemRequest", [0n, vault]) },
2532
+ { target: subVault, allowFailure: true, callData: subVaultIface.encodeFunctionData("claimableRedeemRequest", [0n, vault]) }
2533
+ ];
2534
+ const results = await mc.aggregate3.staticCall(calls);
2535
+ const pendingDeposit = results[0].success ? subVaultIface.decodeFunctionResult("pendingDepositRequest", results[0].returnData)[0] : 0n;
2536
+ const claimableDeposit = results[1].success ? subVaultIface.decodeFunctionResult("claimableDepositRequest", results[1].returnData)[0] : 0n;
2537
+ const pendingRedeem = results[2].success ? subVaultIface.decodeFunctionResult("pendingRedeemRequest", results[2].returnData)[0] : 0n;
2538
+ const claimableRedeem = results[3].success ? subVaultIface.decodeFunctionResult("claimableRedeemRequest", results[3].returnData)[0] : 0n;
2539
+ return {
2540
+ subVault,
2541
+ pendingDeposit,
2542
+ claimableDeposit,
2543
+ pendingRedeem,
2544
+ claimableRedeem,
2545
+ canFinalizeDeposit: claimableDeposit > 0n,
2546
+ canFinalizeRedeem: claimableRedeem > 0n
2547
+ };
2548
+ }
2549
+ async function previewSubVaultDeposit(provider, subVault, assets) {
2550
+ const contract = new ethers.Contract(subVault, SUB_VAULT_ABI, provider);
2551
+ return await contract.previewDeposit(assets);
2552
+ }
2553
+ async function previewSubVaultRedeem(provider, subVault, shares) {
2554
+ const contract = new ethers.Contract(subVault, SUB_VAULT_ABI, provider);
2555
+ return await contract.previewRedeem(shares);
2556
+ }
2557
+ async function getVaultPortfolio(provider, vault) {
2558
+ const mc = new ethers.Contract(MULTICALL3_ADDRESS4, MULTICALL3_ABI4, provider);
2559
+ new ethers.Interface(SUB_VAULT_ABI);
2560
+ new ethers.Interface(VAULT_ANALYSIS_ABI);
2561
+ const vaultIface = new ethers.Interface(VAULT_ABI);
2562
+ const erc20Iface = new ethers.Interface(ERC20_ABI);
2563
+ const metaIface = new ethers.Interface(METADATA_ABI);
2564
+ const [availableRaw, subVaultPositions, vaultTotals] = await Promise.all([
2565
+ new ethers.Contract(vault, VAULT_ANALYSIS_ABI, provider).getAvailableAssets().catch(() => []),
2566
+ getSubVaultPositions(provider, vault),
2567
+ mc.aggregate3.staticCall([
2568
+ { target: vault, allowFailure: true, callData: vaultIface.encodeFunctionData("totalAssets") },
2569
+ { target: vault, allowFailure: true, callData: vaultIface.encodeFunctionData("totalSupply") },
2570
+ { target: vault, allowFailure: true, callData: vaultIface.encodeFunctionData("asset") }
2571
+ ])
2572
+ ]);
2573
+ const totalAssets = vaultTotals[0].success ? vaultIface.decodeFunctionResult("totalAssets", vaultTotals[0].returnData)[0] : 0n;
2574
+ const totalSupply = vaultTotals[1].success ? vaultIface.decodeFunctionResult("totalSupply", vaultTotals[1].returnData)[0] : 0n;
2575
+ const underlyingAsset = vaultTotals[2].success ? vaultIface.decodeFunctionResult("asset", vaultTotals[2].returnData)[0] : ethers.ethers.ZeroAddress;
2576
+ const subVaultAddressSet = new Set(subVaultPositions.map((p) => p.address.toLowerCase()));
2577
+ const liquidAddresses = availableRaw.filter(
2578
+ (addr) => !subVaultAddressSet.has(addr.toLowerCase())
2579
+ );
2580
+ const PER_ASSET = 4;
2581
+ let liquidAssets = [];
2582
+ if (liquidAddresses.length > 0) {
2583
+ const liquidCalls = liquidAddresses.flatMap((addr) => [
2584
+ { target: addr, allowFailure: true, callData: erc20Iface.encodeFunctionData("balanceOf", [vault]) },
2585
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("name") },
2586
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("symbol") },
2587
+ { target: addr, allowFailure: true, callData: metaIface.encodeFunctionData("decimals") }
2588
+ ]);
2589
+ const liquidResults = await mc.aggregate3.staticCall(liquidCalls);
2590
+ liquidAssets = liquidAddresses.map((addr, i) => {
2591
+ const base = i * PER_ASSET;
2592
+ const balance = liquidResults[base].success ? erc20Iface.decodeFunctionResult("balanceOf", liquidResults[base].returnData)[0] : 0n;
2593
+ const name = liquidResults[base + 1].success ? metaIface.decodeFunctionResult("name", liquidResults[base + 1].returnData)[0] : "";
2594
+ const symbol = liquidResults[base + 2].success ? metaIface.decodeFunctionResult("symbol", liquidResults[base + 2].returnData)[0] : "";
2595
+ const decimals = liquidResults[base + 3].success ? Number(metaIface.decodeFunctionResult("decimals", liquidResults[base + 3].returnData)[0]) : 18;
2596
+ return { address: addr, name, symbol, decimals, balance };
2597
+ });
2598
+ }
2599
+ const lockedAssets = await new ethers.Contract(vault, SUB_VAULT_ABI, provider).lockedTokensAmountOfAsset(underlyingAsset).catch(() => 0n);
2600
+ const subVaultTotal = subVaultPositions.reduce((sum, p) => sum + p.underlyingValue, 0n);
2601
+ const underlyingBalance = liquidAssets.find(
2602
+ (a) => a.address.toLowerCase() === underlyingAsset.toLowerCase()
2603
+ )?.balance ?? 0n;
2604
+ const totalValue = underlyingBalance + subVaultTotal;
2605
+ return {
2606
+ liquidAssets,
2607
+ subVaultPositions,
2608
+ totalValue,
2609
+ totalAssets,
2610
+ totalSupply,
2611
+ lockedAssets
2612
+ };
2613
+ }
2370
2614
 
2371
2615
  // src/ethers/distribution.ts
2372
2616
  async function getVaultDistribution(hubProvider, vault, spokeProviders) {
@@ -2638,6 +2882,7 @@ exports.OFT_ABI = OFT_ABI;
2638
2882
  exports.OFT_ROUTES = OFT_ROUTES;
2639
2883
  exports.OMNI_FACTORY_ADDRESS = OMNI_FACTORY_ADDRESS;
2640
2884
  exports.REGISTRY_ABI = REGISTRY_ABI;
2885
+ exports.SUB_VAULT_ABI = SUB_VAULT_ABI;
2641
2886
  exports.UNISWAP_V3_ROUTERS = UNISWAP_V3_ROUTERS;
2642
2887
  exports.VAULT_ABI = VAULT_ABI;
2643
2888
  exports.VAULT_ANALYSIS_ABI = VAULT_ANALYSIS_ABI;
@@ -2657,6 +2902,7 @@ exports.depositFromSpokeAsync = depositFromSpokeAsync;
2657
2902
  exports.depositMultiAsset = depositMultiAsset;
2658
2903
  exports.depositSimple = depositSimple;
2659
2904
  exports.detectStargateOft = detectStargateOft;
2905
+ exports.detectSubVaultType = detectSubVaultType;
2660
2906
  exports.discoverVaultTopology = discoverVaultTopology;
2661
2907
  exports.encodeBridgeParams = encodeBridgeParams;
2662
2908
  exports.encodeCuratorAction = encodeCuratorAction;
@@ -2670,11 +2916,14 @@ exports.getAllVaultChainIds = getAllVaultChainIds;
2670
2916
  exports.getAsyncRequestStatus = getAsyncRequestStatus;
2671
2917
  exports.getAsyncRequestStatusLabel = getAsyncRequestStatusLabel;
2672
2918
  exports.getCuratorVaultStatus = getCuratorVaultStatus;
2919
+ exports.getERC7540RequestStatus = getERC7540RequestStatus;
2673
2920
  exports.getFullVaultTopology = getFullVaultTopology;
2674
2921
  exports.getInboundRoutes = getInboundRoutes;
2675
2922
  exports.getMaxWithdrawable = getMaxWithdrawable;
2676
2923
  exports.getOutboundRoutes = getOutboundRoutes;
2677
2924
  exports.getPendingActions = getPendingActions;
2925
+ exports.getSubVaultInfo = getSubVaultInfo;
2926
+ exports.getSubVaultPositions = getSubVaultPositions;
2678
2927
  exports.getUserBalances = getUserBalances;
2679
2928
  exports.getUserBalancesForRoutes = getUserBalancesForRoutes;
2680
2929
  exports.getUserPosition = getUserPosition;
@@ -2684,6 +2933,7 @@ exports.getVaultAssetBreakdown = getVaultAssetBreakdown;
2684
2933
  exports.getVaultDistribution = getVaultDistribution;
2685
2934
  exports.getVaultDistributionWithTopology = getVaultDistributionWithTopology;
2686
2935
  exports.getVaultMetadata = getVaultMetadata;
2936
+ exports.getVaultPortfolio = getVaultPortfolio;
2687
2937
  exports.getVaultStatus = getVaultStatus;
2688
2938
  exports.getVaultSummary = getVaultSummary;
2689
2939
  exports.getVaultTopology = getVaultTopology;
@@ -2699,6 +2949,8 @@ exports.preflightSpokeRedeem = preflightSpokeRedeem;
2699
2949
  exports.preflightSync = preflightSync;
2700
2950
  exports.previewDeposit = previewDeposit;
2701
2951
  exports.previewRedeem = previewRedeem;
2952
+ exports.previewSubVaultDeposit = previewSubVaultDeposit;
2953
+ exports.previewSubVaultRedeem = previewSubVaultRedeem;
2702
2954
  exports.quoteComposeFee = quoteComposeFee;
2703
2955
  exports.quoteCuratorBridgeFee = quoteCuratorBridgeFee;
2704
2956
  exports.quoteDepositFromSpokeFee = quoteDepositFromSpokeFee;