@oydual31/more-vaults-sdk 1.1.21 → 1.1.23

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": "1.1.21",
3
+ "version": "1.1.23",
4
4
  "description": "TypeScript SDK for MoreVaults protocol — viem/wagmi and ethers.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -4,6 +4,11 @@
4
4
  */
5
5
 
6
6
  export const VAULT_ABI = [
7
+ // Errors (for proper revert decoding)
8
+ "error WithdrawalQueueDisabled()",
9
+ "error WithdrawalQueueEnabled()",
10
+ "error RequestWithdrawDisabled()",
11
+
7
12
  // ERC4626 core
8
13
  "function deposit(uint256 assets, address receiver) returns (uint256 shares)",
9
14
  "function mint(uint256 shares, address receiver) returns (uint256 assets)",
@@ -10,6 +10,7 @@ import {
10
10
  NoSuchActionsError,
11
11
  CapacityFullError,
12
12
  InvalidInputError,
13
+ WithdrawalQueueDisabledError,
13
14
  } from './errors.js'
14
15
 
15
16
  /**
@@ -46,6 +47,13 @@ export function parseContractError(err: unknown, vault: string, caller?: string)
46
47
  throw new VaultPausedError(vault)
47
48
  }
48
49
 
50
+ // Withdrawal queue disabled — caller used requestRedeem/requestWithdraw on a vault
51
+ // where the queue is off. Should use redeemShares/withdrawAssets directly, or smartRedeem.
52
+ // 0xdbb22fbf is the selector for WithdrawalQueueDisabled().
53
+ if (msg.includes('WithdrawalQueueDisabled') || msg.includes('0xdbb22fbf')) {
54
+ throw new WithdrawalQueueDisabledError(vault)
55
+ }
56
+
49
57
  // Role checks
50
58
  if (msg.includes('NotCurator') || msg.includes('OwnableUnauthorizedAccount')) {
51
59
  if (msg.includes('NotCurator')) {
@@ -42,6 +42,13 @@ export class WhitelistQuotaExhaustedError extends MoreVaultsError {
42
42
  }
43
43
  }
44
44
 
45
+ export class WithdrawalQueueDisabledError extends MoreVaultsError {
46
+ constructor(vault: string) {
47
+ super(`[MoreVaults] Withdrawal queue is disabled on vault ${vault}. Cannot call requestRedeem/requestWithdraw — use redeemShares/withdrawAssets directly, or smartRedeem which auto-selects the correct flow.`)
48
+ this.name = 'WithdrawalQueueDisabledError'
49
+ }
50
+ }
51
+
45
52
  export class InsufficientLiquidityError extends MoreVaultsError {
46
53
  hubLiquid: bigint
47
54
  required: bigint
@@ -93,6 +93,7 @@ export {
93
93
  ComposeTimeoutError,
94
94
  ComposeAlreadyExecutedError,
95
95
  WithdrawalTimelockActiveError,
96
+ WithdrawalQueueDisabledError,
96
97
  } from "./errors";
97
98
 
98
99
  // --- Deposit flows ---
@@ -15,7 +15,7 @@ import {
15
15
  } from "./types";
16
16
  import type { ContractTransactionReceipt } from "ethers";
17
17
  import { preflightAsync, preflightRedeemLiquidity } from "./preflight";
18
- import { EscrowNotConfiguredError, InvalidInputError, VaultPausedError, WithdrawalTimelockActiveError } from "./errors";
18
+ import { EscrowNotConfiguredError, InvalidInputError, VaultPausedError, WithdrawalTimelockActiveError, WithdrawalQueueDisabledError } from "./errors";
19
19
  import { validateWalletChain } from "./chainValidation";
20
20
  import { getVaultStatus, quoteLzFee, detectStargateOft } from "./utils";
21
21
  import { CHAIN_ID_TO_EID, OFT_ROUTES, createChainProvider } from "./chains";
@@ -159,6 +159,16 @@ export async function requestRedeem(
159
159
  // Validate wallet is on the correct chain (opt-in via hubChainId)
160
160
  await validateWalletChain(signer, addresses.hubChainId);
161
161
 
162
+ // Pre-check: vault must have withdrawal queue enabled — otherwise the contract
163
+ // reverts with WithdrawalQueueDisabled (0xdbb22fbf). Use redeemShares directly
164
+ // or smartRedeem which auto-selects the correct flow.
165
+ const provider = signer.provider!
166
+ const configRead = new Contract(addresses.vault, ['function getWithdrawalQueueStatus() view returns (bool)'], provider)
167
+ const queueEnabled: boolean = await configRead.getWithdrawalQueueStatus()
168
+ if (!queueEnabled) {
169
+ throw new WithdrawalQueueDisabledError(addresses.vault)
170
+ }
171
+
162
172
  // Detect which signature the vault supports: new (uint256, address) or legacy (uint256)
163
173
  let useLegacy = false
164
174
  const vaultNew = new Contract(addresses.vault, VAULT_ABI, signer)
package/src/viem/abis.ts CHANGED
@@ -4,6 +4,10 @@
4
4
  */
5
5
 
6
6
  export const VAULT_ABI = [
7
+ // ── Errors (for proper revert decoding) ──
8
+ { type: 'error', name: 'WithdrawalQueueDisabled', inputs: [] },
9
+ { type: 'error', name: 'WithdrawalQueueEnabled', inputs: [] },
10
+ { type: 'error', name: 'RequestWithdrawDisabled', inputs: [] },
7
11
  {
8
12
  type: 'function',
9
13
  name: 'deposit',
@@ -126,7 +126,7 @@ export async function addAvailableAssets(
126
126
  ): Promise<{ txHash: `0x${string}` }> {
127
127
  const account = walletClient.account!
128
128
  const v = getAddress(vault)
129
- const checksummed = assets.map(getAddress)
129
+ const checksummed = assets.map(a => getAddress(a))
130
130
 
131
131
  try {
132
132
  await publicClient.simulateContract({
@@ -268,7 +268,7 @@ export async function setDepositWhitelist(
268
268
  throw new InvalidInputError('depositors and caps arrays must have the same length')
269
269
  }
270
270
 
271
- const checksummed = depositors.map(getAddress)
271
+ const checksummed = depositors.map(a => getAddress(a))
272
272
 
273
273
  try {
274
274
  await publicClient.simulateContract({
@@ -267,8 +267,9 @@ export const LZ_TIMEOUTS = {
267
267
  *
268
268
  * Note on struct differences:
269
269
  * - SwapRouter (Eth/Arb/Op, 0xE592...): exactInputSingle struct includes `deadline`
270
- * - SwapRouter02 (Base, 0x2626...): exactInputSingle struct does NOT include `deadline`
271
- * - FlowSwap V3 (Flow EVM, 0xeEDC...): derived from original UniV3, includes `deadline`
270
+ * - SwapRouter02 (Base/Flow, 0x2626.../0xeEDC...): struct does NOT include `deadline`
271
+ * Flow EVM confirmed via PUSH4 scan: router only dispatches 0x04e45aaf (SwapRouter02),
272
+ * not 0x414bf389 (SwapRouter). Both are in curatorSwaps.ts SWAP_ROUTER02_CHAINS.
272
273
  */
273
274
  export const UNISWAP_V3_ROUTERS: Record<number, `0x${string}`> = {
274
275
  [8453]: '0x2626664c2603336E57B271c5C0b26F421741e481', // Base — SwapRouter02 (no deadline)
@@ -138,7 +138,7 @@ export function encodeCuratorAction(action: CuratorAction): `0x${string}` {
138
138
  return encodeFunctionData({
139
139
  abi: ADMIN_WRITE_ABI,
140
140
  functionName: 'addAvailableAssets',
141
- args: [action.assets.map(getAddress)],
141
+ args: [action.assets.map(a => getAddress(a))],
142
142
  })
143
143
 
144
144
  case 'disableAssetToDeposit':
@@ -55,7 +55,7 @@ export async function getCuratorVaultStatus(
55
55
  timeLockPeriod,
56
56
  maxSlippagePercent,
57
57
  currentNonce,
58
- availableAssets: availableAssets.map(getAddress),
58
+ availableAssets: availableAssets.map(a => getAddress(a)),
59
59
  lzAdapter: getAddress(lzAdapter),
60
60
  paused,
61
61
  }
@@ -177,8 +177,8 @@ export async function getVaultAnalysis(
177
177
  }).catch(() => null),
178
178
  ])
179
179
 
180
- const availableAddresses = (availableRaw as Address[]).map(getAddress)
181
- const depositableAddresses = (depositableRaw as Address[]).map(getAddress)
180
+ const availableAddresses = (availableRaw as Address[]).map(a => getAddress(a))
181
+ const depositableAddresses = (depositableRaw as Address[]).map(a => getAddress(a))
182
182
 
183
183
  // Deduplicated set of all asset addresses we need metadata for
184
184
  const allAddresses = Array.from(new Set([...availableAddresses, ...depositableAddresses]))
@@ -302,7 +302,7 @@ export async function getVaultAssetBreakdown(
302
302
  return { assets: [], totalAssets: 0n, totalSupply: 0n, underlyingDecimals: 6 }
303
303
  }
304
304
 
305
- const addresses = availableRaw.map(getAddress)
305
+ const addresses = availableRaw.map(a => getAddress(a))
306
306
 
307
307
  // Step 2: multicall — balanceOf + metadata for each asset + totalAssets + totalSupply + vault decimals
308
308
  const results = await publicClient.multicall({
@@ -86,8 +86,8 @@ export async function getSubVaultPositions(
86
86
  .catch(() => [] as Address[]),
87
87
  ])
88
88
 
89
- const erc4626Vaults = (erc4626Raw as Address[]).map(getAddress)
90
- const erc7540Vaults = (erc7540Raw as Address[]).map(getAddress)
89
+ const erc4626Vaults = (erc4626Raw as Address[]).map(a => getAddress(a))
90
+ const erc7540Vaults = (erc7540Raw as Address[]).map(a => getAddress(a))
91
91
 
92
92
  const allSubVaults: Array<{ address: Address; type: 'erc4626' | 'erc7540' }> = [
93
93
  ...erc4626Vaults.map((a) => ({ address: a, type: 'erc4626' as const })),
@@ -465,7 +465,7 @@ export async function getVaultPortfolio(
465
465
  ? getAddress(vaultTotals[2].result as Address)
466
466
  : zeroAddress
467
467
 
468
- const availableAddresses = (availableRaw as Address[]).map(getAddress)
468
+ const availableAddresses = (availableRaw as Address[]).map(a => getAddress(a))
469
469
 
470
470
  // Sub-vault share addresses to exclude from liquid assets (avoid double-counting)
471
471
  const subVaultAddressSet = new Set(subVaultPositions.map((p) => p.address.toLowerCase()))
@@ -10,7 +10,7 @@
10
10
  * - Ethereum (1): SwapRouter 0xE592... — HAS deadline field
11
11
  * - Arbitrum (42161): SwapRouter 0xE592... — HAS deadline field
12
12
  * - Optimism (10): SwapRouter 0xE592... — HAS deadline field
13
- * - Flow EVM (747): FlowSwap V3 0xeEDC... — HAS deadline field
13
+ * - Flow EVM (747): FlowSwap V3 0xeEDC... — NO deadline field (SwapRouter02-compatible)
14
14
  *
15
15
  * @module curatorSwaps
16
16
  */
@@ -89,8 +89,10 @@ const UNISWAP_V3_SWAP_ROUTER02_ABI = [
89
89
  /**
90
90
  * Chains that use SwapRouter02 (no deadline in struct).
91
91
  * All other chains in UNISWAP_V3_ROUTERS use the original SwapRouter.
92
+ *
93
+ * Flow EVM (747): FlowSwap V3 router exposes exactInputSingle with selector 0x04e45aaf
92
94
  */
93
- const SWAP_ROUTER02_CHAINS = new Set([8453])
95
+ const SWAP_ROUTER02_CHAINS = new Set([8453, 747])
94
96
 
95
97
  // ─────────────────────────────────────────────────────────────────────────────
96
98
  // Calldata encoding
@@ -10,6 +10,7 @@ import {
10
10
  NoSuchActionsError,
11
11
  CapacityFullError,
12
12
  InvalidInputError,
13
+ WithdrawalQueueDisabledError,
13
14
  } from './errors.js'
14
15
 
15
16
  /**
@@ -45,6 +46,13 @@ export function parseContractError(err: unknown, vault: string, caller?: string)
45
46
  throw new VaultPausedError(vault)
46
47
  }
47
48
 
49
+ // Withdrawal queue disabled — caller used requestRedeem/requestWithdraw on a vault
50
+ // where the queue is off. Should use redeemShares/withdrawAssets directly, or smartRedeem.
51
+ // 0xdbb22fbf is the selector for WithdrawalQueueDisabled().
52
+ if (msg.includes('WithdrawalQueueDisabled') || msg.includes('0xdbb22fbf')) {
53
+ throw new WithdrawalQueueDisabledError(vault)
54
+ }
55
+
48
56
  // Role checks
49
57
  if (msg.includes('NotCurator') || msg.includes('OwnableUnauthorizedAccount')) {
50
58
  // Distinguish curator vs owner by checking the specific error
@@ -42,6 +42,13 @@ export class WhitelistQuotaExhaustedError extends MoreVaultsError {
42
42
  }
43
43
  }
44
44
 
45
+ export class WithdrawalQueueDisabledError extends MoreVaultsError {
46
+ constructor(vault: string) {
47
+ super(`[MoreVaults] Withdrawal queue is disabled on vault ${vault}. Cannot call requestRedeem/requestWithdraw — use redeemShares/withdrawAssets directly, or smartRedeem which auto-selects the correct flow.`)
48
+ this.name = 'WithdrawalQueueDisabledError'
49
+ }
50
+ }
51
+
45
52
  export class InsufficientLiquidityError extends MoreVaultsError {
46
53
  hubLiquid: bigint
47
54
  required: bigint
package/src/viem/index.ts CHANGED
@@ -89,6 +89,7 @@ export {
89
89
  ComposeTimeoutError,
90
90
  ComposeAlreadyExecutedError,
91
91
  WithdrawalTimelockActiveError,
92
+ WithdrawalQueueDisabledError,
92
93
  } from './errors'
93
94
 
94
95
  // --- Deposit Flows ---
@@ -18,7 +18,7 @@ import type {
18
18
  import { ActionType } from './types'
19
19
  import { ensureAllowance, getVaultStatus, quoteLzFee, detectStargateOft } from './utils'
20
20
  import { preflightAsync, preflightRedeemLiquidity } from './preflight'
21
- import { EscrowNotConfiguredError, VaultPausedError, InvalidInputError, WithdrawalTimelockActiveError } from './errors'
21
+ import { EscrowNotConfiguredError, VaultPausedError, InvalidInputError, WithdrawalTimelockActiveError, WithdrawalQueueDisabledError } from './errors'
22
22
  import { validateWalletChain } from './chainValidation'
23
23
  import { parseContractError } from './errorParser'
24
24
  import { OFT_ROUTES, CHAIN_ID_TO_EID } from './chains'
@@ -175,6 +175,18 @@ export async function requestRedeem(
175
175
  // Validate wallet is on the correct chain (opt-in via hubChainId)
176
176
  validateWalletChain(walletClient, addresses.hubChainId)
177
177
 
178
+ // Pre-check: vault must have withdrawal queue enabled — otherwise the contract
179
+ // reverts with WithdrawalQueueDisabled (0xdbb22fbf). Use redeemShares directly
180
+ // or smartRedeem which auto-selects the correct flow.
181
+ const queueEnabled = await publicClient.readContract({
182
+ address: vault,
183
+ abi: CONFIG_ABI,
184
+ functionName: 'getWithdrawalQueueStatus',
185
+ })
186
+ if (!queueEnabled) {
187
+ throw new WithdrawalQueueDisabledError(vault)
188
+ }
189
+
178
190
  // Detect which signature the vault supports: new (uint256, address) or legacy (uint256)
179
191
  let useLegacy = false
180
192
 
@@ -93,7 +93,7 @@ export async function getVaultConfiguration(
93
93
  const num_ = (i: number): number =>
94
94
  results[i].status === 'success' ? Number(results[i].result) : 0
95
95
  const addrArray = (i: number): Address[] =>
96
- results[i].status === 'success' ? (results[i].result as Address[]).map(getAddress) : []
96
+ results[i].status === 'success' ? (results[i].result as Address[]).map(a => getAddress(a)) : []
97
97
 
98
98
  return {
99
99
  // Roles