@oydual31/more-vaults-sdk 0.1.0 → 0.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oydual31/more-vaults-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "TypeScript SDK for MoreVaults protocol — viem/wagmi and ethers.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -40,7 +40,11 @@
40
40
  "ethers": ">=6.0.0"
41
41
  },
42
42
  "peerDependenciesMeta": {
43
- "viem": { "optional": true },
44
- "ethers": { "optional": true }
43
+ "viem": {
44
+ "optional": true
45
+ },
46
+ "ethers": {
47
+ "optional": true
48
+ }
45
49
  }
46
50
  }
@@ -0,0 +1,16 @@
1
+ import type { Signer } from 'ethers'
2
+ import { WrongChainError } from './errors'
3
+
4
+ /**
5
+ * Validate that the signer is connected to the expected chain.
6
+ * Only validates if hubChainId is provided — opt-in, non-breaking.
7
+ */
8
+ export async function validateWalletChain(signer: Signer, hubChainId?: number): Promise<void> {
9
+ if (!hubChainId) return
10
+ const network = await signer.provider?.getNetwork()
11
+ if (!network) return
12
+ const current = Number(network.chainId)
13
+ if (current !== hubChainId) {
14
+ throw new WrongChainError(current, hubChainId)
15
+ }
16
+ }
@@ -0,0 +1,35 @@
1
+ /** EVM Chain IDs for chains supported by MoreVaults */
2
+ export const CHAIN_IDS = {
3
+ flowEVMMainnet: 747,
4
+ flowEVMTestnet: 545,
5
+ arbitrum: 42161,
6
+ base: 8453,
7
+ ethereum: 1,
8
+ } as const;
9
+
10
+ /** LayerZero Endpoint IDs (EID) for chains supported by MoreVaults */
11
+ export const LZ_EIDS = {
12
+ flowMainnet: 30332,
13
+ flowTestnet: 30333,
14
+ arbitrum: 30110,
15
+ base: 30184,
16
+ ethereum: 30101,
17
+ } as const;
18
+
19
+ /** LayerZero EID → EVM Chain ID */
20
+ export const EID_TO_CHAIN_ID: Record<number, number> = {
21
+ [LZ_EIDS.flowMainnet]: CHAIN_IDS.flowEVMMainnet,
22
+ [LZ_EIDS.flowTestnet]: CHAIN_IDS.flowEVMTestnet,
23
+ [LZ_EIDS.arbitrum]: CHAIN_IDS.arbitrum,
24
+ [LZ_EIDS.base]: CHAIN_IDS.base,
25
+ [LZ_EIDS.ethereum]: CHAIN_IDS.ethereum,
26
+ };
27
+
28
+ /** EVM Chain ID → LayerZero EID */
29
+ export const CHAIN_ID_TO_EID: Record<number, number> = {
30
+ [CHAIN_IDS.flowEVMMainnet]: LZ_EIDS.flowMainnet,
31
+ [CHAIN_IDS.flowEVMTestnet]: LZ_EIDS.flowTestnet,
32
+ [CHAIN_IDS.arbitrum]: LZ_EIDS.arbitrum,
33
+ [CHAIN_IDS.base]: LZ_EIDS.base,
34
+ [CHAIN_IDS.ethereum]: LZ_EIDS.ethereum,
35
+ };
@@ -9,6 +9,7 @@ import {
9
9
  } from "./types";
10
10
  import { preflightAsync, preflightSync } from "./preflight";
11
11
  import { MissingEscrowAddressError, VaultPausedError, CapacityFullError } from "./errors";
12
+ import { validateWalletChain } from "./chainValidation";
12
13
  import { getVaultStatus, quoteLzFee } from "./utils";
13
14
 
14
15
  /**
@@ -53,6 +54,9 @@ export async function depositSimple(
53
54
  ): Promise<DepositResult> {
54
55
  const provider = signer.provider!;
55
56
 
57
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
58
+ await validateWalletChain(signer, addresses.hubChainId);
59
+
56
60
  // Pre-flight: validate vault is operational and accepting deposits
57
61
  await preflightSync(provider, addresses.vault);
58
62
 
@@ -114,6 +118,9 @@ export async function depositMultiAsset(
114
118
  receiver: string,
115
119
  minShares: bigint
116
120
  ): Promise<DepositResult> {
121
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
122
+ await validateWalletChain(signer, addresses.hubChainId);
123
+
117
124
  // Approve each token
118
125
  for (let i = 0; i < tokens.length; i++) {
119
126
  await ensureAllowance(signer, tokens[i], addresses.vault, amounts[i]);
@@ -190,6 +197,9 @@ export async function depositAsync(
190
197
  if (!addresses.escrow) throw new MissingEscrowAddressError();
191
198
  const escrow = addresses.escrow;
192
199
 
200
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
201
+ await validateWalletChain(signer, addresses.hubChainId);
202
+
193
203
  // Pre-flight: validate async cross-chain setup before sending any transaction
194
204
  await preflightAsync(provider, addresses.vault, escrow);
195
205
 
@@ -262,6 +272,9 @@ export async function mintAsync(
262
272
  if (!addresses.escrow) throw new MissingEscrowAddressError();
263
273
  const escrow = addresses.escrow;
264
274
 
275
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
276
+ await validateWalletChain(signer, addresses.hubChainId);
277
+
265
278
  // Pre-flight: validate async cross-chain setup before sending any transaction
266
279
  await preflightAsync(provider, addresses.vault, escrow);
267
280
 
@@ -79,3 +79,12 @@ export class MissingEscrowAddressError extends MoreVaultsError {
79
79
  this.name = 'MissingEscrowAddressError'
80
80
  }
81
81
  }
82
+
83
+ export class WrongChainError extends MoreVaultsError {
84
+ constructor(currentChainId: number, expectedChainId: number) {
85
+ super(
86
+ `Wrong network: wallet is on chain ${currentChainId}, but the vault hub requires chain ${expectedChainId}. Switch networks before proceeding.`,
87
+ )
88
+ this.name = 'WrongChainError'
89
+ }
90
+ }
@@ -1,6 +1,9 @@
1
1
  // MoreVaults SDK -- ethers.js v6
2
2
  // Barrel export for all flows and utilities.
3
3
 
4
+ // --- Chain constants ---
5
+ export { CHAIN_IDS, LZ_EIDS, EID_TO_CHAIN_ID, CHAIN_ID_TO_EID } from "./chains";
6
+
4
7
  // --- Types ---
5
8
  export type {
6
9
  VaultAddresses,
@@ -36,6 +39,7 @@ export {
36
39
  EscrowNotConfiguredError,
37
40
  NotHubVaultError,
38
41
  MissingEscrowAddressError,
42
+ WrongChainError,
39
43
  } from "./errors";
40
44
 
41
45
  // --- Deposit flows ---
@@ -101,3 +105,6 @@ export type {
101
105
  MaxWithdrawable,
102
106
  VaultSummary,
103
107
  } from "./userHelpers";
108
+
109
+ // --- wagmi / ethers adapter compatibility ---
110
+ export { asSdkSigner } from "./wagmiCompat";
@@ -14,6 +14,7 @@ import {
14
14
  import type { ContractTransactionReceipt } from "ethers";
15
15
  import { preflightAsync, preflightRedeemLiquidity } from "./preflight";
16
16
  import { MissingEscrowAddressError } from "./errors";
17
+ import { validateWalletChain } from "./chainValidation";
17
18
 
18
19
  /**
19
20
  * Ensure `spender` has at least `amount` allowance from `owner`.
@@ -58,6 +59,9 @@ export async function redeemShares(
58
59
  receiver: string,
59
60
  owner: string
60
61
  ): Promise<RedeemResult> {
62
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
63
+ await validateWalletChain(signer, addresses.hubChainId);
64
+
61
65
  const vault = new Contract(addresses.vault, VAULT_ABI, signer);
62
66
 
63
67
  // Static call to get the return value (assets) before broadcasting
@@ -92,6 +96,9 @@ export async function withdrawAssets(
92
96
  receiver: string,
93
97
  owner: string
94
98
  ): Promise<RedeemResult> {
99
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
100
+ await validateWalletChain(signer, addresses.hubChainId);
101
+
95
102
  const vault = new Contract(addresses.vault, VAULT_ABI, signer);
96
103
  const tx = await vault.withdraw(assets, receiver, owner);
97
104
  const receipt = await tx.wait();
@@ -122,6 +129,9 @@ export async function requestRedeem(
122
129
  shares: bigint,
123
130
  owner: string
124
131
  ): Promise<{ receipt: ContractTransactionReceipt }> {
132
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
133
+ await validateWalletChain(signer, addresses.hubChainId);
134
+
125
135
  const vault = new Contract(addresses.vault, VAULT_ABI, signer);
126
136
  const tx = await vault.requestRedeem(shares, owner);
127
137
  const receipt = await tx.wait();
@@ -192,6 +202,9 @@ export async function redeemAsync(
192
202
  if (!addresses.escrow) throw new MissingEscrowAddressError();
193
203
  const escrow = addresses.escrow;
194
204
 
205
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
206
+ await validateWalletChain(signer, addresses.hubChainId);
207
+
195
208
  // Pre-flight: validate async cross-chain setup before sending any transaction
196
209
  await preflightAsync(provider, addresses.vault, escrow);
197
210
 
@@ -10,6 +10,12 @@ export interface VaultAddresses {
10
10
  shareOFT?: string;
11
11
  /** OFT address for USDC bridging (cross-chain only). */
12
12
  usdcOFT?: string;
13
+ /**
14
+ * Expected EVM chain ID of the hub. When provided, SDK functions will
15
+ * throw a clear WrongChainError if the signer is on a different chain.
16
+ * Prevents silent failures when MetaMask is connected to the wrong network.
17
+ */
18
+ hubChainId?: number;
13
19
  }
14
20
 
15
21
  /** Result of a synchronous deposit or mint. */
@@ -0,0 +1,17 @@
1
+ import type { Signer } from 'ethers'
2
+
3
+ /**
4
+ * Cast an ethers Signer (e.g. from wagmi's useEthersSigner adapter) to
5
+ * the SDK's expected type. Use this to avoid `as any` casts:
6
+ * ```ts
7
+ * import { useEthersSigner } from './wagmi-ethers-adapter'
8
+ * import { asSdkSigner } from '@oydual31/more-vaults-sdk/ethers'
9
+ * const signer = asSdkSigner(useEthersSigner())
10
+ * ```
11
+ * This function validates the signer is non-null and applies a documented
12
+ * cast instead of an opaque `as any`.
13
+ */
14
+ export function asSdkSigner(signer: unknown): Signer {
15
+ if (!signer) throw new Error('[MoreVaults] No signer available. Make sure the wallet is connected and wagmi is configured correctly.')
16
+ return signer as Signer
17
+ }
@@ -0,0 +1,14 @@
1
+ import type { WalletClient } from 'viem'
2
+ import { WrongChainError } from './errors'
3
+
4
+ /**
5
+ * Validate that the walletClient is connected to the expected chain.
6
+ * Only validates if hubChainId is provided — opt-in, non-breaking.
7
+ */
8
+ export function validateWalletChain(walletClient: WalletClient, hubChainId?: number): void {
9
+ if (!hubChainId) return
10
+ const current = walletClient.chain?.id
11
+ if (current !== undefined && current !== hubChainId) {
12
+ throw new WrongChainError(current, hubChainId)
13
+ }
14
+ }
@@ -0,0 +1,35 @@
1
+ /** EVM Chain IDs for chains supported by MoreVaults */
2
+ export const CHAIN_IDS = {
3
+ flowEVMMainnet: 747,
4
+ flowEVMTestnet: 545,
5
+ arbitrum: 42161,
6
+ base: 8453,
7
+ ethereum: 1,
8
+ } as const
9
+
10
+ /** LayerZero Endpoint IDs (EID) for chains supported by MoreVaults */
11
+ export const LZ_EIDS = {
12
+ flowMainnet: 30332,
13
+ flowTestnet: 30333,
14
+ arbitrum: 30110,
15
+ base: 30184,
16
+ ethereum: 30101,
17
+ } as const
18
+
19
+ /** LayerZero EID → EVM Chain ID */
20
+ export const EID_TO_CHAIN_ID: Record<number, number> = {
21
+ [LZ_EIDS.flowMainnet]: CHAIN_IDS.flowEVMMainnet,
22
+ [LZ_EIDS.flowTestnet]: CHAIN_IDS.flowEVMTestnet,
23
+ [LZ_EIDS.arbitrum]: CHAIN_IDS.arbitrum,
24
+ [LZ_EIDS.base]: CHAIN_IDS.base,
25
+ [LZ_EIDS.ethereum]: CHAIN_IDS.ethereum,
26
+ }
27
+
28
+ /** EVM Chain ID → LayerZero EID */
29
+ export const CHAIN_ID_TO_EID: Record<number, number> = {
30
+ [CHAIN_IDS.flowEVMMainnet]: LZ_EIDS.flowMainnet,
31
+ [CHAIN_IDS.flowEVMTestnet]: LZ_EIDS.flowTestnet,
32
+ [CHAIN_IDS.arbitrum]: LZ_EIDS.arbitrum,
33
+ [CHAIN_IDS.base]: LZ_EIDS.base,
34
+ [CHAIN_IDS.ethereum]: LZ_EIDS.ethereum,
35
+ }
@@ -15,6 +15,7 @@ import { ActionType } from './types'
15
15
  import { ensureAllowance, getVaultStatus, quoteLzFee } from './utils'
16
16
  import { preflightSync, preflightAsync } from './preflight'
17
17
  import { MissingEscrowAddressError, VaultPausedError, CapacityFullError } from './errors'
18
+ import { validateWalletChain } from './chainValidation'
18
19
 
19
20
  /**
20
21
  * D1 / D3 — Simple deposit (ERC-4626 standard).
@@ -42,6 +43,9 @@ export async function depositSimple(
42
43
  const account = walletClient.account!
43
44
  const vault = getAddress(addresses.vault)
44
45
 
46
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
47
+ validateWalletChain(walletClient, addresses.hubChainId)
48
+
45
49
  // Pre-flight: validate vault is operational and accepting deposits
46
50
  await preflightSync(publicClient, vault)
47
51
 
@@ -111,6 +115,9 @@ export async function depositMultiAsset(
111
115
  const account = walletClient.account!
112
116
  const vault = getAddress(addresses.vault)
113
117
 
118
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
119
+ validateWalletChain(walletClient, addresses.hubChainId)
120
+
114
121
  // Approve each token
115
122
  for (let i = 0; i < tokens.length; i++) {
116
123
  await ensureAllowance(walletClient, publicClient, tokens[i], vault, amounts[i])
@@ -169,6 +176,9 @@ export async function depositAsync(
169
176
  if (!addresses.escrow) throw new MissingEscrowAddressError()
170
177
  const escrow = getAddress(addresses.escrow)
171
178
 
179
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
180
+ validateWalletChain(walletClient, addresses.hubChainId)
181
+
172
182
  // Pre-flight: validate async cross-chain setup before sending any transaction
173
183
  await preflightAsync(publicClient, vault, escrow)
174
184
 
@@ -244,6 +254,9 @@ export async function mintAsync(
244
254
  if (!addresses.escrow) throw new MissingEscrowAddressError()
245
255
  const escrow = getAddress(addresses.escrow)
246
256
 
257
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
258
+ validateWalletChain(walletClient, addresses.hubChainId)
259
+
247
260
  // Pre-flight: validate async cross-chain setup before sending any transaction
248
261
  await preflightAsync(publicClient, vault, escrow)
249
262
 
@@ -79,3 +79,12 @@ export class MissingEscrowAddressError extends MoreVaultsError {
79
79
  this.name = 'MissingEscrowAddressError'
80
80
  }
81
81
  }
82
+
83
+ export class WrongChainError extends MoreVaultsError {
84
+ constructor(currentChainId: number, expectedChainId: number) {
85
+ super(
86
+ `Wrong network: wallet is on chain ${currentChainId}, but the vault hub requires chain ${expectedChainId}. Switch networks before proceeding.`,
87
+ )
88
+ this.name = 'WrongChainError'
89
+ }
90
+ }
package/src/viem/index.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  // MoreVaults SDK — viem/wagmi
2
2
  // Provides typed helpers for all deposit, redeem, and cross-chain vault flows.
3
3
 
4
+ // --- Chain constants ---
5
+ export { CHAIN_IDS, LZ_EIDS, EID_TO_CHAIN_ID, CHAIN_ID_TO_EID } from './chains'
6
+
4
7
  // --- ABIs ---
5
8
  export {
6
9
  VAULT_ABI,
@@ -33,6 +36,7 @@ export {
33
36
  EscrowNotConfiguredError,
34
37
  NotHubVaultError,
35
38
  MissingEscrowAddressError,
39
+ WrongChainError,
36
40
  } from './errors'
37
41
 
38
42
  // --- Deposit Flows ---
@@ -98,3 +102,10 @@ export type {
98
102
  MaxWithdrawable,
99
103
  VaultSummary,
100
104
  } from './userHelpers'
105
+
106
+ // --- wagmi compatibility ---
107
+ // Re-export viem's PublicClient type for wagmi compatibility.
108
+ // wagmi's usePublicClient() returns a type that is structurally compatible
109
+ // with viem's PublicClient but TypeScript may complain without this cast helper.
110
+ export type { PublicClient as SdkPublicClient } from 'viem'
111
+ export { asSdkClient } from './wagmiCompat'
@@ -17,6 +17,7 @@ import { ActionType } from './types'
17
17
  import { ensureAllowance } from './utils'
18
18
  import { preflightAsync, preflightRedeemLiquidity } from './preflight'
19
19
  import { MissingEscrowAddressError } from './errors'
20
+ import { validateWalletChain } from './chainValidation'
20
21
 
21
22
  /**
22
23
  * R1 — Simple share redemption (ERC-4626 standard).
@@ -46,6 +47,9 @@ export async function redeemShares(
46
47
  const account = walletClient.account!
47
48
  const vault = getAddress(addresses.vault)
48
49
 
50
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
51
+ validateWalletChain(walletClient, addresses.hubChainId)
52
+
49
53
  const { result: assets } = await publicClient.simulateContract({
50
54
  address: vault,
51
55
  abi: VAULT_ABI,
@@ -94,6 +98,9 @@ export async function withdrawAssets(
94
98
  const account = walletClient.account!
95
99
  const vault = getAddress(addresses.vault)
96
100
 
101
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
102
+ validateWalletChain(walletClient, addresses.hubChainId)
103
+
97
104
  const { result: sharesBurned } = await publicClient.simulateContract({
98
105
  address: vault,
99
106
  abi: VAULT_ABI,
@@ -143,6 +150,9 @@ export async function requestRedeem(
143
150
  const account = walletClient.account!
144
151
  const vault = getAddress(addresses.vault)
145
152
 
153
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
154
+ validateWalletChain(walletClient, addresses.hubChainId)
155
+
146
156
  await publicClient.simulateContract({
147
157
  address: vault,
148
158
  abi: VAULT_ABI,
@@ -231,6 +241,9 @@ export async function redeemAsync(
231
241
  if (!addresses.escrow) throw new MissingEscrowAddressError()
232
242
  const escrow = getAddress(addresses.escrow)
233
243
 
244
+ // Validate wallet is on the correct chain (opt-in via hubChainId)
245
+ validateWalletChain(walletClient, addresses.hubChainId)
246
+
234
247
  // Pre-flight: validate async cross-chain setup before sending any transaction
235
248
  await preflightAsync(publicClient, vault, escrow)
236
249
 
package/src/viem/types.ts CHANGED
@@ -9,6 +9,12 @@ export interface VaultAddresses {
9
9
  shareOFT?: Address
10
10
  /** OFT for USDC bridging (cross-chain deposits from spoke) */
11
11
  usdcOFT?: Address
12
+ /**
13
+ * Expected EVM chain ID of the hub. When provided, SDK functions will
14
+ * throw a clear WrongChainError if the walletClient is on a different chain.
15
+ * Prevents silent failures when MetaMask is connected to the wrong network.
16
+ */
17
+ hubChainId?: number
12
18
  }
13
19
 
14
20
  export interface DepositResult {
@@ -0,0 +1,18 @@
1
+ import type { PublicClient } from 'viem'
2
+
3
+ /**
4
+ * Cast a wagmi PublicClient to the SDK's expected type.
5
+ * Use this in React components to avoid `as any` casts:
6
+ * ```ts
7
+ * import { usePublicClient } from 'wagmi'
8
+ * import { asSdkClient } from '@oydual31/more-vaults-sdk/viem'
9
+ * const pc = asSdkClient(usePublicClient())
10
+ * ```
11
+ * wagmi v2 uses viem as a peer dependency, so the types are structurally
12
+ * identical — this function validates the client is non-null and applies
13
+ * a documented cast instead of an opaque `as any`.
14
+ */
15
+ export function asSdkClient(client: unknown): PublicClient {
16
+ if (!client) throw new Error('[MoreVaults] No public client available. Make sure wagmi is configured correctly.')
17
+ return client as PublicClient
18
+ }