@oydual31/more-vaults-sdk 0.1.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 +283 -0
- package/dist/ethers/index.cjs +973 -0
- package/dist/ethers/index.cjs.map +1 -0
- package/dist/ethers/index.d.cts +671 -0
- package/dist/ethers/index.d.ts +671 -0
- package/dist/ethers/index.js +924 -0
- package/dist/ethers/index.js.map +1 -0
- package/dist/viem/index.cjs +1426 -0
- package/dist/viem/index.cjs.map +1 -0
- package/dist/viem/index.d.cts +1291 -0
- package/dist/viem/index.d.ts +1291 -0
- package/dist/viem/index.js +1377 -0
- package/dist/viem/index.js.map +1 -0
- package/package.json +46 -0
- package/src/ethers/abis.ts +82 -0
- package/src/ethers/crossChainFlows.ts +206 -0
- package/src/ethers/depositFlows.ts +347 -0
- package/src/ethers/errors.ts +81 -0
- package/src/ethers/index.ts +103 -0
- package/src/ethers/preflight.ts +156 -0
- package/src/ethers/redeemFlows.ts +286 -0
- package/src/ethers/types.ts +67 -0
- package/src/ethers/userHelpers.ts +480 -0
- package/src/ethers/utils.ts +377 -0
- package/src/viem/abis.ts +392 -0
- package/src/viem/crossChainFlows.ts +220 -0
- package/src/viem/depositFlows.ts +331 -0
- package/src/viem/errors.ts +81 -0
- package/src/viem/index.ts +100 -0
- package/src/viem/preflight.ts +204 -0
- package/src/viem/redeemFlows.ts +337 -0
- package/src/viem/types.ts +56 -0
- package/src/viem/userHelpers.ts +489 -0
- package/src/viem/utils.ts +421 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import { type Address, type PublicClient, getAddress } from 'viem'
|
|
2
|
+
import { BRIDGE_ABI, CONFIG_ABI, ERC20_ABI, VAULT_ABI, METADATA_ABI } from './abis'
|
|
3
|
+
import type { CrossChainRequestInfo } from './types'
|
|
4
|
+
import { getVaultStatus } from './utils'
|
|
5
|
+
import type { VaultStatus } from './utils'
|
|
6
|
+
|
|
7
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface UserPosition {
|
|
10
|
+
/** Vault share balance */
|
|
11
|
+
shares: bigint
|
|
12
|
+
/** convertToAssets(shares) — what they'd get if they redeemed now */
|
|
13
|
+
estimatedAssets: bigint
|
|
14
|
+
/** Price of 1 full share in underlying (convertToAssets(10n ** decimals)) */
|
|
15
|
+
sharePrice: bigint
|
|
16
|
+
/** Vault decimals (for display) */
|
|
17
|
+
decimals: number
|
|
18
|
+
pendingWithdrawal: {
|
|
19
|
+
shares: bigint
|
|
20
|
+
timelockEndsAt: bigint
|
|
21
|
+
/** block.timestamp >= timelockEndsAt (or timelockEndsAt === 0n) */
|
|
22
|
+
canRedeemNow: boolean
|
|
23
|
+
} | null // null if no pending withdrawal request
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read the user's current position in the vault.
|
|
28
|
+
*
|
|
29
|
+
* @param publicClient Public client for reads
|
|
30
|
+
* @param vault Vault address (diamond proxy)
|
|
31
|
+
* @param user User wallet address
|
|
32
|
+
* @returns Full user position snapshot
|
|
33
|
+
*/
|
|
34
|
+
export async function getUserPosition(
|
|
35
|
+
publicClient: PublicClient,
|
|
36
|
+
vault: Address,
|
|
37
|
+
user: Address,
|
|
38
|
+
): Promise<UserPosition> {
|
|
39
|
+
const v = getAddress(vault)
|
|
40
|
+
const u = getAddress(user)
|
|
41
|
+
|
|
42
|
+
// First batch: balance, decimals, withdrawal request — via multicall
|
|
43
|
+
const [sharesResult, decimalsResult, withdrawalRequestResult] = await publicClient.multicall({
|
|
44
|
+
contracts: [
|
|
45
|
+
{ address: v, abi: VAULT_ABI, functionName: 'balanceOf', args: [u] },
|
|
46
|
+
{ address: v, abi: METADATA_ABI, functionName: 'decimals' },
|
|
47
|
+
{ address: v, abi: VAULT_ABI, functionName: 'getWithdrawalRequest', args: [u] },
|
|
48
|
+
],
|
|
49
|
+
allowFailure: false,
|
|
50
|
+
})
|
|
51
|
+
const block = await publicClient.getBlock()
|
|
52
|
+
const shares = sharesResult
|
|
53
|
+
const decimals = decimalsResult
|
|
54
|
+
const withdrawalRequest = withdrawalRequestResult
|
|
55
|
+
|
|
56
|
+
const [withdrawShares, timelockEndsAt] = withdrawalRequest as unknown as [bigint, bigint]
|
|
57
|
+
|
|
58
|
+
// Second batch: convertToAssets calls (need shares and decimals from first batch)
|
|
59
|
+
const oneShare = 10n ** BigInt(decimals)
|
|
60
|
+
const [estimatedAssets, sharePrice] = await Promise.all([
|
|
61
|
+
shares === 0n
|
|
62
|
+
? Promise.resolve(0n)
|
|
63
|
+
: publicClient.readContract({ address: v, abi: VAULT_ABI, functionName: 'convertToAssets', args: [shares] }),
|
|
64
|
+
publicClient.readContract({ address: v, abi: VAULT_ABI, functionName: 'convertToAssets', args: [oneShare] }),
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
const currentTimestamp = block.timestamp
|
|
68
|
+
|
|
69
|
+
const pendingWithdrawal =
|
|
70
|
+
withdrawShares === 0n
|
|
71
|
+
? null
|
|
72
|
+
: {
|
|
73
|
+
shares: withdrawShares,
|
|
74
|
+
timelockEndsAt,
|
|
75
|
+
canRedeemNow: timelockEndsAt === 0n || currentTimestamp >= timelockEndsAt,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
shares,
|
|
80
|
+
estimatedAssets,
|
|
81
|
+
sharePrice,
|
|
82
|
+
decimals,
|
|
83
|
+
pendingWithdrawal,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Preview how many shares a given asset amount would mint.
|
|
91
|
+
*
|
|
92
|
+
* @param publicClient Public client for reads
|
|
93
|
+
* @param vault Vault address
|
|
94
|
+
* @param assets Amount of underlying tokens to deposit
|
|
95
|
+
* @returns Estimated shares to be minted
|
|
96
|
+
*/
|
|
97
|
+
export async function previewDeposit(
|
|
98
|
+
publicClient: PublicClient,
|
|
99
|
+
vault: Address,
|
|
100
|
+
assets: bigint,
|
|
101
|
+
): Promise<bigint> {
|
|
102
|
+
return publicClient.readContract({
|
|
103
|
+
address: getAddress(vault),
|
|
104
|
+
abi: VAULT_ABI,
|
|
105
|
+
functionName: 'previewDeposit',
|
|
106
|
+
args: [assets],
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Preview how many underlying assets a given share amount would redeem.
|
|
114
|
+
*
|
|
115
|
+
* @param publicClient Public client for reads
|
|
116
|
+
* @param vault Vault address
|
|
117
|
+
* @param shares Amount of vault shares to redeem
|
|
118
|
+
* @returns Estimated assets to be returned
|
|
119
|
+
*/
|
|
120
|
+
export async function previewRedeem(
|
|
121
|
+
publicClient: PublicClient,
|
|
122
|
+
vault: Address,
|
|
123
|
+
shares: bigint,
|
|
124
|
+
): Promise<bigint> {
|
|
125
|
+
return publicClient.readContract({
|
|
126
|
+
address: getAddress(vault),
|
|
127
|
+
abi: VAULT_ABI,
|
|
128
|
+
functionName: 'previewRedeem',
|
|
129
|
+
args: [shares],
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
export type DepositBlockReason = 'paused' | 'capacity-full' | 'not-whitelisted' | 'ok'
|
|
136
|
+
|
|
137
|
+
export interface DepositEligibility {
|
|
138
|
+
allowed: boolean
|
|
139
|
+
reason: DepositBlockReason
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check whether a user is eligible to deposit into the vault right now.
|
|
144
|
+
*
|
|
145
|
+
* @param publicClient Public client for reads
|
|
146
|
+
* @param vault Vault address
|
|
147
|
+
* @param user User wallet address
|
|
148
|
+
* @returns Eligibility result with reason
|
|
149
|
+
*/
|
|
150
|
+
export async function canDeposit(
|
|
151
|
+
publicClient: PublicClient,
|
|
152
|
+
vault: Address,
|
|
153
|
+
user: Address,
|
|
154
|
+
): Promise<DepositEligibility> {
|
|
155
|
+
const v = getAddress(vault)
|
|
156
|
+
|
|
157
|
+
const isPaused = await publicClient.readContract({
|
|
158
|
+
address: v,
|
|
159
|
+
abi: CONFIG_ABI,
|
|
160
|
+
functionName: 'paused',
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
if (isPaused) {
|
|
164
|
+
return { allowed: false, reason: 'paused' }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// maxDeposit(user) can REVERT on vaults with whitelist/ACL
|
|
168
|
+
let maxDepositAmount: bigint
|
|
169
|
+
try {
|
|
170
|
+
maxDepositAmount = await publicClient.readContract({
|
|
171
|
+
address: v,
|
|
172
|
+
abi: CONFIG_ABI,
|
|
173
|
+
functionName: 'maxDeposit',
|
|
174
|
+
args: [getAddress(user)],
|
|
175
|
+
})
|
|
176
|
+
} catch {
|
|
177
|
+
// Revert means the vault has whitelist/ACL and this user is not approved
|
|
178
|
+
return { allowed: false, reason: 'not-whitelisted' }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (maxDepositAmount === 0n) {
|
|
182
|
+
return { allowed: false, reason: 'capacity-full' }
|
|
183
|
+
}
|
|
184
|
+
return { allowed: true, reason: 'ok' }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
export interface VaultMetadata {
|
|
190
|
+
name: string
|
|
191
|
+
symbol: string
|
|
192
|
+
decimals: number
|
|
193
|
+
underlying: Address
|
|
194
|
+
underlyingSymbol: string
|
|
195
|
+
underlyingDecimals: number
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Read display metadata for a vault and its underlying token.
|
|
200
|
+
*
|
|
201
|
+
* @param publicClient Public client for reads
|
|
202
|
+
* @param vault Vault address
|
|
203
|
+
* @returns Vault and underlying token metadata
|
|
204
|
+
*/
|
|
205
|
+
export async function getVaultMetadata(
|
|
206
|
+
publicClient: PublicClient,
|
|
207
|
+
vault: Address,
|
|
208
|
+
): Promise<VaultMetadata> {
|
|
209
|
+
const v = getAddress(vault)
|
|
210
|
+
|
|
211
|
+
// Batch 1: vault name, symbol, decimals, underlying — 1 eth_call via multicall
|
|
212
|
+
const b1 = await publicClient.multicall({
|
|
213
|
+
contracts: [
|
|
214
|
+
{ address: v, abi: METADATA_ABI, functionName: 'name' },
|
|
215
|
+
{ address: v, abi: METADATA_ABI, functionName: 'symbol' },
|
|
216
|
+
{ address: v, abi: METADATA_ABI, functionName: 'decimals' },
|
|
217
|
+
{ address: v, abi: VAULT_ABI, functionName: 'asset' },
|
|
218
|
+
] as const,
|
|
219
|
+
allowFailure: false,
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const [name, symbol, decimals, underlying] = b1
|
|
223
|
+
const underlyingAddr = getAddress(underlying as Address)
|
|
224
|
+
|
|
225
|
+
// Batch 2: underlying symbol + decimals — 1 eth_call via multicall
|
|
226
|
+
const b2 = await publicClient.multicall({
|
|
227
|
+
contracts: [
|
|
228
|
+
{ address: underlyingAddr, abi: METADATA_ABI, functionName: 'symbol' },
|
|
229
|
+
{ address: underlyingAddr, abi: METADATA_ABI, functionName: 'decimals' },
|
|
230
|
+
] as const,
|
|
231
|
+
allowFailure: false,
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const [underlyingSymbol, underlyingDecimals] = b2
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
name,
|
|
238
|
+
symbol,
|
|
239
|
+
decimals,
|
|
240
|
+
underlying: underlyingAddr,
|
|
241
|
+
underlyingSymbol,
|
|
242
|
+
underlyingDecimals,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
247
|
+
|
|
248
|
+
export type AsyncRequestStatus = 'pending' | 'ready-to-execute' | 'completed' | 'refunded'
|
|
249
|
+
|
|
250
|
+
export interface AsyncRequestStatusInfo {
|
|
251
|
+
status: AsyncRequestStatus
|
|
252
|
+
/** Human-readable description */
|
|
253
|
+
label: string
|
|
254
|
+
/** Shares minted or assets returned (0 if still pending) */
|
|
255
|
+
result: bigint
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get the human-readable status of an async cross-chain request.
|
|
260
|
+
*
|
|
261
|
+
* @param publicClient Public client for reads
|
|
262
|
+
* @param vault Vault address
|
|
263
|
+
* @param guid Request GUID returned by depositAsync / mintAsync / redeemAsync
|
|
264
|
+
* @returns Status info with label and result
|
|
265
|
+
*/
|
|
266
|
+
export async function getAsyncRequestStatusLabel(
|
|
267
|
+
publicClient: PublicClient,
|
|
268
|
+
vault: Address,
|
|
269
|
+
guid: `0x${string}`,
|
|
270
|
+
): Promise<AsyncRequestStatusInfo> {
|
|
271
|
+
const v = getAddress(vault)
|
|
272
|
+
|
|
273
|
+
const [info, finalizationResult] = await Promise.all([
|
|
274
|
+
publicClient.readContract({
|
|
275
|
+
address: v,
|
|
276
|
+
abi: BRIDGE_ABI,
|
|
277
|
+
functionName: 'getRequestInfo',
|
|
278
|
+
args: [guid],
|
|
279
|
+
}) as Promise<CrossChainRequestInfo>,
|
|
280
|
+
publicClient.readContract({
|
|
281
|
+
address: v,
|
|
282
|
+
abi: BRIDGE_ABI,
|
|
283
|
+
functionName: 'getFinalizationResult',
|
|
284
|
+
args: [guid],
|
|
285
|
+
}),
|
|
286
|
+
])
|
|
287
|
+
|
|
288
|
+
if (info.refunded) {
|
|
289
|
+
return {
|
|
290
|
+
status: 'refunded',
|
|
291
|
+
label: 'Request refunded — tokens returned to initiator',
|
|
292
|
+
result: 0n,
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (info.finalized) {
|
|
296
|
+
return {
|
|
297
|
+
status: 'completed',
|
|
298
|
+
label: 'Completed',
|
|
299
|
+
result: finalizationResult,
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (info.fulfilled) {
|
|
303
|
+
return {
|
|
304
|
+
status: 'ready-to-execute',
|
|
305
|
+
label: 'Oracle responded — ready to execute',
|
|
306
|
+
result: 0n,
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
status: 'pending',
|
|
311
|
+
label: 'Waiting for cross-chain oracle response...',
|
|
312
|
+
result: 0n,
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
317
|
+
|
|
318
|
+
export interface UserBalances {
|
|
319
|
+
/** Vault shares the user holds */
|
|
320
|
+
shareBalance: bigint
|
|
321
|
+
/** Underlying token balance in wallet (for deposit input) */
|
|
322
|
+
underlyingBalance: bigint
|
|
323
|
+
/** convertToAssets(shareBalance) — vault position value */
|
|
324
|
+
estimatedAssets: bigint
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Read the user's token balances relevant to a vault.
|
|
329
|
+
*
|
|
330
|
+
* @param publicClient Public client for reads
|
|
331
|
+
* @param vault Vault address
|
|
332
|
+
* @param user User wallet address
|
|
333
|
+
* @returns Share balance, underlying wallet balance, and estimated assets
|
|
334
|
+
*/
|
|
335
|
+
export async function getUserBalances(
|
|
336
|
+
publicClient: PublicClient,
|
|
337
|
+
vault: Address,
|
|
338
|
+
user: Address,
|
|
339
|
+
): Promise<UserBalances> {
|
|
340
|
+
const v = getAddress(vault)
|
|
341
|
+
const u = getAddress(user)
|
|
342
|
+
|
|
343
|
+
// Batch 1: get underlying address, share balance, decimals
|
|
344
|
+
const [shareBalance, , underlying] = await publicClient.multicall({
|
|
345
|
+
contracts: [
|
|
346
|
+
{ address: v, abi: VAULT_ABI, functionName: 'balanceOf', args: [u] },
|
|
347
|
+
{ address: v, abi: METADATA_ABI, functionName: 'decimals' },
|
|
348
|
+
{ address: v, abi: VAULT_ABI, functionName: 'asset' },
|
|
349
|
+
],
|
|
350
|
+
allowFailure: false,
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
const underlyingAddr = getAddress(underlying)
|
|
354
|
+
|
|
355
|
+
// Batch 2: underlying balance + estimated assets (skip convertToAssets if no shares)
|
|
356
|
+
const [underlyingBalance, estimatedAssets] = await Promise.all([
|
|
357
|
+
publicClient.readContract({
|
|
358
|
+
address: underlyingAddr,
|
|
359
|
+
abi: ERC20_ABI,
|
|
360
|
+
functionName: 'balanceOf',
|
|
361
|
+
args: [u],
|
|
362
|
+
}),
|
|
363
|
+
shareBalance === 0n
|
|
364
|
+
? Promise.resolve(0n)
|
|
365
|
+
: publicClient.readContract({
|
|
366
|
+
address: v,
|
|
367
|
+
abi: VAULT_ABI,
|
|
368
|
+
functionName: 'convertToAssets',
|
|
369
|
+
args: [shareBalance],
|
|
370
|
+
}),
|
|
371
|
+
])
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
shareBalance,
|
|
375
|
+
underlyingBalance,
|
|
376
|
+
estimatedAssets,
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
381
|
+
|
|
382
|
+
export interface MaxWithdrawable {
|
|
383
|
+
/** How many shares can be redeemed right now */
|
|
384
|
+
shares: bigint
|
|
385
|
+
/** How many underlying assets that corresponds to */
|
|
386
|
+
assets: bigint
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Calculate the maximum amount a user can withdraw from a vault right now.
|
|
391
|
+
*
|
|
392
|
+
* For hub vaults without oracle accounting, this is limited by hub liquidity.
|
|
393
|
+
* For local and oracle vaults, all assets are immediately redeemable.
|
|
394
|
+
*
|
|
395
|
+
* @param publicClient Public client for reads
|
|
396
|
+
* @param vault Vault address
|
|
397
|
+
* @param user User wallet address
|
|
398
|
+
* @returns Maximum withdrawable shares and assets
|
|
399
|
+
*/
|
|
400
|
+
export async function getMaxWithdrawable(
|
|
401
|
+
publicClient: PublicClient,
|
|
402
|
+
vault: Address,
|
|
403
|
+
user: Address,
|
|
404
|
+
): Promise<MaxWithdrawable> {
|
|
405
|
+
const v = getAddress(vault)
|
|
406
|
+
const u = getAddress(user)
|
|
407
|
+
|
|
408
|
+
// Batch 1: isHub, oraclesCrossChainAccounting, user share balance, underlying address
|
|
409
|
+
const [isHub, oraclesEnabled, userShares, underlying] = await publicClient.multicall({
|
|
410
|
+
contracts: [
|
|
411
|
+
{ address: v, abi: CONFIG_ABI, functionName: 'isHub' },
|
|
412
|
+
{ address: v, abi: BRIDGE_ABI, functionName: 'oraclesCrossChainAccounting' },
|
|
413
|
+
{ address: v, abi: VAULT_ABI, functionName: 'balanceOf', args: [u] },
|
|
414
|
+
{ address: v, abi: VAULT_ABI, functionName: 'asset' },
|
|
415
|
+
],
|
|
416
|
+
allowFailure: false,
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
if (userShares === 0n) {
|
|
420
|
+
return { shares: 0n, assets: 0n }
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const underlyingAddr = getAddress(underlying)
|
|
424
|
+
|
|
425
|
+
// Batch 2: estimated assets for user shares + hub liquid balance
|
|
426
|
+
const [estimatedAssets, hubLiquidBalance] = await Promise.all([
|
|
427
|
+
publicClient.readContract({
|
|
428
|
+
address: v,
|
|
429
|
+
abi: VAULT_ABI,
|
|
430
|
+
functionName: 'convertToAssets',
|
|
431
|
+
args: [userShares],
|
|
432
|
+
}),
|
|
433
|
+
publicClient.readContract({
|
|
434
|
+
address: underlyingAddr,
|
|
435
|
+
abi: ERC20_ABI,
|
|
436
|
+
functionName: 'balanceOf',
|
|
437
|
+
args: [v],
|
|
438
|
+
}),
|
|
439
|
+
])
|
|
440
|
+
|
|
441
|
+
let maxAssets: bigint
|
|
442
|
+
if (isHub && !oraclesEnabled) {
|
|
443
|
+
// Hub vault: limited by hub liquidity
|
|
444
|
+
maxAssets = estimatedAssets < hubLiquidBalance ? estimatedAssets : hubLiquidBalance
|
|
445
|
+
} else {
|
|
446
|
+
// Local or oracle vault: all assets redeemable
|
|
447
|
+
maxAssets = estimatedAssets
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Convert back to shares if limited by hub liquidity
|
|
451
|
+
let maxShares: bigint
|
|
452
|
+
if (maxAssets < estimatedAssets) {
|
|
453
|
+
maxShares = await publicClient.readContract({
|
|
454
|
+
address: v,
|
|
455
|
+
abi: VAULT_ABI,
|
|
456
|
+
functionName: 'convertToShares',
|
|
457
|
+
args: [maxAssets],
|
|
458
|
+
})
|
|
459
|
+
} else {
|
|
460
|
+
maxShares = userShares
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
shares: maxShares,
|
|
465
|
+
assets: maxAssets,
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
470
|
+
|
|
471
|
+
export type VaultSummary = VaultStatus & VaultMetadata
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Get a combined snapshot of vault status and metadata in one call.
|
|
475
|
+
*
|
|
476
|
+
* @param publicClient Public client for reads
|
|
477
|
+
* @param vault Vault address
|
|
478
|
+
* @returns Merged VaultStatus and VaultMetadata
|
|
479
|
+
*/
|
|
480
|
+
export async function getVaultSummary(
|
|
481
|
+
publicClient: PublicClient,
|
|
482
|
+
vault: Address,
|
|
483
|
+
): Promise<VaultSummary> {
|
|
484
|
+
const [status, metadata] = await Promise.all([
|
|
485
|
+
getVaultStatus(publicClient, vault),
|
|
486
|
+
getVaultMetadata(publicClient, vault),
|
|
487
|
+
])
|
|
488
|
+
return { ...status, ...metadata }
|
|
489
|
+
}
|