@oydual31/more-vaults-sdk 0.3.3 → 0.4.1

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 (42) 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 +644 -0
  8. package/dist/react/index.cjs.map +1 -1
  9. package/dist/react/index.d.cts +111 -2
  10. package/dist/react/index.d.ts +111 -2
  11. package/dist/react/index.js +638 -3
  12. package/dist/react/index.js.map +1 -1
  13. package/dist/{spokeRoutes-BIafSbQ3.d.cts → spokeRoutes-B8Lnk-t4.d.cts} +191 -2
  14. package/dist/{spokeRoutes-BIafSbQ3.d.ts → spokeRoutes-B8Lnk-t4.d.ts} +191 -2
  15. package/dist/viem/index.d.cts +4 -192
  16. package/dist/viem/index.d.ts +4 -192
  17. package/package.json +1 -1
  18. package/src/ethers/abis.ts +92 -0
  19. package/src/ethers/chains.ts +191 -0
  20. package/src/ethers/crossChainFlows.ts +208 -0
  21. package/src/ethers/curatorMulticall.ts +195 -0
  22. package/src/ethers/curatorStatus.ts +319 -0
  23. package/src/ethers/curatorSwaps.ts +192 -0
  24. package/src/ethers/distribution.ts +156 -0
  25. package/src/ethers/index.ts +96 -1
  26. package/src/ethers/preflight.ts +225 -1
  27. package/src/ethers/redeemFlows.ts +160 -1
  28. package/src/ethers/spokeRoutes.ts +361 -0
  29. package/src/ethers/topology.ts +240 -0
  30. package/src/ethers/types.ts +95 -0
  31. package/src/ethers/userHelpers.ts +193 -0
  32. package/src/ethers/utils.ts +28 -0
  33. package/src/react/index.ts +25 -0
  34. package/src/react/useCuratorVaultStatus.ts +32 -0
  35. package/src/react/useExecuteActions.ts +23 -0
  36. package/src/react/useIsCurator.ts +30 -0
  37. package/src/react/usePendingActions.ts +33 -0
  38. package/src/react/useProtocolWhitelist.ts +30 -0
  39. package/src/react/useSubmitActions.ts +27 -0
  40. package/src/react/useVaultAnalysis.ts +32 -0
  41. package/src/react/useVaultAssetBreakdown.ts +32 -0
  42. package/src/react/useVetoActions.ts +23 -0
@@ -10,6 +10,8 @@ import { BRIDGE_ABI, CONFIG_ABI, ERC20_ABI, VAULT_ABI, METADATA_ABI } from "./ab
10
10
  import type { CrossChainRequestInfo } from "./types";
11
11
  import { getVaultStatus } from "./utils";
12
12
  import type { VaultStatus } from "./utils";
13
+ import { CHAIN_ID_TO_EID, OFT_ROUTES, createChainProvider } from "./chains";
14
+ import { discoverVaultTopology, OMNI_FACTORY_ADDRESS } from "./topology";
13
15
 
14
16
  // Multicall3 — deployed at the same address on every EVM chain
15
17
  const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
@@ -478,3 +480,194 @@ export async function getVaultSummary(
478
480
  ]);
479
481
  return { ...status, ...metadata };
480
482
  }
483
+
484
+ // ─────────────────────────────────────────────────────────────────────────────
485
+
486
+ /** Minimal ABIs for SHARE_OFT discovery in getUserPositionMultiChain */
487
+ const FACTORY_COMPOSER_ABI_UH = [
488
+ "function vaultComposer(address _vault) view returns (address)",
489
+ ] as const;
490
+
491
+ const COMPOSER_SHARE_OFT_ABI_UH = [
492
+ "function SHARE_OFT() view returns (address)",
493
+ ] as const;
494
+
495
+ const OFT_PEERS_ABI_UH = [
496
+ "function peers(uint32 eid) view returns (bytes32)",
497
+ ] as const;
498
+
499
+ export interface MultiChainUserPosition {
500
+ /** Shares held directly on the hub vault (vault.balanceOf) */
501
+ hubShares: bigint;
502
+ /** Per-spoke SHARE_OFT balances normalized to vault decimals: { [chainId]: bigint } */
503
+ spokeShares: Record<number, bigint>;
504
+ /** Per-spoke SHARE_OFT raw balances in OFT native decimals: { [chainId]: bigint }
505
+ * Use these for bridgeSharesToHub() and quoteShareBridgeFee() */
506
+ rawSpokeShares: Record<number, bigint>;
507
+ /** hubShares + sum of all spokeShares (in vault decimals) */
508
+ totalShares: bigint;
509
+ /** convertToAssets(totalShares) on the hub */
510
+ estimatedAssets: bigint;
511
+ /** Share price: convertToAssets(10^decimals) */
512
+ sharePrice: bigint;
513
+ /** Vault decimals */
514
+ decimals: number;
515
+ /** Pending async withdrawal request on hub, or null */
516
+ pendingWithdrawal: {
517
+ shares: bigint;
518
+ timelockEndsAt: bigint;
519
+ canRedeemNow: boolean;
520
+ } | null;
521
+ }
522
+
523
+ /**
524
+ * Read the user's position across all chains of an omni vault.
525
+ *
526
+ * Discovers topology automatically, reads hub shares + pending withdrawal,
527
+ * then reads SHARE_OFT balances on each spoke chain in parallel.
528
+ *
529
+ * For local (single-chain) vaults, spokeShares will be empty and this
530
+ * behaves identically to getUserPosition.
531
+ *
532
+ * @param vault Vault address (same on all chains via CREATE3)
533
+ * @param user User wallet address
534
+ * @returns Aggregated position across all chains
535
+ */
536
+ export async function getUserPositionMultiChain(
537
+ vault: string,
538
+ user: string,
539
+ ): Promise<MultiChainUserPosition> {
540
+ // Step 1: discover topology
541
+ const topo = await discoverVaultTopology(vault);
542
+ const hubProvider = createChainProvider(topo.hubChainId);
543
+ if (!hubProvider) throw new Error(`No public RPC for hub chainId ${topo.hubChainId}`);
544
+
545
+ // Step 2: read hub data (shares, decimals, withdrawal request)
546
+ const mc = new Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, hubProvider);
547
+ const vaultIface = new Interface(VAULT_ABI as unknown as string[]);
548
+ const decimalsIface = new Interface(["function decimals() view returns (uint8)"]);
549
+
550
+ const b1Calls = [
551
+ { target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("balanceOf", [user]) },
552
+ { target: vault, allowFailure: false, callData: decimalsIface.encodeFunctionData("decimals") },
553
+ { target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("getWithdrawalRequest", [user]) },
554
+ ];
555
+
556
+ const [b1Raw, block] = await Promise.all([
557
+ mc.aggregate3.staticCall(b1Calls) as Promise<{ success: boolean; returnData: string }[]>,
558
+ hubProvider.getBlock("latest"),
559
+ ]);
560
+
561
+ const hubShares = vaultIface.decodeFunctionResult("balanceOf", b1Raw[0].returnData)[0] as bigint;
562
+ const decimals = Number(decimalsIface.decodeFunctionResult("decimals", b1Raw[1].returnData)[0]);
563
+ const withdrawalResult = vaultIface.decodeFunctionResult("getWithdrawalRequest", b1Raw[2].returnData);
564
+ const withdrawShares = withdrawalResult[0] as bigint;
565
+ const timelockEndsAt = withdrawalResult[1] as bigint;
566
+
567
+ // Step 3: resolve SHARE_OFT addresses for spokes (if any)
568
+ const spokeShares: Record<number, bigint> = {};
569
+ const rawSpokeShares: Record<number, bigint> = {};
570
+
571
+ if (topo.spokeChainIds.length > 0) {
572
+ let hubShareOft: string | null = null;
573
+ try {
574
+ const factory = new Contract(OMNI_FACTORY_ADDRESS, FACTORY_COMPOSER_ABI_UH, hubProvider);
575
+ const composerAddress: string = await factory.vaultComposer(vault);
576
+
577
+ if (composerAddress !== "0x0000000000000000000000000000000000000000") {
578
+ const composer = new Contract(composerAddress, COMPOSER_SHARE_OFT_ABI_UH, hubProvider);
579
+ hubShareOft = await composer.SHARE_OFT();
580
+ }
581
+ } catch { /* no composer — skip spoke reads */ }
582
+
583
+ if (hubShareOft) {
584
+ const hubShareOftContract = new Contract(hubShareOft, OFT_PEERS_ABI_UH, hubProvider);
585
+
586
+ const spokePromises = topo.spokeChainIds.map(async (spokeChainId) => {
587
+ try {
588
+ const spokeEid = CHAIN_ID_TO_EID[spokeChainId];
589
+ if (!spokeEid) return { chainId: spokeChainId, balance: 0n, rawBalance: 0n };
590
+
591
+ const spokeOftBytes32: string = await hubShareOftContract.peers(spokeEid);
592
+ const spokeOft = `0x${spokeOftBytes32.slice(-40)}`;
593
+
594
+ if (spokeOft === "0x0000000000000000000000000000000000000000") {
595
+ return { chainId: spokeChainId, balance: 0n, rawBalance: 0n };
596
+ }
597
+
598
+ const spokeProvider = createChainProvider(spokeChainId);
599
+ if (!spokeProvider) return { chainId: spokeChainId, balance: 0n, rawBalance: 0n };
600
+
601
+ // Read balance + decimals on spoke chain via Multicall3
602
+ const spokeMc = new Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, spokeProvider);
603
+ const erc20Iface = new Interface(ERC20_ABI as unknown as string[]);
604
+ const spokeDecimalsIface = new Interface(["function decimals() view returns (uint8)"]);
605
+
606
+ const spokeCalls = [
607
+ { target: spokeOft, allowFailure: false, callData: erc20Iface.encodeFunctionData("balanceOf", [user]) },
608
+ { target: spokeOft, allowFailure: false, callData: spokeDecimalsIface.encodeFunctionData("decimals") },
609
+ ];
610
+ const spokeRaw: { success: boolean; returnData: string }[] =
611
+ await spokeMc.aggregate3.staticCall(spokeCalls);
612
+
613
+ const rawBalance = erc20Iface.decodeFunctionResult("balanceOf", spokeRaw[0].returnData)[0] as bigint;
614
+ const spokeOftDecimals = Number(spokeDecimalsIface.decodeFunctionResult("decimals", spokeRaw[1].returnData)[0]);
615
+
616
+ // Normalize to vault decimals
617
+ let balance: bigint;
618
+ if (spokeOftDecimals > decimals) {
619
+ balance = rawBalance / (10n ** BigInt(spokeOftDecimals - decimals));
620
+ } else if (spokeOftDecimals < decimals) {
621
+ balance = rawBalance * (10n ** BigInt(decimals - spokeOftDecimals));
622
+ } else {
623
+ balance = rawBalance;
624
+ }
625
+
626
+ return { chainId: spokeChainId, balance, rawBalance };
627
+ } catch {
628
+ return { chainId: spokeChainId, balance: 0n, rawBalance: 0n };
629
+ }
630
+ });
631
+
632
+ const results = await Promise.all(spokePromises);
633
+ for (const { chainId, balance, rawBalance } of results) {
634
+ spokeShares[chainId] = balance;
635
+ rawSpokeShares[chainId] = rawBalance;
636
+ }
637
+ }
638
+ }
639
+
640
+ // Step 4: compute totals
641
+ const totalSpokeShares = Object.values(spokeShares).reduce((sum, b) => sum + b, 0n);
642
+ const totalShares = hubShares + totalSpokeShares;
643
+
644
+ const oneShare = 10n ** BigInt(decimals);
645
+ const vaultContract = new Contract(vault, VAULT_ABI, hubProvider);
646
+ const [estimatedAssets, sharePrice]: [bigint, bigint] = await Promise.all([
647
+ totalShares === 0n
648
+ ? Promise.resolve(0n)
649
+ : (vaultContract.convertToAssets(totalShares) as Promise<bigint>),
650
+ vaultContract.convertToAssets(oneShare) as Promise<bigint>,
651
+ ]);
652
+
653
+ // Step 5: pending withdrawal
654
+ const currentTimestamp = BigInt(block?.timestamp ?? 0);
655
+ const pendingWithdrawal = withdrawShares === 0n
656
+ ? null
657
+ : {
658
+ shares: withdrawShares,
659
+ timelockEndsAt,
660
+ canRedeemNow: timelockEndsAt === 0n || currentTimestamp >= timelockEndsAt,
661
+ };
662
+
663
+ return {
664
+ hubShares,
665
+ spokeShares,
666
+ rawSpokeShares,
667
+ totalShares,
668
+ estimatedAssets,
669
+ sharePrice,
670
+ decimals,
671
+ pendingWithdrawal,
672
+ };
673
+ }
@@ -8,6 +8,11 @@ import { Contract, Interface, ZeroAddress } from "ethers";
8
8
  import type { Provider, Signer } from "ethers";
9
9
  import { BRIDGE_ABI, CONFIG_ABI, ERC20_ABI, VAULT_ABI } from "./abis";
10
10
 
11
+ // Minimal ABI for Stargate type detection
12
+ const STARGATE_TYPE_ABI = [
13
+ "function stargateType() view returns (uint8)",
14
+ ] as const;
15
+
11
16
  // Multicall3 — deployed at the same address on every EVM chain
12
17
  const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
13
18
  const MULTICALL3_ABI = [
@@ -379,3 +384,26 @@ export async function getVaultStatus(
379
384
  issues,
380
385
  };
381
386
  }
387
+
388
+ /**
389
+ * Detect whether an OFT address is a Stargate V2 pool by calling `stargateType()`.
390
+ *
391
+ * Stargate pools implement this function (returns 0=Pool, 1=OFT).
392
+ * Standard OFTs revert because they don't have it.
393
+ *
394
+ * @param provider Read-only provider on the OFT's chain
395
+ * @param oft OFT contract address
396
+ * @returns true if the contract is a Stargate V2 pool/OFT
397
+ */
398
+ export async function detectStargateOft(
399
+ provider: Provider,
400
+ oft: string
401
+ ): Promise<boolean> {
402
+ try {
403
+ const contract = new Contract(oft, STARGATE_TYPE_ABI, provider);
404
+ await contract.stargateType();
405
+ return true;
406
+ } catch {
407
+ return false;
408
+ }
409
+ }
@@ -37,3 +37,28 @@ export { useSmartRedeem } from './useSmartRedeem.js'
37
37
 
38
38
  // --- Inbound Routes ---
39
39
  export { useInboundRoutes, getRouteTokenDecimals } from './useInboundRoutes.js'
40
+
41
+ // --- Curator Read hooks ---
42
+ export { useCuratorVaultStatus } from './useCuratorVaultStatus.js'
43
+ export type { CuratorVaultStatus } from './useCuratorVaultStatus.js'
44
+
45
+ export { useVaultAnalysis } from './useVaultAnalysis.js'
46
+ export type { VaultAnalysis } from './useVaultAnalysis.js'
47
+
48
+ export { useVaultAssetBreakdown } from './useVaultAssetBreakdown.js'
49
+ export type { VaultAssetBreakdown } from './useVaultAssetBreakdown.js'
50
+
51
+ export { usePendingActions } from './usePendingActions.js'
52
+ export type { PendingAction } from './usePendingActions.js'
53
+
54
+ export { useIsCurator } from './useIsCurator.js'
55
+
56
+ export { useProtocolWhitelist } from './useProtocolWhitelist.js'
57
+
58
+ // --- Curator Write hooks ---
59
+ export { useSubmitActions } from './useSubmitActions.js'
60
+ export type { CuratorAction } from './useSubmitActions.js'
61
+
62
+ export { useExecuteActions } from './useExecuteActions.js'
63
+
64
+ export { useVetoActions } from './useVetoActions.js'
@@ -0,0 +1,32 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import { usePublicClient } from 'wagmi'
3
+ import { asSdkClient, getCuratorVaultStatus } from '../viem/index.js'
4
+ import type { CuratorVaultStatus } from '../viem/index.js'
5
+
6
+ export type { CuratorVaultStatus }
7
+
8
+ interface UseCuratorVaultStatusOptions {
9
+ /** Refetch interval in ms. Default: 30_000 (30s) */
10
+ refetchInterval?: number
11
+ }
12
+
13
+ /**
14
+ * Read the curator vault status snapshot.
15
+ *
16
+ * @example
17
+ * const { data: status, isLoading } = useCuratorVaultStatus('0xVAULT', 747)
18
+ */
19
+ export function useCuratorVaultStatus(
20
+ vault: `0x${string}` | undefined,
21
+ chainId: number,
22
+ options?: UseCuratorVaultStatusOptions,
23
+ ) {
24
+ const publicClient = usePublicClient({ chainId })
25
+ return useQuery({
26
+ queryKey: ['curatorVaultStatus', vault, chainId],
27
+ queryFn: () => getCuratorVaultStatus(asSdkClient(publicClient), vault!),
28
+ enabled: !!vault && !!publicClient,
29
+ refetchInterval: options?.refetchInterval ?? 30_000,
30
+ staleTime: 15_000,
31
+ })
32
+ }
@@ -0,0 +1,23 @@
1
+ import { useMutation } from '@tanstack/react-query'
2
+ import { usePublicClient, useWalletClient } from 'wagmi'
3
+ import { asSdkClient, executeActions } from '../viem/index.js'
4
+
5
+ /**
6
+ * Execute a pending curator action batch by nonce.
7
+ *
8
+ * @example
9
+ * const { mutateAsync } = useExecuteActions('0xVAULT', 747)
10
+ * await mutateAsync({ nonce: 1n })
11
+ */
12
+ export function useExecuteActions(vault: `0x${string}`, chainId: number) {
13
+ const publicClient = usePublicClient({ chainId })
14
+ const { data: walletClient } = useWalletClient({ chainId })
15
+
16
+ return useMutation({
17
+ mutationFn: async ({ nonce }: { nonce: bigint }) => {
18
+ if (!walletClient || !publicClient) throw new Error('Wallet or public client not available')
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ return executeActions(walletClient as any, asSdkClient(publicClient), vault, nonce)
21
+ },
22
+ })
23
+ }
@@ -0,0 +1,30 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import { usePublicClient } from 'wagmi'
3
+ import { asSdkClient, isCurator } from '../viem/index.js'
4
+
5
+ interface UseIsCuratorOptions {
6
+ /** Refetch interval in ms. Default: 30_000 (30s) */
7
+ refetchInterval?: number
8
+ }
9
+
10
+ /**
11
+ * Check whether an address is a curator of a vault.
12
+ *
13
+ * @example
14
+ * const { data: curator, isLoading } = useIsCurator('0xVAULT', 747, '0xADDR')
15
+ */
16
+ export function useIsCurator(
17
+ vault: `0x${string}` | undefined,
18
+ chainId: number,
19
+ address: `0x${string}` | undefined,
20
+ options?: UseIsCuratorOptions,
21
+ ) {
22
+ const publicClient = usePublicClient({ chainId })
23
+ return useQuery({
24
+ queryKey: ['isCurator', vault, chainId, address],
25
+ queryFn: () => isCurator(asSdkClient(publicClient), vault!, address!),
26
+ enabled: !!vault && !!publicClient && !!address,
27
+ refetchInterval: options?.refetchInterval ?? 30_000,
28
+ staleTime: 15_000,
29
+ })
30
+ }
@@ -0,0 +1,33 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import { usePublicClient } from 'wagmi'
3
+ import { asSdkClient, getPendingActions } from '../viem/index.js'
4
+ import type { PendingAction } from '../viem/index.js'
5
+
6
+ export type { PendingAction }
7
+
8
+ interface UsePendingActionsOptions {
9
+ /** Refetch interval in ms. Default: 30_000 (30s) */
10
+ refetchInterval?: number
11
+ }
12
+
13
+ /**
14
+ * Read pending curator actions for a vault by nonce.
15
+ *
16
+ * @example
17
+ * const { data: pending, isLoading } = usePendingActions('0xVAULT', 747, 1n)
18
+ */
19
+ export function usePendingActions(
20
+ vault: `0x${string}` | undefined,
21
+ chainId: number,
22
+ nonce: bigint | undefined,
23
+ options?: UsePendingActionsOptions,
24
+ ) {
25
+ const publicClient = usePublicClient({ chainId })
26
+ return useQuery({
27
+ queryKey: ['pendingActions', vault, chainId, nonce?.toString()],
28
+ queryFn: () => getPendingActions(asSdkClient(publicClient), vault!, nonce!),
29
+ enabled: !!vault && !!publicClient && nonce !== undefined,
30
+ refetchInterval: options?.refetchInterval ?? 30_000,
31
+ staleTime: 15_000,
32
+ })
33
+ }
@@ -0,0 +1,30 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import { usePublicClient } from 'wagmi'
3
+ import { asSdkClient, checkProtocolWhitelist } from '../viem/index.js'
4
+
5
+ interface UseProtocolWhitelistOptions {
6
+ /** Refetch interval in ms. Default: 30_000 (30s) */
7
+ refetchInterval?: number
8
+ }
9
+
10
+ /**
11
+ * Check which protocols are whitelisted for a vault.
12
+ *
13
+ * @example
14
+ * const { data: whitelist, isLoading } = useProtocolWhitelist('0xVAULT', 747, ['0xPROTOCOL'])
15
+ */
16
+ export function useProtocolWhitelist(
17
+ vault: `0x${string}` | undefined,
18
+ chainId: number,
19
+ protocols: `0x${string}`[],
20
+ options?: UseProtocolWhitelistOptions,
21
+ ) {
22
+ const publicClient = usePublicClient({ chainId })
23
+ return useQuery({
24
+ queryKey: ['protocolWhitelist', vault, chainId, protocols],
25
+ queryFn: () => checkProtocolWhitelist(asSdkClient(publicClient), vault!, protocols),
26
+ enabled: !!vault && !!publicClient,
27
+ refetchInterval: options?.refetchInterval ?? 30_000,
28
+ staleTime: 15_000,
29
+ })
30
+ }
@@ -0,0 +1,27 @@
1
+ import { useMutation } from '@tanstack/react-query'
2
+ import { usePublicClient, useWalletClient } from 'wagmi'
3
+ import { asSdkClient, submitActions, buildCuratorBatch } from '../viem/index.js'
4
+ import type { CuratorAction } from '../viem/index.js'
5
+
6
+ export type { CuratorAction }
7
+
8
+ /**
9
+ * Submit a batch of curator actions to the vault.
10
+ *
11
+ * @example
12
+ * const { mutateAsync } = useSubmitActions('0xVAULT', 747)
13
+ * await mutateAsync({ actions: [{ type: 'erc4626Deposit', vault: '0x...', assets: 100n }] })
14
+ */
15
+ export function useSubmitActions(vault: `0x${string}`, chainId: number) {
16
+ const publicClient = usePublicClient({ chainId })
17
+ const { data: walletClient } = useWalletClient({ chainId })
18
+
19
+ return useMutation({
20
+ mutationFn: async ({ actions }: { actions: CuratorAction[] }) => {
21
+ if (!walletClient || !publicClient) throw new Error('Wallet or public client not available')
22
+ const encodedActions = buildCuratorBatch(actions)
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ return submitActions(walletClient as any, asSdkClient(publicClient), vault, encodedActions)
25
+ },
26
+ })
27
+ }
@@ -0,0 +1,32 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import { usePublicClient } from 'wagmi'
3
+ import { asSdkClient, getVaultAnalysis } from '../viem/index.js'
4
+ import type { VaultAnalysis } from '../viem/index.js'
5
+
6
+ export type { VaultAnalysis }
7
+
8
+ interface UseVaultAnalysisOptions {
9
+ /** Refetch interval in ms. Default: 30_000 (30s) */
10
+ refetchInterval?: number
11
+ }
12
+
13
+ /**
14
+ * Read a detailed analysis of a vault.
15
+ *
16
+ * @example
17
+ * const { data: analysis, isLoading } = useVaultAnalysis('0xVAULT', 747)
18
+ */
19
+ export function useVaultAnalysis(
20
+ vault: `0x${string}` | undefined,
21
+ chainId: number,
22
+ options?: UseVaultAnalysisOptions,
23
+ ) {
24
+ const publicClient = usePublicClient({ chainId })
25
+ return useQuery({
26
+ queryKey: ['vaultAnalysis', vault, chainId],
27
+ queryFn: () => getVaultAnalysis(asSdkClient(publicClient), vault!),
28
+ enabled: !!vault && !!publicClient,
29
+ refetchInterval: options?.refetchInterval ?? 30_000,
30
+ staleTime: 15_000,
31
+ })
32
+ }
@@ -0,0 +1,32 @@
1
+ import { useQuery } from '@tanstack/react-query'
2
+ import { usePublicClient } from 'wagmi'
3
+ import { asSdkClient, getVaultAssetBreakdown } from '../viem/index.js'
4
+ import type { VaultAssetBreakdown } from '../viem/index.js'
5
+
6
+ export type { VaultAssetBreakdown }
7
+
8
+ interface UseVaultAssetBreakdownOptions {
9
+ /** Refetch interval in ms. Default: 30_000 (30s) */
10
+ refetchInterval?: number
11
+ }
12
+
13
+ /**
14
+ * Read the asset breakdown of a vault.
15
+ *
16
+ * @example
17
+ * const { data: breakdown, isLoading } = useVaultAssetBreakdown('0xVAULT', 747)
18
+ */
19
+ export function useVaultAssetBreakdown(
20
+ vault: `0x${string}` | undefined,
21
+ chainId: number,
22
+ options?: UseVaultAssetBreakdownOptions,
23
+ ) {
24
+ const publicClient = usePublicClient({ chainId })
25
+ return useQuery({
26
+ queryKey: ['vaultAssetBreakdown', vault, chainId],
27
+ queryFn: () => getVaultAssetBreakdown(asSdkClient(publicClient), vault!),
28
+ enabled: !!vault && !!publicClient,
29
+ refetchInterval: options?.refetchInterval ?? 30_000,
30
+ staleTime: 15_000,
31
+ })
32
+ }
@@ -0,0 +1,23 @@
1
+ import { useMutation } from '@tanstack/react-query'
2
+ import { usePublicClient, useWalletClient } from 'wagmi'
3
+ import { asSdkClient, vetoActions } from '../viem/index.js'
4
+
5
+ /**
6
+ * Guardian-only: cancel (veto) one or more pending curator action batches.
7
+ *
8
+ * @example
9
+ * const { mutateAsync } = useVetoActions('0xVAULT', 747)
10
+ * await mutateAsync({ nonces: [1n, 2n] })
11
+ */
12
+ export function useVetoActions(vault: `0x${string}`, chainId: number) {
13
+ const publicClient = usePublicClient({ chainId })
14
+ const { data: walletClient } = useWalletClient({ chainId })
15
+
16
+ return useMutation({
17
+ mutationFn: async ({ nonces }: { nonces: bigint[] }) => {
18
+ if (!walletClient || !publicClient) throw new Error('Wallet or public client not available')
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ return vetoActions(walletClient as any, asSdkClient(publicClient), vault, nonces)
21
+ },
22
+ })
23
+ }