@oydual31/more-vaults-sdk 0.1.2 → 0.1.4
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/dist/ethers/index.cjs +3 -2
- package/dist/ethers/index.cjs.map +1 -1
- package/dist/ethers/index.js +3 -2
- package/dist/ethers/index.js.map +1 -1
- package/dist/react/index.cjs +1109 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +382 -0
- package/dist/react/index.d.ts +382 -0
- package/dist/react/index.js +1098 -0
- package/dist/react/index.js.map +1 -0
- package/dist/userHelpers-CZLB9oQ4.d.cts +286 -0
- package/dist/userHelpers-CZLB9oQ4.d.ts +286 -0
- package/dist/viem/index.cjs +3 -2
- package/dist/viem/index.cjs.map +1 -1
- package/dist/viem/index.d.cts +2 -284
- package/dist/viem/index.d.ts +2 -284
- package/dist/viem/index.js +3 -2
- package/dist/viem/index.js.map +1 -1
- package/package.json +26 -5
- package/src/ethers/utils.ts +7 -3
- package/src/react/index.ts +26 -0
- package/src/react/useAsyncRequestStatus.ts +36 -0
- package/src/react/useDepositSimple.ts +76 -0
- package/src/react/useLzFee.ts +27 -0
- package/src/react/useOmniDeposit.ts +97 -0
- package/src/react/useOmniRedeem.ts +96 -0
- package/src/react/useRedeemShares.ts +77 -0
- package/src/react/useSmartDeposit.ts +70 -0
- package/src/react/useUserPosition.ts +29 -0
- package/src/react/useVaultMetadata.ts +29 -0
- package/src/react/useVaultStatus.ts +34 -0
- package/src/viem/utils.ts +7 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oydual31/more-vaults-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "TypeScript SDK for MoreVaults protocol — viem/wagmi and ethers.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
"types": "./dist/ethers/index.d.ts",
|
|
14
14
|
"import": "./dist/ethers/index.js",
|
|
15
15
|
"require": "./dist/ethers/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./react": {
|
|
18
|
+
"types": "./dist/react/index.d.ts",
|
|
19
|
+
"import": "./dist/react/index.js",
|
|
20
|
+
"require": "./dist/react/index.cjs"
|
|
16
21
|
}
|
|
17
22
|
},
|
|
18
23
|
"files": [
|
|
@@ -26,18 +31,25 @@
|
|
|
26
31
|
"prepublishOnly": "npm run build"
|
|
27
32
|
},
|
|
28
33
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
34
|
+
"ethers": ">=6.0.0",
|
|
35
|
+
"viem": ">=2.0.0"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
38
|
+
"@tanstack/react-query": "^5.90.21",
|
|
33
39
|
"@types/node": "^22.0.0",
|
|
40
|
+
"@types/react": "^18.3.28",
|
|
41
|
+
"react": "^18.3.1",
|
|
34
42
|
"tsup": "^8.0.0",
|
|
35
43
|
"tsx": "^4.19.0",
|
|
36
|
-
"typescript": "^5.7.0"
|
|
44
|
+
"typescript": "^5.7.0",
|
|
45
|
+
"wagmi": "^2.19.5"
|
|
37
46
|
},
|
|
38
47
|
"peerDependencies": {
|
|
48
|
+
"@tanstack/react-query": ">=5.0.0",
|
|
49
|
+
"ethers": ">=6.0.0",
|
|
50
|
+
"react": ">=18.0.0",
|
|
39
51
|
"viem": ">=2.0.0",
|
|
40
|
-
"
|
|
52
|
+
"wagmi": ">=2.0.0"
|
|
41
53
|
},
|
|
42
54
|
"peerDependenciesMeta": {
|
|
43
55
|
"viem": {
|
|
@@ -45,6 +57,15 @@
|
|
|
45
57
|
},
|
|
46
58
|
"ethers": {
|
|
47
59
|
"optional": true
|
|
60
|
+
},
|
|
61
|
+
"wagmi": {
|
|
62
|
+
"optional": true
|
|
63
|
+
},
|
|
64
|
+
"react": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"@tanstack/react-query": {
|
|
68
|
+
"optional": true
|
|
48
69
|
}
|
|
49
70
|
}
|
|
50
71
|
}
|
package/src/ethers/utils.ts
CHANGED
|
@@ -265,10 +265,14 @@ export async function getVaultStatus(
|
|
|
265
265
|
|
|
266
266
|
const spokesDeployedBalance: bigint = totalAssets > hubLiquidBalance ? totalAssets - hubLiquidBalance : 0n;
|
|
267
267
|
|
|
268
|
-
// null = maxDeposit reverted
|
|
268
|
+
// null = maxDeposit reverted.
|
|
269
|
+
// For cross-chain-async hubs this is expected — the contract reverts with
|
|
270
|
+
// NotAnERC4626CompatibleVault because maxDeposit is not meaningful in async mode.
|
|
271
|
+
// Only treat the revert as "whitelist/ACL" when the vault is NOT a hub.
|
|
269
272
|
const MAX_UINT256 = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
|
|
270
|
-
const
|
|
271
|
-
const
|
|
273
|
+
const isCrossChainAsync = isHub && !oraclesEnabled;
|
|
274
|
+
const depositAccessRestricted = maxDepositRaw === null && !isCrossChainAsync;
|
|
275
|
+
const effectiveCapacity: bigint = maxDepositRaw === null ? MAX_UINT256 : maxDepositRaw!;
|
|
272
276
|
|
|
273
277
|
// ── Derive mode ────────────────────────────────────────────────────────────
|
|
274
278
|
let mode: VaultMode;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// MoreVaults SDK — React hooks (wagmi v2 + @tanstack/react-query v5)
|
|
2
|
+
// Peer dependencies: react >=18, wagmi >=2, @tanstack/react-query >=5
|
|
3
|
+
|
|
4
|
+
// --- Read hooks ---
|
|
5
|
+
export { useVaultStatus } from './useVaultStatus.js'
|
|
6
|
+
export type { VaultStatus } from './useVaultStatus.js'
|
|
7
|
+
|
|
8
|
+
export { useVaultMetadata } from './useVaultMetadata.js'
|
|
9
|
+
export type { VaultMetadata } from './useVaultMetadata.js'
|
|
10
|
+
|
|
11
|
+
export { useUserPosition } from './useUserPosition.js'
|
|
12
|
+
export type { UserPosition } from './useUserPosition.js'
|
|
13
|
+
|
|
14
|
+
export { useLzFee } from './useLzFee.js'
|
|
15
|
+
|
|
16
|
+
export { useAsyncRequestStatus } from './useAsyncRequestStatus.js'
|
|
17
|
+
export type { AsyncRequestStatusInfo } from './useAsyncRequestStatus.js'
|
|
18
|
+
|
|
19
|
+
// --- Action hooks ---
|
|
20
|
+
export { useOmniDeposit } from './useOmniDeposit.js'
|
|
21
|
+
export { useOmniRedeem } from './useOmniRedeem.js'
|
|
22
|
+
export { useDepositSimple } from './useDepositSimple.js'
|
|
23
|
+
export { useRedeemShares } from './useRedeemShares.js'
|
|
24
|
+
|
|
25
|
+
// --- Smart (auto-routing) hooks ---
|
|
26
|
+
export { useSmartDeposit } from './useSmartDeposit.js'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { usePublicClient } from 'wagmi'
|
|
3
|
+
import { asSdkClient, getAsyncRequestStatusLabel } from '../viem/index.js'
|
|
4
|
+
import type { AsyncRequestStatusInfo } from '../viem/index.js'
|
|
5
|
+
|
|
6
|
+
export type { AsyncRequestStatusInfo }
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Poll the status of an async cross-chain request (D4/D5/R5) by GUID.
|
|
10
|
+
*
|
|
11
|
+
* Automatically stops polling when status reaches 'completed' or 'refunded'.
|
|
12
|
+
* Polls every 10s while the request is still pending or ready-to-execute.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const { data } = useAsyncRequestStatus('0xVAULT', guid, 747)
|
|
16
|
+
* // data.status: 'pending' | 'ready-to-execute' | 'completed' | 'refunded'
|
|
17
|
+
* // data.label: human-readable description
|
|
18
|
+
* // data.result: shares minted or assets returned (0n while pending)
|
|
19
|
+
*/
|
|
20
|
+
export function useAsyncRequestStatus(
|
|
21
|
+
vault: `0x${string}` | undefined,
|
|
22
|
+
guid: `0x${string}` | undefined,
|
|
23
|
+
chainId: number,
|
|
24
|
+
) {
|
|
25
|
+
const publicClient = usePublicClient({ chainId })
|
|
26
|
+
return useQuery<AsyncRequestStatusInfo>({
|
|
27
|
+
queryKey: ['asyncRequestStatus', vault, guid, chainId],
|
|
28
|
+
queryFn: () => getAsyncRequestStatusLabel(asSdkClient(publicClient), vault!, guid!),
|
|
29
|
+
enabled: !!vault && !!guid && !!publicClient,
|
|
30
|
+
refetchInterval: (query) => {
|
|
31
|
+
const status = query.state.data?.status
|
|
32
|
+
if (status === 'completed' || status === 'refunded') return false
|
|
33
|
+
return 10_000
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react'
|
|
2
|
+
import { usePublicClient, useWalletClient } from 'wagmi'
|
|
3
|
+
import { asSdkClient, depositSimple } from '../viem/index.js'
|
|
4
|
+
import type { DepositResult } from '../viem/index.js'
|
|
5
|
+
|
|
6
|
+
interface UseDepositSimpleReturn {
|
|
7
|
+
/** Execute approve + depositSimple (D1/D3 flows). */
|
|
8
|
+
deposit: (amountInWei: bigint, receiver: `0x${string}`) => Promise<void>
|
|
9
|
+
isLoading: boolean
|
|
10
|
+
txHash: `0x${string}` | undefined
|
|
11
|
+
/** Shares minted. Available after tx confirmation. */
|
|
12
|
+
shares: bigint | undefined
|
|
13
|
+
error: Error | undefined
|
|
14
|
+
reset: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Hook for local and oracle-on cross-chain vaults (D1/D3 flows).
|
|
19
|
+
*
|
|
20
|
+
* Simpler than useOmniDeposit — no LZ fee, no GUID, no polling.
|
|
21
|
+
* One approve + one deposit transaction.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const { deposit, isLoading, txHash, shares } = useDepositSimple('0xVAULT', 747)
|
|
25
|
+
* await deposit(parseUnits('100', 6), userAddress)
|
|
26
|
+
*/
|
|
27
|
+
export function useDepositSimple(
|
|
28
|
+
vault: `0x${string}` | undefined,
|
|
29
|
+
chainId: number,
|
|
30
|
+
): UseDepositSimpleReturn {
|
|
31
|
+
const { data: walletClient } = useWalletClient({ chainId })
|
|
32
|
+
const publicClient = usePublicClient({ chainId })
|
|
33
|
+
|
|
34
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
35
|
+
const [result, setResult] = useState<DepositResult | undefined>()
|
|
36
|
+
const [error, setError] = useState<Error | undefined>()
|
|
37
|
+
|
|
38
|
+
const deposit = useCallback(
|
|
39
|
+
async (amountInWei: bigint, receiver: `0x${string}`) => {
|
|
40
|
+
if (!vault || !walletClient || !publicClient) return
|
|
41
|
+
setIsLoading(true)
|
|
42
|
+
setError(undefined)
|
|
43
|
+
try {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const res = await depositSimple(
|
|
46
|
+
walletClient as any,
|
|
47
|
+
asSdkClient(publicClient),
|
|
48
|
+
{ vault, hubChainId: chainId },
|
|
49
|
+
amountInWei,
|
|
50
|
+
receiver,
|
|
51
|
+
)
|
|
52
|
+
setResult(res)
|
|
53
|
+
} catch (err) {
|
|
54
|
+
setError(err instanceof Error ? err : new Error(String(err)))
|
|
55
|
+
} finally {
|
|
56
|
+
setIsLoading(false)
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
[vault, walletClient, publicClient, chainId],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
const reset = useCallback(() => {
|
|
63
|
+
setResult(undefined)
|
|
64
|
+
setError(undefined)
|
|
65
|
+
setIsLoading(false)
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
deposit,
|
|
70
|
+
isLoading,
|
|
71
|
+
txHash: result?.txHash,
|
|
72
|
+
shares: result?.shares,
|
|
73
|
+
error,
|
|
74
|
+
reset,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { usePublicClient } from 'wagmi'
|
|
3
|
+
import { asSdkClient, quoteLzFee } from '../viem/index.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Quote the LayerZero fee required for async operations (D4, D5, R5).
|
|
7
|
+
* Refreshes every 60s — fees change with network congestion.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const { fee, feeWithBuffer } = useLzFee('0xVAULT', 747)
|
|
11
|
+
* // feeWithBuffer adds 1% buffer automatically (fee * 101 / 100)
|
|
12
|
+
*/
|
|
13
|
+
export function useLzFee(vault: `0x${string}` | undefined, chainId: number) {
|
|
14
|
+
const publicClient = usePublicClient({ chainId })
|
|
15
|
+
const query = useQuery({
|
|
16
|
+
queryKey: ['lzFee', vault, chainId],
|
|
17
|
+
queryFn: () => quoteLzFee(asSdkClient(publicClient), vault!),
|
|
18
|
+
enabled: !!vault && !!publicClient,
|
|
19
|
+
refetchInterval: 60_000,
|
|
20
|
+
staleTime: 30_000,
|
|
21
|
+
})
|
|
22
|
+
return {
|
|
23
|
+
...query,
|
|
24
|
+
fee: query.data,
|
|
25
|
+
feeWithBuffer: query.data ? (query.data * 101n) / 100n : undefined,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react'
|
|
2
|
+
import { usePublicClient, useWalletClient, useChainId } from 'wagmi'
|
|
3
|
+
import {
|
|
4
|
+
asSdkClient,
|
|
5
|
+
depositAsync,
|
|
6
|
+
getVaultStatus,
|
|
7
|
+
} from '../viem/index.js'
|
|
8
|
+
import type { AsyncRequestStatusInfo } from '../viem/index.js'
|
|
9
|
+
import { useLzFee } from './useLzFee.js'
|
|
10
|
+
import { useAsyncRequestStatus } from './useAsyncRequestStatus.js'
|
|
11
|
+
|
|
12
|
+
interface UseOmniDepositReturn {
|
|
13
|
+
/** Execute approve + depositAsync. Handles everything internally. */
|
|
14
|
+
deposit: (amountInWei: bigint, receiver: `0x${string}`) => Promise<void>
|
|
15
|
+
isLoading: boolean
|
|
16
|
+
txHash: `0x${string}` | undefined
|
|
17
|
+
/** GUID for cross-chain tracking. Available after tx confirmation. */
|
|
18
|
+
guid: `0x${string}` | undefined
|
|
19
|
+
/** Cross-chain request status. undefined until a guid is available. */
|
|
20
|
+
requestStatus: AsyncRequestStatusInfo | undefined
|
|
21
|
+
/** true when the wallet is connected to the wrong chain */
|
|
22
|
+
wrongChain: boolean
|
|
23
|
+
error: Error | undefined
|
|
24
|
+
reset: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Complete hook for async deposits on hub vaults (D4 flow).
|
|
29
|
+
*
|
|
30
|
+
* Handles: fee quote, chain validation, approve, depositAsync, and GUID polling.
|
|
31
|
+
* The deposit function wraps the entire flow — callers only pass amount + receiver.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const { deposit, isLoading, guid, requestStatus, wrongChain } = useOmniDeposit('0xVAULT', 747)
|
|
35
|
+
*
|
|
36
|
+
* if (wrongChain) return <SwitchNetworkButton chainId={747} />
|
|
37
|
+
*
|
|
38
|
+
* await deposit(parseUnits('100', 6), userAddress)
|
|
39
|
+
* // requestStatus.status goes: 'pending' → 'completed' | 'refunded'
|
|
40
|
+
*/
|
|
41
|
+
export function useOmniDeposit(
|
|
42
|
+
vault: `0x${string}` | undefined,
|
|
43
|
+
hubChainId: number,
|
|
44
|
+
): UseOmniDepositReturn {
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
46
|
+
const { data: walletClient } = useWalletClient({ chainId: hubChainId })
|
|
47
|
+
const publicClient = usePublicClient({ chainId: hubChainId })
|
|
48
|
+
const currentChainId = useChainId()
|
|
49
|
+
|
|
50
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
51
|
+
const [txHash, setTxHash] = useState<`0x${string}` | undefined>()
|
|
52
|
+
const [guid, setGuid] = useState<`0x${string}` | undefined>()
|
|
53
|
+
const [error, setError] = useState<Error | undefined>()
|
|
54
|
+
|
|
55
|
+
const { feeWithBuffer } = useLzFee(vault, hubChainId)
|
|
56
|
+
const { data: requestStatus } = useAsyncRequestStatus(vault, guid, hubChainId)
|
|
57
|
+
|
|
58
|
+
const wrongChain = currentChainId !== hubChainId
|
|
59
|
+
|
|
60
|
+
const deposit = useCallback(
|
|
61
|
+
async (amountInWei: bigint, receiver: `0x${string}`) => {
|
|
62
|
+
if (!vault || !walletClient || !publicClient || !feeWithBuffer) return
|
|
63
|
+
setIsLoading(true)
|
|
64
|
+
setError(undefined)
|
|
65
|
+
try {
|
|
66
|
+
const pc = asSdkClient(publicClient)
|
|
67
|
+
const status = await getVaultStatus(pc, vault)
|
|
68
|
+
// walletClient from wagmi is structurally compatible with viem WalletClient
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
const result = await depositAsync(
|
|
71
|
+
walletClient as any,
|
|
72
|
+
pc,
|
|
73
|
+
{ vault, escrow: status.escrow, hubChainId },
|
|
74
|
+
amountInWei,
|
|
75
|
+
receiver,
|
|
76
|
+
feeWithBuffer,
|
|
77
|
+
)
|
|
78
|
+
setTxHash(result.txHash)
|
|
79
|
+
setGuid(result.guid)
|
|
80
|
+
} catch (err) {
|
|
81
|
+
setError(err instanceof Error ? err : new Error(String(err)))
|
|
82
|
+
} finally {
|
|
83
|
+
setIsLoading(false)
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
[vault, walletClient, publicClient, feeWithBuffer, hubChainId],
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const reset = useCallback(() => {
|
|
90
|
+
setTxHash(undefined)
|
|
91
|
+
setGuid(undefined)
|
|
92
|
+
setError(undefined)
|
|
93
|
+
setIsLoading(false)
|
|
94
|
+
}, [])
|
|
95
|
+
|
|
96
|
+
return { deposit, isLoading, txHash, guid, requestStatus, wrongChain, error, reset }
|
|
97
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react'
|
|
2
|
+
import { usePublicClient, useWalletClient, useChainId } from 'wagmi'
|
|
3
|
+
import {
|
|
4
|
+
asSdkClient,
|
|
5
|
+
redeemAsync,
|
|
6
|
+
getVaultStatus,
|
|
7
|
+
} from '../viem/index.js'
|
|
8
|
+
import type { AsyncRequestStatusInfo } from '../viem/index.js'
|
|
9
|
+
import { useLzFee } from './useLzFee.js'
|
|
10
|
+
import { useAsyncRequestStatus } from './useAsyncRequestStatus.js'
|
|
11
|
+
|
|
12
|
+
interface UseOmniRedeemReturn {
|
|
13
|
+
/** Execute approve + redeemAsync. Handles everything internally. */
|
|
14
|
+
redeem: (sharesInWei: bigint, receiver: `0x${string}`, owner: `0x${string}`) => Promise<void>
|
|
15
|
+
isLoading: boolean
|
|
16
|
+
txHash: `0x${string}` | undefined
|
|
17
|
+
/** GUID for cross-chain tracking. Available after tx confirmation. */
|
|
18
|
+
guid: `0x${string}` | undefined
|
|
19
|
+
/** Cross-chain request status. undefined until a guid is available. */
|
|
20
|
+
requestStatus: AsyncRequestStatusInfo | undefined
|
|
21
|
+
/** true when the wallet is connected to the wrong chain */
|
|
22
|
+
wrongChain: boolean
|
|
23
|
+
error: Error | undefined
|
|
24
|
+
reset: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Complete hook for async redeems on hub vaults (R5 flow).
|
|
29
|
+
*
|
|
30
|
+
* Handles: fee quote, chain validation, share approve, redeemAsync, and GUID polling.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const { redeem, isLoading, guid, requestStatus, wrongChain } = useOmniRedeem('0xVAULT', 747)
|
|
34
|
+
*
|
|
35
|
+
* if (wrongChain) return <SwitchNetworkButton chainId={747} />
|
|
36
|
+
*
|
|
37
|
+
* await redeem(sharesInWei, userAddress, userAddress)
|
|
38
|
+
* // requestStatus.status goes: 'pending' → 'completed' | 'refunded'
|
|
39
|
+
*/
|
|
40
|
+
export function useOmniRedeem(
|
|
41
|
+
vault: `0x${string}` | undefined,
|
|
42
|
+
hubChainId: number,
|
|
43
|
+
): UseOmniRedeemReturn {
|
|
44
|
+
const { data: walletClient } = useWalletClient({ chainId: hubChainId })
|
|
45
|
+
const publicClient = usePublicClient({ chainId: hubChainId })
|
|
46
|
+
const currentChainId = useChainId()
|
|
47
|
+
|
|
48
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
49
|
+
const [txHash, setTxHash] = useState<`0x${string}` | undefined>()
|
|
50
|
+
const [guid, setGuid] = useState<`0x${string}` | undefined>()
|
|
51
|
+
const [error, setError] = useState<Error | undefined>()
|
|
52
|
+
|
|
53
|
+
const { feeWithBuffer } = useLzFee(vault, hubChainId)
|
|
54
|
+
const { data: requestStatus } = useAsyncRequestStatus(vault, guid, hubChainId)
|
|
55
|
+
|
|
56
|
+
const wrongChain = currentChainId !== hubChainId
|
|
57
|
+
|
|
58
|
+
const redeem = useCallback(
|
|
59
|
+
async (sharesInWei: bigint, receiver: `0x${string}`, owner: `0x${string}`) => {
|
|
60
|
+
if (!vault || !walletClient || !publicClient || !feeWithBuffer) return
|
|
61
|
+
setIsLoading(true)
|
|
62
|
+
setError(undefined)
|
|
63
|
+
try {
|
|
64
|
+
const pc = asSdkClient(publicClient)
|
|
65
|
+
const status = await getVaultStatus(pc, vault)
|
|
66
|
+
// walletClient from wagmi is structurally compatible with viem WalletClient
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
68
|
+
const result = await redeemAsync(
|
|
69
|
+
walletClient as any,
|
|
70
|
+
pc,
|
|
71
|
+
{ vault, escrow: status.escrow, hubChainId },
|
|
72
|
+
sharesInWei,
|
|
73
|
+
receiver,
|
|
74
|
+
owner,
|
|
75
|
+
feeWithBuffer,
|
|
76
|
+
)
|
|
77
|
+
setTxHash(result.txHash)
|
|
78
|
+
setGuid(result.guid)
|
|
79
|
+
} catch (err) {
|
|
80
|
+
setError(err instanceof Error ? err : new Error(String(err)))
|
|
81
|
+
} finally {
|
|
82
|
+
setIsLoading(false)
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
[vault, walletClient, publicClient, feeWithBuffer, hubChainId],
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const reset = useCallback(() => {
|
|
89
|
+
setTxHash(undefined)
|
|
90
|
+
setGuid(undefined)
|
|
91
|
+
setError(undefined)
|
|
92
|
+
setIsLoading(false)
|
|
93
|
+
}, [])
|
|
94
|
+
|
|
95
|
+
return { redeem, isLoading, txHash, guid, requestStatus, wrongChain, error, reset }
|
|
96
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react'
|
|
2
|
+
import { usePublicClient, useWalletClient } from 'wagmi'
|
|
3
|
+
import { asSdkClient, redeemShares } from '../viem/index.js'
|
|
4
|
+
import type { RedeemResult } from '../viem/index.js'
|
|
5
|
+
|
|
6
|
+
interface UseRedeemSharesReturn {
|
|
7
|
+
/** Execute redeemShares (R1 flow). */
|
|
8
|
+
redeem: (sharesInWei: bigint, receiver: `0x${string}`, owner: `0x${string}`) => Promise<void>
|
|
9
|
+
isLoading: boolean
|
|
10
|
+
txHash: `0x${string}` | undefined
|
|
11
|
+
/** Assets received. Available after tx confirmation. */
|
|
12
|
+
assets: bigint | undefined
|
|
13
|
+
error: Error | undefined
|
|
14
|
+
reset: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Hook for standard ERC-4626 share redemption (R1 flow).
|
|
19
|
+
*
|
|
20
|
+
* Used for local and oracle-on cross-chain vaults.
|
|
21
|
+
* No LZ fee required — single transaction.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const { redeem, isLoading, txHash, assets } = useRedeemShares('0xVAULT', 747)
|
|
25
|
+
* await redeem(sharesInWei, userAddress, userAddress)
|
|
26
|
+
*/
|
|
27
|
+
export function useRedeemShares(
|
|
28
|
+
vault: `0x${string}` | undefined,
|
|
29
|
+
chainId: number,
|
|
30
|
+
): UseRedeemSharesReturn {
|
|
31
|
+
const { data: walletClient } = useWalletClient({ chainId })
|
|
32
|
+
const publicClient = usePublicClient({ chainId })
|
|
33
|
+
|
|
34
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
35
|
+
const [result, setResult] = useState<RedeemResult | undefined>()
|
|
36
|
+
const [error, setError] = useState<Error | undefined>()
|
|
37
|
+
|
|
38
|
+
const redeem = useCallback(
|
|
39
|
+
async (sharesInWei: bigint, receiver: `0x${string}`, owner: `0x${string}`) => {
|
|
40
|
+
if (!vault || !walletClient || !publicClient) return
|
|
41
|
+
setIsLoading(true)
|
|
42
|
+
setError(undefined)
|
|
43
|
+
try {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const res = await redeemShares(
|
|
46
|
+
walletClient as any,
|
|
47
|
+
asSdkClient(publicClient),
|
|
48
|
+
{ vault, hubChainId: chainId },
|
|
49
|
+
sharesInWei,
|
|
50
|
+
receiver,
|
|
51
|
+
owner,
|
|
52
|
+
)
|
|
53
|
+
setResult(res)
|
|
54
|
+
} catch (err) {
|
|
55
|
+
setError(err instanceof Error ? err : new Error(String(err)))
|
|
56
|
+
} finally {
|
|
57
|
+
setIsLoading(false)
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
[vault, walletClient, publicClient, chainId],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const reset = useCallback(() => {
|
|
64
|
+
setResult(undefined)
|
|
65
|
+
setError(undefined)
|
|
66
|
+
setIsLoading(false)
|
|
67
|
+
}, [])
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
redeem,
|
|
71
|
+
isLoading,
|
|
72
|
+
txHash: result?.txHash,
|
|
73
|
+
assets: result?.assets,
|
|
74
|
+
error,
|
|
75
|
+
reset,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { DepositResult, AsyncRequestResult, AsyncRequestStatusInfo } from '../viem/index.js'
|
|
2
|
+
import { useVaultStatus } from './useVaultStatus.js'
|
|
3
|
+
import { useOmniDeposit } from './useOmniDeposit.js'
|
|
4
|
+
import { useDepositSimple } from './useDepositSimple.js'
|
|
5
|
+
|
|
6
|
+
interface UseSmartDepositReturn {
|
|
7
|
+
/**
|
|
8
|
+
* Execute deposit using the correct flow for this vault's mode.
|
|
9
|
+
* For async vaults: wraps depositAsync (D4) — returns guid for tracking.
|
|
10
|
+
* For local/oracle vaults: wraps depositSimple (D1/D3) — returns shares.
|
|
11
|
+
*/
|
|
12
|
+
deposit: (amountInWei: bigint, receiver: `0x${string}`) => Promise<void>
|
|
13
|
+
isLoading: boolean
|
|
14
|
+
txHash: `0x${string}` | undefined
|
|
15
|
+
/** Shares minted (available for D1/D3 vaults after confirmation, undefined for D4). */
|
|
16
|
+
shares: bigint | undefined
|
|
17
|
+
/** GUID for cross-chain tracking (D4 vaults only). */
|
|
18
|
+
guid: `0x${string}` | undefined
|
|
19
|
+
/** Cross-chain request status (D4 vaults only). */
|
|
20
|
+
requestStatus: AsyncRequestStatusInfo | undefined
|
|
21
|
+
/** true when the wallet is connected to the wrong chain (D4 vaults only). */
|
|
22
|
+
wrongChain: boolean
|
|
23
|
+
/** Vault mode loaded from getVaultStatus. undefined while loading. */
|
|
24
|
+
vaultMode: 'local' | 'cross-chain-oracle' | 'cross-chain-async' | 'paused' | 'full' | undefined
|
|
25
|
+
error: Error | undefined
|
|
26
|
+
reset: () => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Auto-selects the correct deposit flow based on vault mode.
|
|
31
|
+
* Best for frontends that support multiple vault types.
|
|
32
|
+
*
|
|
33
|
+
* Internally uses useVaultStatus to detect the mode, then delegates to:
|
|
34
|
+
* - useOmniDeposit (D4) for 'cross-chain-async' vaults
|
|
35
|
+
* - useDepositSimple (D1/D3) for 'local' and 'cross-chain-oracle' vaults
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const { deposit, isLoading, guid, requestStatus, vaultMode } = useSmartDeposit('0xVAULT', 747)
|
|
39
|
+
*
|
|
40
|
+
* if (vaultMode === 'paused') return <PausedBadge />
|
|
41
|
+
*
|
|
42
|
+
* await deposit(parseUnits('100', 6), userAddress)
|
|
43
|
+
* // For async vaults: poll requestStatus until 'completed'
|
|
44
|
+
* // For sync vaults: txHash + shares are available immediately
|
|
45
|
+
*/
|
|
46
|
+
export function useSmartDeposit(
|
|
47
|
+
vault: `0x${string}` | undefined,
|
|
48
|
+
hubChainId: number,
|
|
49
|
+
): UseSmartDepositReturn {
|
|
50
|
+
const { data: status } = useVaultStatus(vault, hubChainId)
|
|
51
|
+
const omni = useOmniDeposit(vault, hubChainId)
|
|
52
|
+
const simple = useDepositSimple(vault, hubChainId)
|
|
53
|
+
|
|
54
|
+
const isAsync = status?.mode === 'cross-chain-async'
|
|
55
|
+
|
|
56
|
+
const deposit = isAsync ? omni.deposit : simple.deposit
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
deposit,
|
|
60
|
+
isLoading: isAsync ? omni.isLoading : simple.isLoading,
|
|
61
|
+
txHash: isAsync ? omni.txHash : simple.txHash,
|
|
62
|
+
shares: isAsync ? undefined : simple.shares,
|
|
63
|
+
guid: isAsync ? omni.guid : undefined,
|
|
64
|
+
requestStatus: isAsync ? omni.requestStatus : undefined,
|
|
65
|
+
wrongChain: isAsync ? omni.wrongChain : false,
|
|
66
|
+
vaultMode: status?.mode,
|
|
67
|
+
error: isAsync ? omni.error : simple.error,
|
|
68
|
+
reset: isAsync ? omni.reset : simple.reset,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { usePublicClient } from 'wagmi'
|
|
3
|
+
import { asSdkClient, getUserPosition } from '../viem/index.js'
|
|
4
|
+
import type { UserPosition } from '../viem/index.js'
|
|
5
|
+
|
|
6
|
+
export type { UserPosition }
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Read the user's current position in a vault.
|
|
10
|
+
* Refetches every 15s to keep the balance display current.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const { data: position } = useUserPosition('0xVAULT', '0xUSER', 747)
|
|
14
|
+
* // position.shares, position.estimatedAssets, position.pendingWithdrawal
|
|
15
|
+
*/
|
|
16
|
+
export function useUserPosition(
|
|
17
|
+
vault: `0x${string}` | undefined,
|
|
18
|
+
user: `0x${string}` | undefined,
|
|
19
|
+
chainId: number,
|
|
20
|
+
) {
|
|
21
|
+
const publicClient = usePublicClient({ chainId })
|
|
22
|
+
return useQuery({
|
|
23
|
+
queryKey: ['userPosition', vault, user, chainId],
|
|
24
|
+
queryFn: () => getUserPosition(asSdkClient(publicClient), vault!, user!),
|
|
25
|
+
enabled: !!vault && !!user && !!publicClient,
|
|
26
|
+
refetchInterval: 15_000,
|
|
27
|
+
staleTime: 10_000,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { usePublicClient } from 'wagmi'
|
|
3
|
+
import { asSdkClient, getVaultMetadata } from '../viem/index.js'
|
|
4
|
+
import type { VaultMetadata } from '../viem/index.js'
|
|
5
|
+
|
|
6
|
+
export type { VaultMetadata }
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Read display metadata for a vault and its underlying token.
|
|
10
|
+
* Uses a long stale time (5 min) because metadata rarely changes.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const { data: meta } = useVaultMetadata('0xVAULT', 747)
|
|
14
|
+
* // meta.name, meta.symbol, meta.underlying, meta.underlyingSymbol
|
|
15
|
+
*/
|
|
16
|
+
export function useVaultMetadata(
|
|
17
|
+
vault: `0x${string}` | undefined,
|
|
18
|
+
chainId: number,
|
|
19
|
+
) {
|
|
20
|
+
const publicClient = usePublicClient({ chainId })
|
|
21
|
+
return useQuery({
|
|
22
|
+
queryKey: ['vaultMetadata', vault, chainId],
|
|
23
|
+
queryFn: () => getVaultMetadata(asSdkClient(publicClient), vault!),
|
|
24
|
+
enabled: !!vault && !!publicClient,
|
|
25
|
+
// Metadata (name, symbol, underlying) changes very rarely — 5 min stale time
|
|
26
|
+
staleTime: 5 * 60_000,
|
|
27
|
+
refetchInterval: 5 * 60_000,
|
|
28
|
+
})
|
|
29
|
+
}
|