@oydual31/more-vaults-sdk 0.2.9 → 0.3.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.
- package/README.md +25 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/{spokeRoutes-FgKCJQYa.d.cts → spokeRoutes-DK7cIW4z.d.cts} +4 -0
- package/dist/{spokeRoutes-FgKCJQYa.d.ts → spokeRoutes-DK7cIW4z.d.ts} +4 -0
- package/dist/viem/index.cjs +604 -5
- package/dist/viem/index.cjs.map +1 -1
- package/dist/viem/index.d.cts +721 -3
- package/dist/viem/index.d.ts +721 -3
- package/dist/viem/index.js +587 -7
- package/dist/viem/index.js.map +1 -1
- package/package.json +1 -1
- package/src/viem/abis.ts +329 -0
- package/src/viem/curatorMulticall.ts +299 -0
- package/src/viem/curatorStatus.ts +255 -0
- package/src/viem/index.ts +34 -0
- package/src/viem/types.ts +79 -0
- package/src/viem/userHelpers.ts +22 -6
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curator / vault-manager read helpers for the MoreVaults SDK.
|
|
3
|
+
*
|
|
4
|
+
* All functions are read-only (no wallet needed) and use multicall for
|
|
5
|
+
* batched RPC efficiency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type Address, type PublicClient, getAddress } from 'viem'
|
|
9
|
+
import { MULTICALL_ABI, CURATOR_CONFIG_ABI, VAULT_ANALYSIS_ABI, REGISTRY_ABI, METADATA_ABI } from './abis.js'
|
|
10
|
+
import type { CuratorVaultStatus, PendingAction, VaultAnalysis, AssetInfo } from './types.js'
|
|
11
|
+
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Read a comprehensive status snapshot for the curator dashboard.
|
|
16
|
+
*
|
|
17
|
+
* Fetches in two batches (multicall) to minimise round trips:
|
|
18
|
+
* Batch 1: curator, timeLockPeriod, getMaxSlippagePercent, getCurrentNonce,
|
|
19
|
+
* getAvailableAssets, getCrossChainAccountingManager, paused
|
|
20
|
+
*
|
|
21
|
+
* @param publicClient Viem public client (must be on the vault's chain)
|
|
22
|
+
* @param vault Vault address (diamond proxy)
|
|
23
|
+
* @returns CuratorVaultStatus snapshot
|
|
24
|
+
*/
|
|
25
|
+
export async function getCuratorVaultStatus(
|
|
26
|
+
publicClient: PublicClient,
|
|
27
|
+
vault: Address,
|
|
28
|
+
): Promise<CuratorVaultStatus> {
|
|
29
|
+
const v = getAddress(vault)
|
|
30
|
+
|
|
31
|
+
const [
|
|
32
|
+
curator,
|
|
33
|
+
timeLockPeriod,
|
|
34
|
+
maxSlippagePercent,
|
|
35
|
+
currentNonce,
|
|
36
|
+
availableAssets,
|
|
37
|
+
lzAdapter,
|
|
38
|
+
paused,
|
|
39
|
+
] = await publicClient.multicall({
|
|
40
|
+
contracts: [
|
|
41
|
+
{ address: v, abi: CURATOR_CONFIG_ABI, functionName: 'curator' },
|
|
42
|
+
{ address: v, abi: CURATOR_CONFIG_ABI, functionName: 'timeLockPeriod' },
|
|
43
|
+
{ address: v, abi: CURATOR_CONFIG_ABI, functionName: 'getMaxSlippagePercent' },
|
|
44
|
+
{ address: v, abi: MULTICALL_ABI, functionName: 'getCurrentNonce' },
|
|
45
|
+
{ address: v, abi: CURATOR_CONFIG_ABI, functionName: 'getAvailableAssets' },
|
|
46
|
+
{ address: v, abi: CURATOR_CONFIG_ABI, functionName: 'getCrossChainAccountingManager' },
|
|
47
|
+
{ address: v, abi: CURATOR_CONFIG_ABI, functionName: 'paused' },
|
|
48
|
+
],
|
|
49
|
+
allowFailure: false,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
curator: getAddress(curator as Address),
|
|
54
|
+
timeLockPeriod: timeLockPeriod as bigint,
|
|
55
|
+
maxSlippagePercent: maxSlippagePercent as bigint,
|
|
56
|
+
currentNonce: currentNonce as bigint,
|
|
57
|
+
availableAssets: (availableAssets as Address[]).map(getAddress),
|
|
58
|
+
lzAdapter: getAddress(lzAdapter as Address),
|
|
59
|
+
paused: paused as boolean,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Fetch pending actions for a specific nonce and resolve whether they are
|
|
67
|
+
* executable (i.e. the timelock has expired).
|
|
68
|
+
*
|
|
69
|
+
* @param publicClient Viem public client (must be on the vault's chain)
|
|
70
|
+
* @param vault Vault address (diamond proxy)
|
|
71
|
+
* @param nonce Action nonce to query
|
|
72
|
+
* @returns PendingAction with isExecutable flag set
|
|
73
|
+
*/
|
|
74
|
+
export async function getPendingActions(
|
|
75
|
+
publicClient: PublicClient,
|
|
76
|
+
vault: Address,
|
|
77
|
+
nonce: bigint,
|
|
78
|
+
): Promise<PendingAction> {
|
|
79
|
+
const v = getAddress(vault)
|
|
80
|
+
|
|
81
|
+
const [actionsResult, block] = await Promise.all([
|
|
82
|
+
publicClient.readContract({
|
|
83
|
+
address: v,
|
|
84
|
+
abi: MULTICALL_ABI,
|
|
85
|
+
functionName: 'getPendingActions',
|
|
86
|
+
args: [nonce],
|
|
87
|
+
}),
|
|
88
|
+
publicClient.getBlock(),
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
const [actionsData, pendingUntil] = actionsResult as [`0x${string}`[], bigint]
|
|
92
|
+
const isExecutable = pendingUntil > 0n && block.timestamp >= pendingUntil
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
nonce,
|
|
96
|
+
actionsData,
|
|
97
|
+
pendingUntil,
|
|
98
|
+
isExecutable,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check whether a given address is the curator of the vault.
|
|
106
|
+
*
|
|
107
|
+
* @param publicClient Viem public client (must be on the vault's chain)
|
|
108
|
+
* @param vault Vault address (diamond proxy)
|
|
109
|
+
* @param address Address to check
|
|
110
|
+
* @returns true if address is the current curator
|
|
111
|
+
*/
|
|
112
|
+
export async function isCurator(
|
|
113
|
+
publicClient: PublicClient,
|
|
114
|
+
vault: Address,
|
|
115
|
+
address: Address,
|
|
116
|
+
): Promise<boolean> {
|
|
117
|
+
const curatorAddress = await publicClient.readContract({
|
|
118
|
+
address: getAddress(vault),
|
|
119
|
+
abi: CURATOR_CONFIG_ABI,
|
|
120
|
+
functionName: 'curator',
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
return getAddress(curatorAddress as Address) === getAddress(address)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Full vault analysis — available assets with metadata, depositable assets, whitelist config.
|
|
130
|
+
* Useful for curator dashboards to understand what the vault can do.
|
|
131
|
+
*
|
|
132
|
+
* @param publicClient Viem public client (must be on the vault's chain)
|
|
133
|
+
* @param vault Vault address (diamond proxy)
|
|
134
|
+
* @returns VaultAnalysis snapshot
|
|
135
|
+
*/
|
|
136
|
+
export async function getVaultAnalysis(
|
|
137
|
+
publicClient: PublicClient,
|
|
138
|
+
vault: Address,
|
|
139
|
+
): Promise<VaultAnalysis> {
|
|
140
|
+
const v = getAddress(vault)
|
|
141
|
+
|
|
142
|
+
// Batch 1: fetch asset lists, whitelist flag, and registry address in parallel
|
|
143
|
+
const [availableRaw, depositableRaw, depositWhitelistEnabled, registryResult] =
|
|
144
|
+
await Promise.all([
|
|
145
|
+
publicClient.readContract({
|
|
146
|
+
address: v,
|
|
147
|
+
abi: VAULT_ANALYSIS_ABI,
|
|
148
|
+
functionName: 'getAvailableAssets',
|
|
149
|
+
}),
|
|
150
|
+
publicClient.readContract({
|
|
151
|
+
address: v,
|
|
152
|
+
abi: VAULT_ANALYSIS_ABI,
|
|
153
|
+
functionName: 'getDepositableAssets',
|
|
154
|
+
}),
|
|
155
|
+
publicClient.readContract({
|
|
156
|
+
address: v,
|
|
157
|
+
abi: VAULT_ANALYSIS_ABI,
|
|
158
|
+
functionName: 'isDepositWhitelistEnabled',
|
|
159
|
+
}),
|
|
160
|
+
publicClient.readContract({
|
|
161
|
+
address: v,
|
|
162
|
+
abi: VAULT_ANALYSIS_ABI,
|
|
163
|
+
functionName: 'moreVaultsRegistry',
|
|
164
|
+
}).catch(() => null),
|
|
165
|
+
])
|
|
166
|
+
|
|
167
|
+
const availableAddresses = (availableRaw as Address[]).map(getAddress)
|
|
168
|
+
const depositableAddresses = (depositableRaw as Address[]).map(getAddress)
|
|
169
|
+
|
|
170
|
+
// Deduplicated set of all asset addresses we need metadata for
|
|
171
|
+
const allAddresses = Array.from(new Set([...availableAddresses, ...depositableAddresses]))
|
|
172
|
+
|
|
173
|
+
// Batch 2: multicall for name/symbol/decimals on all unique assets
|
|
174
|
+
const metadataCalls = allAddresses.flatMap((addr) => [
|
|
175
|
+
{ address: addr, abi: METADATA_ABI, functionName: 'name' as const },
|
|
176
|
+
{ address: addr, abi: METADATA_ABI, functionName: 'symbol' as const },
|
|
177
|
+
{ address: addr, abi: METADATA_ABI, functionName: 'decimals' as const },
|
|
178
|
+
])
|
|
179
|
+
|
|
180
|
+
const metadataResults = allAddresses.length > 0
|
|
181
|
+
? await publicClient.multicall({ contracts: metadataCalls, allowFailure: true })
|
|
182
|
+
: []
|
|
183
|
+
|
|
184
|
+
// Map address → AssetInfo
|
|
185
|
+
const assetInfoMap = new Map<Address, AssetInfo>()
|
|
186
|
+
for (let i = 0; i < allAddresses.length; i++) {
|
|
187
|
+
const addr = allAddresses[i]
|
|
188
|
+
const nameResult = metadataResults[i * 3]
|
|
189
|
+
const symbolResult = metadataResults[i * 3 + 1]
|
|
190
|
+
const decimalsResult = metadataResults[i * 3 + 2]
|
|
191
|
+
|
|
192
|
+
assetInfoMap.set(addr, {
|
|
193
|
+
address: addr,
|
|
194
|
+
name: nameResult?.status === 'success' ? (nameResult.result as string) : '',
|
|
195
|
+
symbol: symbolResult?.status === 'success' ? (symbolResult.result as string) : '',
|
|
196
|
+
decimals: decimalsResult?.status === 'success' ? (decimalsResult.result as number) : 18,
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const registryAddress = registryResult ? getAddress(registryResult as Address) : null
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
availableAssets: availableAddresses.map((a) => assetInfoMap.get(a)!),
|
|
204
|
+
depositableAssets: depositableAddresses.map((a) => assetInfoMap.get(a)!),
|
|
205
|
+
depositWhitelistEnabled: depositWhitelistEnabled as boolean,
|
|
206
|
+
registryAddress,
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if specific protocol addresses are whitelisted in the global registry.
|
|
214
|
+
* Useful for curators to verify DEX routers before building swap calldata.
|
|
215
|
+
*
|
|
216
|
+
* @param publicClient Viem public client (must be on the vault's chain)
|
|
217
|
+
* @param vault Vault address (diamond proxy)
|
|
218
|
+
* @param protocols Protocol addresses to check
|
|
219
|
+
* @returns Record mapping address → whitelisted boolean
|
|
220
|
+
*/
|
|
221
|
+
export async function checkProtocolWhitelist(
|
|
222
|
+
publicClient: PublicClient,
|
|
223
|
+
vault: Address,
|
|
224
|
+
protocols: Address[],
|
|
225
|
+
): Promise<Record<string, boolean>> {
|
|
226
|
+
const v = getAddress(vault)
|
|
227
|
+
|
|
228
|
+
const registryRaw = await publicClient.readContract({
|
|
229
|
+
address: v,
|
|
230
|
+
abi: VAULT_ANALYSIS_ABI,
|
|
231
|
+
functionName: 'moreVaultsRegistry',
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const registry = getAddress(registryRaw as Address)
|
|
235
|
+
|
|
236
|
+
if (protocols.length === 0) return {}
|
|
237
|
+
|
|
238
|
+
const results = await publicClient.multicall({
|
|
239
|
+
contracts: protocols.map((protocol) => ({
|
|
240
|
+
address: registry,
|
|
241
|
+
abi: REGISTRY_ABI,
|
|
242
|
+
functionName: 'isWhitelisted' as const,
|
|
243
|
+
args: [getAddress(protocol)] as [Address],
|
|
244
|
+
})),
|
|
245
|
+
allowFailure: true,
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const out: Record<string, boolean> = {}
|
|
249
|
+
for (let i = 0; i < protocols.length; i++) {
|
|
250
|
+
const r = results[i]
|
|
251
|
+
out[getAddress(protocols[i])] = r?.status === 'success' ? (r.result as boolean) : false
|
|
252
|
+
}
|
|
253
|
+
return out
|
|
254
|
+
}
|
|
255
|
+
|
package/src/viem/index.ts
CHANGED
|
@@ -13,6 +13,15 @@ export {
|
|
|
13
13
|
OFT_ABI,
|
|
14
14
|
METADATA_ABI,
|
|
15
15
|
LZ_ENDPOINT_ABI,
|
|
16
|
+
MULTICALL_ABI,
|
|
17
|
+
DEX_ABI,
|
|
18
|
+
BRIDGE_FACET_ABI,
|
|
19
|
+
ERC7540_FACET_ABI,
|
|
20
|
+
ERC4626_FACET_ABI,
|
|
21
|
+
CURATOR_CONFIG_ABI,
|
|
22
|
+
LZ_ADAPTER_ABI,
|
|
23
|
+
VAULT_ANALYSIS_ABI,
|
|
24
|
+
REGISTRY_ABI,
|
|
16
25
|
} from './abis'
|
|
17
26
|
|
|
18
27
|
// --- Types ---
|
|
@@ -25,6 +34,15 @@ export type {
|
|
|
25
34
|
ActionTypeValue,
|
|
26
35
|
ComposeData,
|
|
27
36
|
SpokeDepositResult,
|
|
37
|
+
SwapParams,
|
|
38
|
+
BatchSwapParams,
|
|
39
|
+
BridgeParams,
|
|
40
|
+
PendingAction,
|
|
41
|
+
SubmitActionsResult,
|
|
42
|
+
CuratorAction,
|
|
43
|
+
CuratorVaultStatus,
|
|
44
|
+
AssetInfo,
|
|
45
|
+
VaultAnalysis,
|
|
28
46
|
} from './types'
|
|
29
47
|
export { ActionType } from './types'
|
|
30
48
|
|
|
@@ -137,6 +155,22 @@ export type { VaultDistribution, SpokeBalance } from './distribution'
|
|
|
137
155
|
export { getInboundRoutes, getUserBalancesForRoutes, getOutboundRoutes, quoteRouteDepositFee, NATIVE_SYMBOL } from './spokeRoutes'
|
|
138
156
|
export type { InboundRoute, InboundRouteWithBalance, OutboundRoute } from './spokeRoutes'
|
|
139
157
|
|
|
158
|
+
// --- Curator Operations ---
|
|
159
|
+
export {
|
|
160
|
+
getCuratorVaultStatus,
|
|
161
|
+
getPendingActions,
|
|
162
|
+
isCurator,
|
|
163
|
+
getVaultAnalysis,
|
|
164
|
+
checkProtocolWhitelist,
|
|
165
|
+
} from './curatorStatus'
|
|
166
|
+
export {
|
|
167
|
+
encodeCuratorAction,
|
|
168
|
+
buildCuratorBatch,
|
|
169
|
+
submitActions,
|
|
170
|
+
executeActions,
|
|
171
|
+
vetoActions,
|
|
172
|
+
} from './curatorMulticall'
|
|
173
|
+
|
|
140
174
|
// --- wagmi compatibility ---
|
|
141
175
|
// Re-export viem's PublicClient type for wagmi compatibility.
|
|
142
176
|
// wagmi's usePublicClient() returns a type that is structurally compatible
|
package/src/viem/types.ts
CHANGED
|
@@ -98,3 +98,82 @@ export interface CrossChainRequestInfo {
|
|
|
98
98
|
finalizationResult: bigint
|
|
99
99
|
amountLimit: bigint
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
+
// Curator Operations Types
|
|
104
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
export interface SwapParams {
|
|
107
|
+
targetContract: Address
|
|
108
|
+
tokenIn: Address
|
|
109
|
+
tokenOut: Address
|
|
110
|
+
maxAmountIn: bigint
|
|
111
|
+
minAmountOut: bigint
|
|
112
|
+
swapCallData: `0x${string}`
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface BatchSwapParams {
|
|
116
|
+
swaps: SwapParams[]
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface BridgeParams {
|
|
120
|
+
oftToken: Address
|
|
121
|
+
dstEid: number
|
|
122
|
+
amount: bigint
|
|
123
|
+
dstVault: Address
|
|
124
|
+
refundAddress: Address
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface PendingAction {
|
|
128
|
+
nonce: bigint
|
|
129
|
+
actionsData: `0x${string}`[]
|
|
130
|
+
pendingUntil: bigint
|
|
131
|
+
isExecutable: boolean
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface SubmitActionsResult {
|
|
135
|
+
txHash: `0x${string}`
|
|
136
|
+
nonce: bigint
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type CuratorAction =
|
|
140
|
+
| { type: 'swap'; params: SwapParams }
|
|
141
|
+
| { type: 'batchSwap'; params: BatchSwapParams }
|
|
142
|
+
| { type: 'erc4626Deposit'; vault: Address; assets: bigint }
|
|
143
|
+
| { type: 'erc4626Redeem'; vault: Address; shares: bigint }
|
|
144
|
+
| { type: 'erc7540RequestDeposit'; vault: Address; assets: bigint }
|
|
145
|
+
| { type: 'erc7540Deposit'; vault: Address; assets: bigint }
|
|
146
|
+
| { type: 'erc7540RequestRedeem'; vault: Address; shares: bigint }
|
|
147
|
+
| { type: 'erc7540Redeem'; vault: Address; shares: bigint }
|
|
148
|
+
|
|
149
|
+
export interface CuratorVaultStatus {
|
|
150
|
+
curator: Address
|
|
151
|
+
timeLockPeriod: bigint
|
|
152
|
+
maxSlippagePercent: bigint
|
|
153
|
+
currentNonce: bigint
|
|
154
|
+
availableAssets: Address[]
|
|
155
|
+
lzAdapter: Address
|
|
156
|
+
paused: boolean
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
160
|
+
// Vault Analysis Types
|
|
161
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
export interface AssetInfo {
|
|
164
|
+
address: Address
|
|
165
|
+
symbol: string
|
|
166
|
+
name: string
|
|
167
|
+
decimals: number
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface VaultAnalysis {
|
|
171
|
+
/** All tokens the vault can hold/swap (curator-managed) */
|
|
172
|
+
availableAssets: AssetInfo[]
|
|
173
|
+
/** Tokens users can deposit */
|
|
174
|
+
depositableAssets: AssetInfo[]
|
|
175
|
+
/** Whether deposit whitelist is enabled (restricts who can deposit) */
|
|
176
|
+
depositWhitelistEnabled: boolean
|
|
177
|
+
/** Registry address for global protocol whitelist checks */
|
|
178
|
+
registryAddress: Address | null
|
|
179
|
+
}
|
package/src/viem/userHelpers.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Address, type PublicClient, getAddress } from 'viem'
|
|
2
|
-
import { BRIDGE_ABI, CONFIG_ABI, ERC20_ABI, VAULT_ABI, METADATA_ABI, OFT_ABI } from './abis'
|
|
2
|
+
import { BRIDGE_ABI, CONFIG_ABI, ERC20_ABI, VAULT_ABI, METADATA_ABI, OFT_ABI, VAULT_ANALYSIS_ABI } from './abis'
|
|
3
3
|
import type { CrossChainRequestInfo } from './types'
|
|
4
4
|
import { getVaultStatus } from './utils'
|
|
5
5
|
import type { VaultStatus } from './utils'
|
|
@@ -140,6 +140,10 @@ export type DepositBlockReason = 'paused' | 'capacity-full' | 'not-whitelisted'
|
|
|
140
140
|
export interface DepositEligibility {
|
|
141
141
|
allowed: boolean
|
|
142
142
|
reason: DepositBlockReason
|
|
143
|
+
/** Max deposit amount for this user (0n if capacity full or not whitelisted) */
|
|
144
|
+
maxDeposit?: bigint
|
|
145
|
+
/** Whether the vault restricts deposits to whitelisted addresses */
|
|
146
|
+
whitelistEnabled?: boolean
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
/**
|
|
@@ -167,15 +171,27 @@ export async function canDeposit(
|
|
|
167
171
|
allowFailure: false,
|
|
168
172
|
})
|
|
169
173
|
|
|
174
|
+
// Read whitelist status (may revert on older vaults — default to false)
|
|
175
|
+
let whitelistEnabled = false
|
|
176
|
+
try {
|
|
177
|
+
whitelistEnabled = await publicClient.readContract({
|
|
178
|
+
address: v,
|
|
179
|
+
abi: VAULT_ANALYSIS_ABI,
|
|
180
|
+
functionName: 'isDepositWhitelistEnabled',
|
|
181
|
+
}) as boolean
|
|
182
|
+
} catch {
|
|
183
|
+
// Older vaults may not have this function
|
|
184
|
+
}
|
|
185
|
+
|
|
170
186
|
if (isPaused) {
|
|
171
|
-
return { allowed: false, reason: 'paused' }
|
|
187
|
+
return { allowed: false, reason: 'paused', whitelistEnabled }
|
|
172
188
|
}
|
|
173
189
|
|
|
174
190
|
// Cross-chain async hubs revert on maxDeposit — this is expected, not a whitelist block.
|
|
175
191
|
// The vault accepts deposits via initVaultActionRequest instead of the standard ERC-4626 path.
|
|
176
192
|
const isCrossChainAsync = isHub && !oraclesEnabled
|
|
177
193
|
if (isCrossChainAsync) {
|
|
178
|
-
return { allowed: true, reason: 'ok' }
|
|
194
|
+
return { allowed: true, reason: 'ok', whitelistEnabled }
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
// maxDeposit(user) can REVERT on vaults with whitelist/ACL
|
|
@@ -189,13 +205,13 @@ export async function canDeposit(
|
|
|
189
205
|
})
|
|
190
206
|
} catch {
|
|
191
207
|
// Revert means the vault has whitelist/ACL and this user is not approved
|
|
192
|
-
return { allowed: false, reason: 'not-whitelisted' }
|
|
208
|
+
return { allowed: false, reason: 'not-whitelisted', maxDeposit: 0n, whitelistEnabled }
|
|
193
209
|
}
|
|
194
210
|
|
|
195
211
|
if (maxDepositAmount === 0n) {
|
|
196
|
-
return { allowed: false, reason: 'capacity-full' }
|
|
212
|
+
return { allowed: false, reason: 'capacity-full', maxDeposit: 0n, whitelistEnabled }
|
|
197
213
|
}
|
|
198
|
-
return { allowed: true, reason: 'ok' }
|
|
214
|
+
return { allowed: true, reason: 'ok', maxDeposit: maxDepositAmount, whitelistEnabled }
|
|
199
215
|
}
|
|
200
216
|
|
|
201
217
|
// ─────────────────────────────────────────────────────────────────────────────
|