@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
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Curator swap helpers for Uniswap V3-compatible DEXes.
3
+ *
4
+ * Provides typed helpers to build CuratorAction objects and raw calldata for
5
+ * Uniswap V3 exactInputSingle swaps, automatically resolving the correct router
6
+ * and ABI variant (SwapRouter vs SwapRouter02) per chain.
7
+ *
8
+ * Supported chains and routers:
9
+ * - Base (8453): SwapRouter02 0x2626... — NO deadline field
10
+ * - Ethereum (1): SwapRouter 0xE592... — HAS deadline field
11
+ * - Arbitrum (42161): SwapRouter 0xE592... — HAS deadline field
12
+ * - Optimism (10): SwapRouter 0xE592... — HAS deadline field
13
+ * - Flow EVM (747): FlowSwap V3 0xeEDC... — HAS deadline field
14
+ *
15
+ * @module curatorSwaps
16
+ */
17
+
18
+ import { Interface } from "ethers";
19
+ import { UNISWAP_V3_ROUTERS } from "./chains";
20
+ import type { CuratorAction } from "./types";
21
+
22
+ // ─────────────────────────────────────────────────────────────────────────────
23
+ // ABI constants
24
+ // ─────────────────────────────────────────────────────────────────────────────
25
+
26
+ /**
27
+ * Uniswap V3 SwapRouter exactInputSingle ABI (human-readable).
28
+ * Used for: Ethereum (1), Arbitrum (42161), Optimism (10), Flow EVM (747).
29
+ * Struct includes `deadline` field.
30
+ */
31
+ const UNISWAP_V3_SWAP_ROUTER_ABI = [
32
+ "function exactInputSingle(tuple(address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountOut)",
33
+ ] as const;
34
+
35
+ /**
36
+ * Uniswap V3 SwapRouter02 exactInputSingle ABI (human-readable).
37
+ * Used for: Base (8453).
38
+ * Struct does NOT include `deadline` field — SwapRouter02 removed it.
39
+ */
40
+ const UNISWAP_V3_SWAP_ROUTER02_ABI = [
41
+ "function exactInputSingle(tuple(address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountOut)",
42
+ ] as const;
43
+
44
+ // ─────────────────────────────────────────────────────────────────────────────
45
+ // Chain variant detection
46
+ // ─────────────────────────────────────────────────────────────────────────────
47
+
48
+ /**
49
+ * Chains that use SwapRouter02 (no deadline in struct).
50
+ * All other chains in UNISWAP_V3_ROUTERS use the original SwapRouter.
51
+ */
52
+ const SWAP_ROUTER02_CHAINS = new Set([8453]);
53
+
54
+ // ─────────────────────────────────────────────────────────────────────────────
55
+ // Calldata encoding
56
+ // ─────────────────────────────────────────────────────────────────────────────
57
+
58
+ /**
59
+ * Encode Uniswap V3 exactInputSingle calldata directly.
60
+ * For curators who want raw calldata without the CuratorAction wrapper.
61
+ *
62
+ * Automatically selects the correct ABI variant (SwapRouter vs SwapRouter02)
63
+ * based on the chainId. The deadline (for SwapRouter chains) is set to
64
+ * `now + 20 minutes` to prevent stale transactions from executing.
65
+ *
66
+ * @param params.chainId EVM chain ID — must be present in UNISWAP_V3_ROUTERS
67
+ * @param params.tokenIn Input token address
68
+ * @param params.tokenOut Output token address
69
+ * @param params.fee Pool fee tier: 100, 500, 3000, or 10000
70
+ * @param params.amountIn Exact input amount (in tokenIn units)
71
+ * @param params.minAmountOut Minimum acceptable output (slippage protection)
72
+ * @param params.recipient Address to receive the output tokens (usually the vault)
73
+ * @returns The router contract address and ABI-encoded calldata
74
+ * @throws If no router is configured for the given chainId
75
+ */
76
+ export function encodeUniswapV3SwapCalldata(params: {
77
+ chainId: number;
78
+ tokenIn: string;
79
+ tokenOut: string;
80
+ fee: number;
81
+ amountIn: bigint;
82
+ minAmountOut: bigint;
83
+ recipient: string;
84
+ }): { targetContract: string; swapCallData: string } {
85
+ const { chainId, tokenIn, tokenOut, fee, amountIn, minAmountOut, recipient } = params;
86
+
87
+ const router = UNISWAP_V3_ROUTERS[chainId];
88
+ if (!router) {
89
+ throw new Error(
90
+ `[MoreVaults] No Uniswap V3 router configured for chainId ${chainId}. ` +
91
+ `Supported chains: ${Object.keys(UNISWAP_V3_ROUTERS).join(', ')}`
92
+ );
93
+ }
94
+
95
+ let swapCallData: string;
96
+
97
+ if (SWAP_ROUTER02_CHAINS.has(chainId)) {
98
+ // SwapRouter02 (Base) — no deadline field
99
+ const iface = new Interface(UNISWAP_V3_SWAP_ROUTER02_ABI as unknown as string[]);
100
+ swapCallData = iface.encodeFunctionData("exactInputSingle", [
101
+ {
102
+ tokenIn,
103
+ tokenOut,
104
+ fee,
105
+ recipient,
106
+ amountIn,
107
+ amountOutMinimum: minAmountOut,
108
+ sqrtPriceLimitX96: 0n,
109
+ },
110
+ ]);
111
+ } else {
112
+ // Original SwapRouter (Eth/Arb/Op/Flow EVM) — has deadline field
113
+ const deadline = BigInt(Math.floor(Date.now() / 1000) + 1200); // now + 20 minutes
114
+ const iface = new Interface(UNISWAP_V3_SWAP_ROUTER_ABI as unknown as string[]);
115
+ swapCallData = iface.encodeFunctionData("exactInputSingle", [
116
+ {
117
+ tokenIn,
118
+ tokenOut,
119
+ fee,
120
+ recipient,
121
+ deadline,
122
+ amountIn,
123
+ amountOutMinimum: minAmountOut,
124
+ sqrtPriceLimitX96: 0n,
125
+ },
126
+ ]);
127
+ }
128
+
129
+ return { targetContract: router, swapCallData };
130
+ }
131
+
132
+ // ─────────────────────────────────────────────────────────────────────────────
133
+ // CuratorAction builder
134
+ // ─────────────────────────────────────────────────────────────────────────────
135
+
136
+ /**
137
+ * Build a CuratorAction for a Uniswap V3 exactInputSingle swap.
138
+ *
139
+ * Automatically resolves the router address from UNISWAP_V3_ROUTERS and
140
+ * selects the correct ABI struct (with or without deadline) based on chainId.
141
+ *
142
+ * The returned action is a `swap` variant ready to be passed to
143
+ * `buildCuratorBatch` and then `submitActions`.
144
+ *
145
+ * @param params.chainId EVM chain ID — must be present in UNISWAP_V3_ROUTERS
146
+ * @param params.tokenIn Input token address
147
+ * @param params.tokenOut Output token address
148
+ * @param params.fee Pool fee tier: 100, 500, 3000, or 10000
149
+ * @param params.amountIn Exact input amount (in tokenIn units)
150
+ * @param params.minAmountOut Minimum acceptable output (slippage protection)
151
+ * @param params.recipient Address to receive output tokens (usually the vault)
152
+ * @returns A typed CuratorAction ready for buildCuratorBatch
153
+ * @throws If no router is configured for the given chainId
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * const action = buildUniswapV3Swap({
158
+ * chainId: 8453,
159
+ * tokenIn: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
160
+ * tokenOut: '0x4200000000000000000000000000000000000006', // WETH
161
+ * fee: 500, // 0.05% pool
162
+ * amountIn: 150_000n, // 0.15 USDC (6 decimals)
163
+ * minAmountOut: 1n, // accept any amount (set properly in production)
164
+ * recipient: VAULT,
165
+ * })
166
+ * const batch = buildCuratorBatch([action])
167
+ * await submitActions(signer, vault, batch)
168
+ * ```
169
+ */
170
+ export function buildUniswapV3Swap(params: {
171
+ chainId: number;
172
+ tokenIn: string;
173
+ tokenOut: string;
174
+ fee: number;
175
+ amountIn: bigint;
176
+ minAmountOut: bigint;
177
+ recipient: string;
178
+ }): CuratorAction {
179
+ const { targetContract, swapCallData } = encodeUniswapV3SwapCalldata(params);
180
+
181
+ return {
182
+ type: 'swap',
183
+ params: {
184
+ targetContract,
185
+ tokenIn: params.tokenIn,
186
+ tokenOut: params.tokenOut,
187
+ maxAmountIn: params.amountIn,
188
+ minAmountOut: params.minAmountOut,
189
+ swapCallData,
190
+ },
191
+ };
192
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Vault distribution helpers for the MoreVaults ethers.js v6 SDK.
3
+ *
4
+ * Reads the cross-chain capital distribution of a vault: hub balance,
5
+ * spoke balances, and aggregate totals.
6
+ */
7
+
8
+ import type { Provider } from "ethers";
9
+ import { getVaultStatus } from "./utils";
10
+ import { getVaultTopology } from "./topology";
11
+
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+
14
+ export interface SpokeBalance {
15
+ chainId: number;
16
+ totalAssets: bigint;
17
+ /** false if the RPC call to this spoke failed */
18
+ isReachable: boolean;
19
+ }
20
+
21
+ export interface VaultDistribution {
22
+ hubChainId: number;
23
+ /** Underlying token balance idle on the hub (not deployed to strategies) */
24
+ hubLiquidBalance: bigint;
25
+ /** Hub totalAssets minus hubLiquidBalance (capital in Morpho, Aave, etc.) */
26
+ hubStrategyBalance: bigint;
27
+ /** Hub vault totalAssets() */
28
+ hubTotalAssets: bigint;
29
+ /** What the hub's accounting thinks is deployed on spokes */
30
+ spokesDeployedBalance: bigint;
31
+ /** Actual per-spoke balances read directly from each spoke chain */
32
+ spokeBalances: SpokeBalance[];
33
+ /** hubTotalAssets + sum of reachable spoke totalAssets */
34
+ totalActual: bigint;
35
+ oracleAccountingEnabled: boolean;
36
+ }
37
+
38
+ // ─────────────────────────────────────────────────────────────────────────────
39
+
40
+ /**
41
+ * Read the full cross-chain capital distribution of a vault.
42
+ *
43
+ * Queries the hub for its status, then reads `totalAssets()` on each spoke
44
+ * chain in parallel. Spoke calls are individually wrapped so a single
45
+ * unreachable RPC never fails the entire call.
46
+ *
47
+ * @param hubProvider Provider connected to the hub chain
48
+ * @param vault Vault address (same on all chains via CREATE3)
49
+ * @param spokeProviders Map of chainId → Provider for each spoke chain
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * const dist = await getVaultDistribution(baseProvider, VAULT, {
54
+ * [1]: ethProvider,
55
+ * [42161]: arbProvider,
56
+ * })
57
+ * console.log(`Hub liquid: ${dist.hubLiquidBalance}`)
58
+ * console.log(`Total actual: ${dist.totalActual}`)
59
+ * ```
60
+ */
61
+ export async function getVaultDistribution(
62
+ hubProvider: Provider,
63
+ vault: string,
64
+ spokeProviders: Record<number, Provider>,
65
+ ): Promise<VaultDistribution> {
66
+ // Read hub status
67
+ const hubStatus = await getVaultStatus(hubProvider, vault);
68
+
69
+ const hubChainId = Number((await hubProvider.getNetwork()).chainId);
70
+ const hubTotalAssets = hubStatus.totalAssets;
71
+ const hubLiquidBalance = hubStatus.hubLiquidBalance;
72
+ const hubStrategyBalance =
73
+ hubTotalAssets > hubLiquidBalance ? hubTotalAssets - hubLiquidBalance : 0n;
74
+
75
+ // Read each spoke's totalAssets in parallel, never throwing
76
+ const spokeEntries = Object.entries(spokeProviders).map(([chainIdStr, provider]) => ({
77
+ chainId: Number(chainIdStr),
78
+ provider,
79
+ }));
80
+
81
+ const spokeBalances: SpokeBalance[] = await Promise.all(
82
+ spokeEntries.map(async ({ chainId, provider }): Promise<SpokeBalance> => {
83
+ try {
84
+ const spokeStatus = await getVaultStatus(provider, vault);
85
+ return { chainId, totalAssets: spokeStatus.totalAssets, isReachable: true };
86
+ } catch {
87
+ return { chainId, totalAssets: 0n, isReachable: false };
88
+ }
89
+ }),
90
+ );
91
+
92
+ // totalActual = hub + reachable spokes
93
+ const reachableSpokeSum = spokeBalances
94
+ .filter((s) => s.isReachable)
95
+ .reduce((acc, s) => acc + s.totalAssets, 0n);
96
+
97
+ const totalActual = hubTotalAssets + reachableSpokeSum;
98
+
99
+ return {
100
+ hubChainId,
101
+ hubLiquidBalance,
102
+ hubStrategyBalance,
103
+ hubTotalAssets,
104
+ spokesDeployedBalance: hubStatus.spokesDeployedBalance,
105
+ spokeBalances,
106
+ totalActual,
107
+ oracleAccountingEnabled: hubStatus.oracleAccountingEnabled,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Hub-only distribution — uses topology to discover spokes but does NOT
113
+ * read spoke chains (no spoke providers needed).
114
+ *
115
+ * Returns hub data plus the list of spoke chainIds from the factory.
116
+ * `spokeBalances` will be empty — callers must provide spoke providers to
117
+ * `getVaultDistribution` for actual spoke reads.
118
+ *
119
+ * @param hubProvider Provider connected to the hub chain
120
+ * @param vault Vault address
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * const dist = await getVaultDistributionWithTopology(baseProvider, VAULT)
125
+ * // dist.spokeBalances === [] (no spoke providers provided)
126
+ * // dist.spokeChainIds tells you which chains to query
127
+ * ```
128
+ */
129
+ export async function getVaultDistributionWithTopology(
130
+ hubProvider: Provider,
131
+ vault: string,
132
+ ): Promise<VaultDistribution & { spokeChainIds: number[] }> {
133
+ // Read hub status and topology in parallel
134
+ const [hubStatus, topology] = await Promise.all([
135
+ getVaultStatus(hubProvider, vault),
136
+ getVaultTopology(hubProvider, vault),
137
+ ]);
138
+
139
+ const hubChainId = Number((await hubProvider.getNetwork()).chainId);
140
+ const hubTotalAssets = hubStatus.totalAssets;
141
+ const hubLiquidBalance = hubStatus.hubLiquidBalance;
142
+ const hubStrategyBalance =
143
+ hubTotalAssets > hubLiquidBalance ? hubTotalAssets - hubLiquidBalance : 0n;
144
+
145
+ return {
146
+ hubChainId,
147
+ hubLiquidBalance,
148
+ hubStrategyBalance,
149
+ hubTotalAssets,
150
+ spokesDeployedBalance: hubStatus.spokesDeployedBalance,
151
+ spokeBalances: [],
152
+ totalActual: hubTotalAssets, // hub-only, no spoke data
153
+ oracleAccountingEnabled: hubStatus.oracleAccountingEnabled,
154
+ spokeChainIds: topology.spokeChainIds,
155
+ };
156
+ }
@@ -15,6 +15,18 @@ export type {
15
15
  Signer,
16
16
  Provider,
17
17
  ContractTransactionReceipt,
18
+ // Curator types
19
+ SwapParams,
20
+ BatchSwapParams,
21
+ BridgeParams,
22
+ PendingAction,
23
+ SubmitActionsResult,
24
+ CuratorAction,
25
+ CuratorVaultStatus,
26
+ AssetInfo,
27
+ VaultAnalysis,
28
+ AssetBalance,
29
+ VaultAssetBreakdown,
18
30
  } from "./types";
19
31
  export { ActionType } from "./types";
20
32
 
@@ -27,6 +39,16 @@ export {
27
39
  OFT_ABI,
28
40
  METADATA_ABI,
29
41
  LZ_ENDPOINT_ABI,
42
+ // Curator ABIs
43
+ MULTICALL_ABI,
44
+ DEX_ABI,
45
+ BRIDGE_FACET_ABI,
46
+ ERC7540_FACET_ABI,
47
+ ERC4626_FACET_ABI,
48
+ CURATOR_CONFIG_ABI,
49
+ LZ_ADAPTER_ABI,
50
+ VAULT_ANALYSIS_ABI,
51
+ REGISTRY_ABI,
30
52
  } from "./abis";
31
53
 
32
54
  // --- Errors ---
@@ -60,6 +82,7 @@ export {
60
82
  quoteDepositFromSpokeFee,
61
83
  quoteComposeFee,
62
84
  executeCompose,
85
+ waitForCompose,
63
86
  } from "./crossChainFlows";
64
87
 
65
88
  // --- Redeem flows ---
@@ -72,7 +95,10 @@ export {
72
95
  smartRedeem,
73
96
  bridgeSharesToHub,
74
97
  bridgeAssetsToSpoke,
98
+ resolveRedeemAddresses,
99
+ quoteShareBridgeFee,
75
100
  } from "./redeemFlows";
101
+ export type { SpokeRedeemRoute } from "./redeemFlows";
76
102
 
77
103
  // --- Utilities ---
78
104
  export {
@@ -81,11 +107,18 @@ export {
81
107
  isAsyncMode,
82
108
  getAsyncRequestStatus,
83
109
  getVaultStatus,
110
+ detectStargateOft,
84
111
  } from "./utils";
85
112
  export type { VaultStatus, VaultMode } from "./utils";
86
113
 
87
114
  // --- Pre-flight validation ---
88
- export { preflightSync, preflightAsync, preflightRedeemLiquidity } from "./preflight";
115
+ export {
116
+ preflightSync,
117
+ preflightAsync,
118
+ preflightRedeemLiquidity,
119
+ preflightSpokeDeposit,
120
+ preflightSpokeRedeem,
121
+ } from "./preflight";
89
122
 
90
123
  // --- User Helpers ---
91
124
  export {
@@ -98,6 +131,7 @@ export {
98
131
  getUserBalances,
99
132
  getMaxWithdrawable,
100
133
  getVaultSummary,
134
+ getUserPositionMultiChain,
101
135
  } from "./userHelpers";
102
136
  export type {
103
137
  UserPosition,
@@ -109,7 +143,68 @@ export type {
109
143
  UserBalances,
110
144
  MaxWithdrawable,
111
145
  VaultSummary,
146
+ MultiChainUserPosition,
112
147
  } from "./userHelpers";
113
148
 
149
+ // --- Curator status reads ---
150
+ export {
151
+ getCuratorVaultStatus,
152
+ getPendingActions,
153
+ isCurator,
154
+ getVaultAnalysis,
155
+ checkProtocolWhitelist,
156
+ getVaultAssetBreakdown,
157
+ } from "./curatorStatus";
158
+
159
+ // --- Curator multicall writes ---
160
+ export {
161
+ encodeCuratorAction,
162
+ buildCuratorBatch,
163
+ submitActions,
164
+ executeActions,
165
+ vetoActions,
166
+ } from "./curatorMulticall";
167
+
168
+ // --- Curator swap helpers ---
169
+ export {
170
+ buildUniswapV3Swap,
171
+ encodeUniswapV3SwapCalldata,
172
+ } from "./curatorSwaps";
173
+
174
+ // --- Topology ---
175
+ export {
176
+ getVaultTopology,
177
+ getFullVaultTopology,
178
+ discoverVaultTopology,
179
+ isOnHubChain,
180
+ getAllVaultChainIds,
181
+ OMNI_FACTORY_ADDRESS,
182
+ } from "./topology";
183
+ export type { VaultTopology } from "./topology";
184
+
185
+ // --- Distribution ---
186
+ export {
187
+ getVaultDistribution,
188
+ getVaultDistributionWithTopology,
189
+ } from "./distribution";
190
+ export type { VaultDistribution, SpokeBalance } from "./distribution";
191
+
192
+ // --- Spoke routes ---
193
+ export {
194
+ getInboundRoutes,
195
+ getUserBalancesForRoutes,
196
+ getOutboundRoutes,
197
+ quoteRouteDepositFee,
198
+ NATIVE_SYMBOL,
199
+ } from "./spokeRoutes";
200
+ export type {
201
+ InboundRoute,
202
+ InboundRouteWithBalance,
203
+ OutboundRoute,
204
+ } from "./spokeRoutes";
205
+
206
+ // --- Chains ---
207
+ export { UNISWAP_V3_ROUTERS, OFT_ROUTES } from "./chains";
208
+
114
209
  // --- wagmi / ethers adapter compatibility ---
115
210
  export { asSdkSigner } from "./wagmiCompat";