@augustdigital/sdk 8.25.0 → 8.26.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.
@@ -1,6 +1,12 @@
1
1
  import type { IEmberVault, IFetchEmberVaultsOptions } from './types';
2
2
  /**
3
- * Fetch Ember vaults from API
3
+ * Fetch Ember vaults from API.
4
+ *
5
+ * Results are cached for {@link EMBER_CACHE_TTL_MS} (keyed by the full request
6
+ * URL, so different query options cache independently) and the request is
7
+ * aborted after {@link EMBER_FETCH_TIMEOUT_MS}. Failures — including timeouts —
8
+ * are never cached and resolve to `[]`, preserving the long-standing
9
+ * fail-tolerant contract of this getter.
4
10
  */
5
11
  export declare function getEmberVaults(options?: IFetchEmberVaultsOptions): Promise<IEmberVault[]>;
6
12
  /**
@@ -4,9 +4,32 @@ exports.getEmberVaults = getEmberVaults;
4
4
  exports.getEmberTVL = getEmberTVL;
5
5
  const constants_1 = require("./constants");
6
6
  const core_1 = require("../../core");
7
+ const cache_1 = require("../../core/cache");
7
8
  const logger_1 = require("../../core/logger");
8
9
  /**
9
- * Fetch Ember vaults from API
10
+ * Hard ceiling on the Ember (Bluefin) vaults request. The endpoint is a
11
+ * third-party API awaited on the `getVaults` hot path; without a bound, a
12
+ * hung connection stalls every vault-list consumer indefinitely (the fetch
13
+ * options carry no signal and no timeout). Measured latency is 0.24–1.02s,
14
+ * so 5s is ~5× the observed worst case while still failing fast enough for
15
+ * callers racing `getVaults` against their own budgets.
16
+ */
17
+ const EMBER_FETCH_TIMEOUT_MS = 5_000;
18
+ /**
19
+ * TTL for the cached Ember vaults payload. The set of active Ember vaults
20
+ * changes rarely (the SDK additionally filters it to a hardcoded allowlist),
21
+ * so 5 minutes matches the freshness of the other list-shaped caches without
22
+ * letting a stale payload outlive a delisting for long.
23
+ */
24
+ const EMBER_CACHE_TTL_MS = 5 * 60 * 1000;
25
+ /**
26
+ * Fetch Ember vaults from API.
27
+ *
28
+ * Results are cached for {@link EMBER_CACHE_TTL_MS} (keyed by the full request
29
+ * URL, so different query options cache independently) and the request is
30
+ * aborted after {@link EMBER_FETCH_TIMEOUT_MS}. Failures — including timeouts —
31
+ * are never cached and resolve to `[]`, preserving the long-standing
32
+ * fail-tolerant contract of this getter.
10
33
  */
11
34
  async function getEmberVaults(options) {
12
35
  try {
@@ -19,12 +42,36 @@ async function getEmberVaults(options) {
19
42
  params.append('status', options.status);
20
43
  const queryString = params.toString();
21
44
  const url = `${constants_1.EMBER_API_BASE_URL}${queryString ? `?${queryString}` : ''}`;
22
- const response = await fetch(url, core_1.DEFAULT_FETCH_OPTIONS);
23
- if (!response.ok) {
24
- throw new Error(`HTTP error! status: ${response.status}`);
45
+ const cacheKey = `ember-vaults-${url}`;
46
+ // The shared LRU is constructed with `allowStale: true`; opt out here so a
47
+ // payload past its TTL is refetched rather than served one last time —
48
+ // keeping the 5-minute bound above an exact contract.
49
+ const cached = cache_1.CACHE.get(cacheKey, { allowStale: false });
50
+ if (cached)
51
+ return cached;
52
+ // AbortController (not AbortSignal.timeout) for browser + older-Node parity.
53
+ // The timer stays armed through the body read: `fetch` resolves as soon as
54
+ // headers arrive, so clearing it there would leave a stalled `json()`
55
+ // unbounded — the exact hang the ceiling exists to prevent.
56
+ const controller = new AbortController();
57
+ const timer = setTimeout(() => controller.abort(), EMBER_FETCH_TIMEOUT_MS);
58
+ let data;
59
+ try {
60
+ const response = await fetch(url, {
61
+ ...core_1.DEFAULT_FETCH_OPTIONS,
62
+ signal: controller.signal,
63
+ });
64
+ if (!response.ok) {
65
+ throw new Error(`HTTP error! status: ${response.status}`);
66
+ }
67
+ data = await response.json();
68
+ }
69
+ finally {
70
+ clearTimeout(timer);
25
71
  }
26
- const data = await response.json();
27
- return Array.isArray(data) ? data : [];
72
+ const vaults = Array.isArray(data) ? data : [];
73
+ cache_1.CACHE.set(cacheKey, vaults, { ttl: EMBER_CACHE_TTL_MS });
74
+ return vaults;
28
75
  }
29
76
  catch (error) {
30
77
  logger_1.Logger.log.error('getEmberVaults', error);
@@ -3,4 +3,4 @@
3
3
  * Generated during publish from package.json version
4
4
  * This file is gitignored and created at publish time
5
5
  */
6
- export declare const SDK_VERSION = "8.25.0";
6
+ export declare const SDK_VERSION = "8.26.0";
@@ -6,5 +6,5 @@ exports.SDK_VERSION = void 0;
6
6
  * Generated during publish from package.json version
7
7
  * This file is gitignored and created at publish time
8
8
  */
9
- exports.SDK_VERSION = '8.25.0';
9
+ exports.SDK_VERSION = '8.26.0';
10
10
  //# sourceMappingURL=version.js.map
package/lib/main.d.ts CHANGED
@@ -7,9 +7,9 @@ import SolanaAdapter from './adapters/solana';
7
7
  import SuiAdapter from './adapters/sui';
8
8
  import StellarAdapter from './adapters/stellar';
9
9
  import EVMAdapter from './adapters/evm';
10
- import type { IAddress, IChainId, IVaultHistoricalParams, IWSMonitorHeaders } from './types';
10
+ import type { IAddress, IChainId, IVaultHistoricalParams } from './types';
11
11
  import { AugustBase, type IAugustBase } from './core';
12
- import { AugustVaults, type IVaultBaseOptions, type IVaultCustomOptions } from './modules/vaults';
12
+ import { AugustVaults, type IGetVaultsOptions, type IVaultBaseOptions, type IVaultCustomOptions } from './modules/vaults';
13
13
  import type { Signer, Wallet } from 'ethers';
14
14
  import type { IContractWriteOptions } from './modules/vaults/write.actions';
15
15
  import { AugustSubAccounts } from './modules/sub-accounts';
@@ -77,15 +77,12 @@ export declare class AugustSDK extends AugustBase {
77
77
  /**
78
78
  * Fetch all available vaults across configured networks.
79
79
  * Optionally filter by chain IDs and include loan/allocation data.
80
- * @param options - Configuration for filtering and enriching vault data
80
+ * @param options - Configuration for filtering and enriching vault data
81
+ * see {@link IGetVaultsOptions} for the full surface (`includeClosed`
82
+ * portfolio mode, `maxRetries`/`baseDelay` retry tuning)
81
83
  * @returns Array of vault objects with metadata and optional position data
82
84
  */
83
- getVaults(options?: {
84
- chainIds?: number[];
85
- headers?: IWSMonitorHeaders;
86
- loadSubaccounts?: boolean;
87
- loadSnapshots?: boolean;
88
- } & IVaultCustomOptions): Promise<import("./types").IVault[]>;
85
+ getVaults(options?: IGetVaultsOptions): Promise<import("./types").IVault[]>;
89
86
  getTotalDeposited(options?: {
90
87
  loadSubaccounts?: boolean;
91
88
  loadSnapshots?: boolean;
package/lib/main.js CHANGED
@@ -207,7 +207,9 @@ class AugustSDK extends core_1.AugustBase {
207
207
  /**
208
208
  * Fetch all available vaults across configured networks.
209
209
  * Optionally filter by chain IDs and include loan/allocation data.
210
- * @param options - Configuration for filtering and enriching vault data
210
+ * @param options - Configuration for filtering and enriching vault data
211
+ * see {@link IGetVaultsOptions} for the full surface (`includeClosed`
212
+ * portfolio mode, `maxRetries`/`baseDelay` retry tuning)
211
213
  * @returns Array of vault objects with metadata and optional position data
212
214
  */
213
215
  async getVaults(options) {
@@ -1,4 +1,4 @@
1
- import { type IAddress, type IHistoricalTimeseriesResponse, type INormalizedNumber, type ISubgraphWithdrawProccessed, type IVault, type IVaultAllocations, type IVaultAnnualizedApy, type IVaultAvailableRedemption, type IVaultLoan, type IVaultPosition, type IVaultRedemptionHistoryItem, type IVaultSummary, type IVaultWithdrawals, type IVaultPendingRedemptions, type IActiveStakingPosition, type IVaultHistoricalParams, type IVaultUserLifetimePnl, type IVaultBorrowerHealthFactor, type IVaultPnl, type VaultAddress } from '../../types';
1
+ import { type IAddress, type IHistoricalTimeseriesResponse, type INormalizedNumber, type ITokenizedVault, type ISubgraphWithdrawProccessed, type IVault, type IVaultAllocations, type IVaultAnnualizedApy, type IVaultAvailableRedemption, type IVaultLoan, type IVaultPosition, type IVaultRedemptionHistoryItem, type IVaultSummary, type IVaultWithdrawals, type IVaultPendingRedemptions, type IActiveStakingPosition, type IVaultHistoricalParams, type IVaultUserLifetimePnl, type IVaultBorrowerHealthFactor, type IVaultPnl, type VaultAddress } from '../../types';
2
2
  import type { IVaultBaseOptions } from './types';
3
3
  /**
4
4
  * Vault Data Getters
@@ -23,15 +23,22 @@ import type { IVaultBaseOptions } from './types';
23
23
  * @param loans - Include active loan data
24
24
  * @param allocations - Include DeFi/CeFi allocation breakdowns
25
25
  * @param options - RPC and service configuration
26
+ * @param tokenizedVault - Optional pre-fetched backend row for `vault`. When a
27
+ * caller already holds the row (e.g. `getVaults` fetched the whole list one
28
+ * call earlier), passing it skips this function's own
29
+ * `GET /tokenized_vault/{address}` — a pure de-duplication of backend
30
+ * traffic. The row must have been fetched with the same
31
+ * `loadSubaccounts`/`loadSnapshots` flags the caller passes here.
26
32
  * @returns Complete vault object with optional enrichments
27
33
  */
28
- export declare function getVault({ vault, loans, allocations, options, loadSubaccounts, loadSnapshots, }: {
34
+ export declare function getVault({ vault, loans, allocations, options, loadSubaccounts, loadSnapshots, tokenizedVault: prefetchedRow, }: {
29
35
  vault: IAddress;
30
36
  loans?: boolean;
31
37
  allocations?: boolean;
32
38
  options: IVaultBaseOptions;
33
39
  loadSubaccounts?: boolean;
34
40
  loadSnapshots?: boolean;
41
+ tokenizedVault?: ITokenizedVault;
35
42
  }): Promise<IVault>;
36
43
  /**
37
44
  * Vault Loans
@@ -112,13 +112,19 @@ const errors_1 = require("../../core/errors");
112
112
  * @param loans - Include active loan data
113
113
  * @param allocations - Include DeFi/CeFi allocation breakdowns
114
114
  * @param options - RPC and service configuration
115
+ * @param tokenizedVault - Optional pre-fetched backend row for `vault`. When a
116
+ * caller already holds the row (e.g. `getVaults` fetched the whole list one
117
+ * call earlier), passing it skips this function's own
118
+ * `GET /tokenized_vault/{address}` — a pure de-duplication of backend
119
+ * traffic. The row must have been fetched with the same
120
+ * `loadSubaccounts`/`loadSnapshots` flags the caller passes here.
115
121
  * @returns Complete vault object with optional enrichments
116
122
  */
117
- async function getVault({ vault, loans = false, allocations = false, options, loadSubaccounts, loadSnapshots, }) {
123
+ async function getVault({ vault, loans = false, allocations = false, options, loadSubaccounts, loadSnapshots, tokenizedVault: prefetchedRow, }) {
118
124
  let returnedVault;
119
125
  try {
120
- const tokenizedVaultRaw = await (0, core_1.fetchTokenizedVault)(vault, undefined, loadSubaccounts, loadSnapshots);
121
- const tokenizedVault = tokenizedVaultRaw?.[0];
126
+ const tokenizedVault = prefetchedRow ??
127
+ (await (0, core_1.fetchTokenizedVault)(vault, undefined, loadSubaccounts, loadSnapshots))?.[0];
122
128
  const vaultVersion = (0, core_1.getVaultVersionV2)(tokenizedVault);
123
129
  switch (vaultVersion) {
124
130
  case 'sol-0': {
@@ -13,8 +13,8 @@ import { AugustBase, type IAugustBase } from '../../core';
13
13
  import SuiAdapter from '../../adapters/sui';
14
14
  import { type IContractWriteOptions, type INativeDepositOptions } from './write.actions';
15
15
  import type { Signer, Wallet } from 'ethers';
16
- import type { IVaultBaseOptions, IVaultCustomOptions } from './types';
17
- export type { IVaultBaseOptions, IVaultCustomOptions } from './types';
16
+ import type { IGetVaultsOptions, IVaultBaseOptions, IVaultCustomOptions } from './types';
17
+ export type { IGetVaultsOptions, IVaultBaseOptions, IVaultCustomOptions, } from './types';
18
18
  /**
19
19
  * Vault operations class handling multi-chain vault queries and user positions.
20
20
  * Supports both EVM and Solana vaults with unified interface.
@@ -64,32 +64,7 @@ export declare class AugustVaults extends AugustBase {
64
64
  * @param options Filtering and enrichment configuration
65
65
  * @returns Array of vault objects with optional loans/allocations/positions
66
66
  */
67
- getVaults(options?: {
68
- chainIds?: number[];
69
- loadSubaccounts?: boolean;
70
- loadSnapshots?: boolean;
71
- /**
72
- * Portfolio mode: include closed vaults in the result.
73
- *
74
- * By default (`false`) closed vaults are excluded, so marketplace /
75
- * discovery callers never receive a `status: 'closed'` vault. When set,
76
- * closed vaults are returned regardless of `is_visible` (closed +
77
- * invisible vaults bucket as closed), so a consumer joining user
78
- * positions can render a position held in a closed vault on the
79
- * portfolio page.
80
- *
81
- * In this mode, loans/allocations enrichment is also skipped for closed
82
- * vaults: they have none, and the per-vault enrichment
83
- * (`getVault` → `getVaultAllocations`) otherwise re-throws when a closed
84
- * vault has no live strategy/debank data or no subaccounts, which would
85
- * land the vault in the `failed` bucket and silently drop it before it
86
- * reaches the filter. Skipping enrichment lets the vault survive on its
87
- * backend metadata + base on-chain read.
88
- *
89
- * @default false
90
- */
91
- includeClosed?: boolean;
92
- } & IVaultCustomOptions): Promise<import("../../types").IVault[]>;
67
+ getVaults(options?: IGetVaultsOptions): Promise<import("../../types").IVault[]>;
93
68
  /**
94
69
  * Calculate total deposited across all tokenized vaults by summing latest_reported_tvl.
95
70
  * Uses the /tokenized_vault endpoint which returns latest_reported_tvl in USD.
@@ -53,6 +53,7 @@ __exportStar(require("./getters"), exports);
53
53
  * AugustVaults class
54
54
  */
55
55
  const fetcher_1 = require("./fetcher");
56
+ const web3_1 = require("../../core/constants/web3");
56
57
  const ethers_1 = require("ethers");
57
58
  const getters_1 = require("./getters");
58
59
  const vaults_1 = require("../../services/subgraph/vaults");
@@ -152,22 +153,54 @@ class AugustVaults extends core_1.AugustBase {
152
153
  const vaultsPerChainId = options?.chainIds
153
154
  ? vaultsPerAvailableProviders.filter((v) => options.chainIds.includes(v.chain))
154
155
  : vaultsPerAvailableProviders;
155
- // Fetch and transform (filter for active status)
156
- const emberVaults = await this.suiService.getEmberVaults({
157
- status: 'active',
156
+ // Skip vaults the post-enrichment filter (filterVaultsIntelligently +
157
+ // the includeClosed gate below) is guaranteed to drop, BEFORE paying
158
+ // their per-vault enrichment. `status` / `is_visible` land on the
159
+ // enriched vault verbatim from this backend row, so filtering on the row
160
+ // is exactly equivalent to filtering on the enriched result — it just
161
+ // saves the on-chain reads (and their retry backoff) for vaults that can
162
+ // never appear in the output. The post-filter below is kept unchanged as
163
+ // the authority on the final shape.
164
+ const enrichableVaults = vaultsPerChainId.filter((v) => {
165
+ const status = v?.status || 'unknown';
166
+ if (status === 'closed')
167
+ return !!options?.includeClosed;
168
+ if (status === 'active')
169
+ return true;
170
+ // Mirrors filterVaultsIntelligently: an unknown-status vault survives
171
+ // only through the invisible bucket; a visible one lands in `failed`.
172
+ return !(v?.is_visible ?? true);
158
173
  });
159
- const activeEmberVaults = Array.isArray(emberVaults)
160
- ? emberVaults.filter((v) => v?.status === 'active')
161
- : [];
162
- const whitelistedEmberVaults = activeEmberVaults.filter((v) => {
163
- if (!v?.address)
164
- return false;
165
- const normalizedAddress = v.address.toLowerCase().trim();
166
- return constants_1.ALLOWED_SUI_VAULT_ADDRESSES.includes(normalizedAddress);
167
- });
168
- const transformedVaults = this.suiService.transformEmberVaultsToIVaults(whitelistedEmberVaults);
174
+ // The backend row for each vault about to be enriched. getVault re-uses
175
+ // it instead of re-fetching `GET /tokenized_vault/{address}` per vault —
176
+ // the row came from the same list response (same load flags), so this is
177
+ // a pure de-duplication of backend traffic, not a freshness change.
178
+ const vaultRowsByAddress = new Map(enrichableVaults.map((row) => [String(row.address).toLowerCase(), row]));
179
+ // Ember (Sui) vaults come from a third-party API and are appended to the
180
+ // non-wallet result. When the caller scopes the query with `chainIds` and
181
+ // that scope excludes Sui, skip the fetch entirely — previously it was
182
+ // awaited unconditionally, so a slow Bluefin endpoint stalled even
183
+ // EVM-only and Solana/Stellar-only queries.
184
+ const includeSuiVaults = !options?.chainIds || options.chainIds.includes(web3_1.SUI_CHAIN_ID);
185
+ let transformedVaults = [];
186
+ if (includeSuiVaults) {
187
+ // Fetch and transform (filter for active status)
188
+ const emberVaults = await this.suiService.getEmberVaults({
189
+ status: 'active',
190
+ });
191
+ const activeEmberVaults = Array.isArray(emberVaults)
192
+ ? emberVaults.filter((v) => v?.status === 'active')
193
+ : [];
194
+ const whitelistedEmberVaults = activeEmberVaults.filter((v) => {
195
+ if (!v?.address)
196
+ return false;
197
+ const normalizedAddress = v.address.toLowerCase().trim();
198
+ return constants_1.ALLOWED_SUI_VAULT_ADDRESSES.includes(normalizedAddress);
199
+ });
200
+ transformedVaults = this.suiService.transformEmberVaultsToIVaults(whitelistedEmberVaults);
201
+ }
169
202
  // Use comprehensive vault fetching for maximum coverage
170
- const vaultFetchResult = await (0, fetcher_1.fetchVaultsComprehensive)(vaultsPerChainId, async (vault) => {
203
+ const vaultFetchResult = await (0, fetcher_1.fetchVaultsComprehensive)(enrichableVaults, async (vault) => {
171
204
  // Handle fallback RPC if provided
172
205
  const rpcUrl = vault.fallbackRpc || this.providers?.[vault.chain];
173
206
  // Closed vaults have no live loans/allocations. In portfolio mode,
@@ -188,6 +221,9 @@ class AugustVaults extends core_1.AugustBase {
188
221
  vault: vault.address,
189
222
  loans: shouldFetchLoans,
190
223
  allocations: shouldFetchAllocations,
224
+ // Re-use the backend row from the list response instead of letting
225
+ // getVault re-fetch it per vault (an N+1 against the backend).
226
+ tokenizedVault: vaultRowsByAddress.get(String(vault.address).toLowerCase()),
191
227
  options: {
192
228
  rpcUrl,
193
229
  solanaService: this.solanaService,
@@ -204,8 +240,8 @@ class AugustVaults extends core_1.AugustBase {
204
240
  });
205
241
  return v;
206
242
  }, {
207
- maxRetries: 5,
208
- baseDelay: 2000,
243
+ maxRetries: options?.maxRetries ?? 5,
244
+ baseDelay: options?.baseDelay ?? 2000,
209
245
  batchSize: 15,
210
246
  parallelLimit: 8,
211
247
  includeClosed: true,
@@ -257,8 +293,8 @@ class AugustVaults extends core_1.AugustBase {
257
293
  ...(options?.includeClosed ? filteredResults.closed : []),
258
294
  ...filteredResults.invisible,
259
295
  ];
260
- if ((options.wallet && (0, ethers_1.isAddress)(options.wallet)) ||
261
- (options.solanaWallet &&
296
+ if ((options?.wallet && (0, ethers_1.isAddress)(options.wallet)) ||
297
+ (options?.solanaWallet &&
262
298
  utils_1.SolanaUtils.isSolanaAddress(options.solanaWallet))) {
263
299
  // Batch the per-vault balanceOf/lagDuration reads (Multicall3, grouped
264
300
  // by chain) before fanning out — vaults the batch couldn't serve fall
@@ -294,7 +330,7 @@ class AugustVaults extends core_1.AugustBase {
294
330
  position: positions?.find((pos) => pos.vault?.toLowerCase() === r.address?.toLowerCase()) || null,
295
331
  }));
296
332
  }
297
- if (options.wallet && !(0, ethers_1.isAddress)(options.wallet)) {
333
+ if (options?.wallet && !(0, ethers_1.isAddress)(options.wallet)) {
298
334
  core_1.Logger.log.warn('getVaults:invalid_wallet', options.wallet);
299
335
  }
300
336
  // console.log(`#getVaults:`, filteredResponses);
@@ -69,3 +69,55 @@ export interface IVaultCustomOptions {
69
69
  solanaWallet?: string;
70
70
  stellarWallet?: string;
71
71
  }
72
+ /**
73
+ * Options accepted by `getVaults` — on both the `AugustSDK` facade and the
74
+ * underlying `AugustVaults` module. Defined once so the two signatures cannot
75
+ * drift (the facade previously declared a narrower inline type that rejected
76
+ * documented options like `includeClosed` at compile time).
77
+ */
78
+ export interface IGetVaultsOptions extends IVaultCustomOptions {
79
+ chainIds?: number[];
80
+ headers?: IWSMonitorHeaders;
81
+ loadSubaccounts?: boolean;
82
+ loadSnapshots?: boolean;
83
+ /**
84
+ * Portfolio mode: include closed vaults in the result.
85
+ *
86
+ * By default (`false`) closed vaults are excluded, so marketplace /
87
+ * discovery callers never receive a `status: 'closed'` vault. When set,
88
+ * closed vaults are returned regardless of `is_visible` (closed +
89
+ * invisible vaults bucket as closed), so a consumer joining user
90
+ * positions can render a position held in a closed vault on the
91
+ * portfolio page.
92
+ *
93
+ * In this mode, loans/allocations enrichment is also skipped for closed
94
+ * vaults: they have none, and the per-vault enrichment
95
+ * (`getVault` → `getVaultAllocations`) otherwise re-throws when a closed
96
+ * vault has no live strategy/debank data or no subaccounts, which would
97
+ * land the vault in the `failed` bucket and silently drop it before it
98
+ * reaches the filter. Skipping enrichment lets the vault survive on its
99
+ * backend metadata + base on-chain read.
100
+ *
101
+ * @default false
102
+ */
103
+ includeClosed?: boolean;
104
+ /**
105
+ * Maximum primary-fetch attempts per vault before the fallback
106
+ * strategies (fallback RPCs, minimal fetch, extended retry) run.
107
+ * Attempt `n` waits `baseDelay * 2^(n-1)` ms before retrying, so the
108
+ * default (5 attempts, 2000 ms base) can spend up to 30s of backoff on
109
+ * a single persistently-failing vault. Callers racing this method
110
+ * against their own timeout should lower it (e.g. `2`) so one flaky
111
+ * vault cannot exhaust the whole budget.
112
+ *
113
+ * @default 5
114
+ */
115
+ maxRetries?: number;
116
+ /**
117
+ * Base backoff delay in **milliseconds** for the per-vault retry
118
+ * schedule (see `maxRetries`).
119
+ *
120
+ * @default 2000
121
+ */
122
+ baseDelay?: number;
123
+ }
@@ -1,4 +1,4 @@
1
- import type { IAddress, IContractRunner, INormalizedNumber, IPoolFunctions, ITokenizedVault, IVault, IVaultFreshness } from '../../types';
1
+ import type { IAddress, IContractRunner, INormalizedNumber, IPoolFunctions, ITokenizedVault, IVault, IVaultFreshness, IVaultVersion } from '../../types';
2
2
  import { ethers } from 'ethers';
3
3
  import type { WalletClient } from 'viem';
4
4
  /**
@@ -50,9 +50,14 @@ export declare function getVaultRewards(tokenizedVault: ITokenizedVault): {
50
50
  * @param provider - The provider
51
51
  * @param vaultAddress - The vault address
52
52
  * @param totalAssets - The total assets of the vault
53
+ * @param knownVersion - Optional pre-resolved vault version. Callers that
54
+ * already hold the backend row (e.g. `buildFormattedVault`) pass it so this
55
+ * function doesn't re-fetch `GET /tokenized_vault/{address}` — and with the
56
+ * API's default load flags at that, a heavier payload on a different cache
57
+ * key than the caller's own fetch — just to dispatch on the version.
53
58
  * @returns The idle assets
54
59
  */
55
- export declare function getIdleAssets(provider: IContractRunner, vaultAddress: IAddress, underlying: IAddress, totalAssets: INormalizedNumber): Promise<bigint>;
60
+ export declare function getIdleAssets(provider: IContractRunner, vaultAddress: IAddress, underlying: IAddress, totalAssets: INormalizedNumber, knownVersion?: IVaultVersion): Promise<bigint>;
56
61
  export declare function getYieldLastRealizedOn(provider: IContractRunner, vaultAddress: IAddress): Promise<number>;
57
62
  /**
58
63
  * Format APY data from backend tokenized vault into the unified IVaultApy shape.
@@ -133,11 +133,16 @@ function getVaultRewards(tokenizedVault) {
133
133
  * @param provider - The provider
134
134
  * @param vaultAddress - The vault address
135
135
  * @param totalAssets - The total assets of the vault
136
+ * @param knownVersion - Optional pre-resolved vault version. Callers that
137
+ * already hold the backend row (e.g. `buildFormattedVault`) pass it so this
138
+ * function doesn't re-fetch `GET /tokenized_vault/{address}` — and with the
139
+ * API's default load flags at that, a heavier payload on a different cache
140
+ * key than the caller's own fetch — just to dispatch on the version.
136
141
  * @returns The idle assets
137
142
  */
138
- async function getIdleAssets(provider, vaultAddress, underlying, totalAssets) {
139
- const tokenizedVault = (await (0, core_1.fetchTokenizedVault)(vaultAddress))?.[0];
140
- const version = (0, vaults_1.getVaultVersionV2)(tokenizedVault);
143
+ async function getIdleAssets(provider, vaultAddress, underlying, totalAssets, knownVersion) {
144
+ const version = knownVersion ??
145
+ (0, vaults_1.getVaultVersionV2)((await (0, core_1.fetchTokenizedVault)(vaultAddress))?.[0]);
141
146
  let idleAssets;
142
147
  switch (version) {
143
148
  case 'evm-0': {
@@ -522,7 +527,10 @@ async function buildFormattedVault(provider, tokenizedVault, contractCalls) {
522
527
  isDepositPaused: (0, vaults_1.isBadVault)(tokenizedVault.address),
523
528
  decimals: contractCalls.decimals,
524
529
  isWithdrawalPaused: contractCalls.withdrawalsPaused,
525
- idleAssets: (0, core_1.toNormalizedBn)(await getIdleAssets(provider, tokenizedVault.address, contractCalls.asset, contractCalls.totalAssets), Number(contractCalls.decimals)),
530
+ idleAssets: (0, core_1.toNormalizedBn)(await getIdleAssets(provider, tokenizedVault.address, contractCalls.asset, contractCalls.totalAssets,
531
+ // The row is already in hand — don't let getIdleAssets re-fetch it
532
+ // just to resolve the version.
533
+ (0, vaults_1.getVaultVersionV2)(tokenizedVault)), Number(contractCalls.decimals)),
526
534
  };
527
535
  // Version specific logic
528
536
  const version = (0, vaults_1.getVaultVersionV2)(tokenizedVault);
package/lib/sdk.d.ts CHANGED
@@ -15998,15 +15998,12 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
15998
15998
  /**
15999
15999
  * Fetch all available vaults across configured networks.
16000
16000
  * Optionally filter by chain IDs and include loan/allocation data.
16001
- * @param options - Configuration for filtering and enriching vault data
16001
+ * @param options - Configuration for filtering and enriching vault data
16002
+ * see {@link IGetVaultsOptions} for the full surface (`includeClosed`
16003
+ * portfolio mode, `maxRetries`/`baseDelay` retry tuning)
16002
16004
  * @returns Array of vault objects with metadata and optional position data
16003
16005
  */
16004
- getVaults(options?: {
16005
- chainIds?: number[];
16006
- headers?: IWSMonitorHeaders;
16007
- loadSubaccounts?: boolean;
16008
- loadSnapshots?: boolean;
16009
- } & IVaultCustomOptions): Promise<IVault[]>;
16006
+ getVaults(options?: IGetVaultsOptions): Promise<IVault[]>;
16010
16007
  getTotalDeposited(options?: {
16011
16008
  loadSubaccounts?: boolean;
16012
16009
  loadSnapshots?: boolean;
@@ -16793,32 +16790,7 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
16793
16790
  * @param options Filtering and enrichment configuration
16794
16791
  * @returns Array of vault objects with optional loans/allocations/positions
16795
16792
  */
16796
- getVaults(options?: {
16797
- chainIds?: number[];
16798
- loadSubaccounts?: boolean;
16799
- loadSnapshots?: boolean;
16800
- /**
16801
- * Portfolio mode: include closed vaults in the result.
16802
- *
16803
- * By default (`false`) closed vaults are excluded, so marketplace /
16804
- * discovery callers never receive a `status: 'closed'` vault. When set,
16805
- * closed vaults are returned regardless of `is_visible` (closed +
16806
- * invisible vaults bucket as closed), so a consumer joining user
16807
- * positions can render a position held in a closed vault on the
16808
- * portfolio page.
16809
- *
16810
- * In this mode, loans/allocations enrichment is also skipped for closed
16811
- * vaults: they have none, and the per-vault enrichment
16812
- * (`getVault` → `getVaultAllocations`) otherwise re-throws when a closed
16813
- * vault has no live strategy/debank data or no subaccounts, which would
16814
- * land the vault in the `failed` bucket and silently drop it before it
16815
- * reaches the filter. Skipping enrichment lets the vault survive on its
16816
- * backend metadata + base on-chain read.
16817
- *
16818
- * @default false
16819
- */
16820
- includeClosed?: boolean;
16821
- } & IVaultCustomOptions): Promise<IVault[]>;
16793
+ getVaults(options?: IGetVaultsOptions): Promise<IVault[]>;
16822
16794
  /**
16823
16795
  * Calculate total deposited across all tokenized vaults by summing latest_reported_tvl.
16824
16796
  * Uses the /tokenized_vault endpoint which returns latest_reported_tvl in USD.
@@ -18830,9 +18802,14 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
18830
18802
  * @param provider - The provider
18831
18803
  * @param vaultAddress - The vault address
18832
18804
  * @param totalAssets - The total assets of the vault
18805
+ * @param knownVersion - Optional pre-resolved vault version. Callers that
18806
+ * already hold the backend row (e.g. `buildFormattedVault`) pass it so this
18807
+ * function doesn't re-fetch `GET /tokenized_vault/{address}` — and with the
18808
+ * API's default load flags at that, a heavier payload on a different cache
18809
+ * key than the caller's own fetch — just to dispatch on the version.
18833
18810
  * @returns The idle assets
18834
18811
  */
18835
- export declare function getIdleAssets(provider: IContractRunner, vaultAddress: IAddress, underlying: IAddress, totalAssets: INormalizedNumber): Promise<bigint>;
18812
+ export declare function getIdleAssets(provider: IContractRunner, vaultAddress: IAddress, underlying: IAddress, totalAssets: INormalizedNumber, knownVersion?: IVaultVersion): Promise<bigint>;
18836
18813
 
18837
18814
  /**
18838
18815
  * Create or reuse a cached Infura provider for the specified chain.
@@ -19348,15 +19325,22 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
19348
19325
  * @param loans - Include active loan data
19349
19326
  * @param allocations - Include DeFi/CeFi allocation breakdowns
19350
19327
  * @param options - RPC and service configuration
19328
+ * @param tokenizedVault - Optional pre-fetched backend row for `vault`. When a
19329
+ * caller already holds the row (e.g. `getVaults` fetched the whole list one
19330
+ * call earlier), passing it skips this function's own
19331
+ * `GET /tokenized_vault/{address}` — a pure de-duplication of backend
19332
+ * traffic. The row must have been fetched with the same
19333
+ * `loadSubaccounts`/`loadSnapshots` flags the caller passes here.
19351
19334
  * @returns Complete vault object with optional enrichments
19352
19335
  */
19353
- export declare function getVault({ vault, loans, allocations, options, loadSubaccounts, loadSnapshots, }: {
19336
+ export declare function getVault({ vault, loans, allocations, options, loadSubaccounts, loadSnapshots, tokenizedVault: prefetchedRow, }: {
19354
19337
  vault: IAddress;
19355
19338
  loans?: boolean;
19356
19339
  allocations?: boolean;
19357
19340
  options: IVaultBaseOptions;
19358
19341
  loadSubaccounts?: boolean;
19359
19342
  loadSnapshots?: boolean;
19343
+ tokenizedVault?: ITokenizedVault;
19360
19344
  }): Promise<IVault>;
19361
19345
 
19362
19346
  /**
@@ -20756,6 +20740,59 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
20756
20740
  status?: string;
20757
20741
  }
20758
20742
 
20743
+ /**
20744
+ * Options accepted by `getVaults` — on both the `AugustSDK` facade and the
20745
+ * underlying `AugustVaults` module. Defined once so the two signatures cannot
20746
+ * drift (the facade previously declared a narrower inline type that rejected
20747
+ * documented options like `includeClosed` at compile time).
20748
+ */
20749
+ export declare interface IGetVaultsOptions extends IVaultCustomOptions {
20750
+ chainIds?: number[];
20751
+ headers?: IWSMonitorHeaders;
20752
+ loadSubaccounts?: boolean;
20753
+ loadSnapshots?: boolean;
20754
+ /**
20755
+ * Portfolio mode: include closed vaults in the result.
20756
+ *
20757
+ * By default (`false`) closed vaults are excluded, so marketplace /
20758
+ * discovery callers never receive a `status: 'closed'` vault. When set,
20759
+ * closed vaults are returned regardless of `is_visible` (closed +
20760
+ * invisible vaults bucket as closed), so a consumer joining user
20761
+ * positions can render a position held in a closed vault on the
20762
+ * portfolio page.
20763
+ *
20764
+ * In this mode, loans/allocations enrichment is also skipped for closed
20765
+ * vaults: they have none, and the per-vault enrichment
20766
+ * (`getVault` → `getVaultAllocations`) otherwise re-throws when a closed
20767
+ * vault has no live strategy/debank data or no subaccounts, which would
20768
+ * land the vault in the `failed` bucket and silently drop it before it
20769
+ * reaches the filter. Skipping enrichment lets the vault survive on its
20770
+ * backend metadata + base on-chain read.
20771
+ *
20772
+ * @default false
20773
+ */
20774
+ includeClosed?: boolean;
20775
+ /**
20776
+ * Maximum primary-fetch attempts per vault before the fallback
20777
+ * strategies (fallback RPCs, minimal fetch, extended retry) run.
20778
+ * Attempt `n` waits `baseDelay * 2^(n-1)` ms before retrying, so the
20779
+ * default (5 attempts, 2000 ms base) can spend up to 30s of backoff on
20780
+ * a single persistently-failing vault. Callers racing this method
20781
+ * against their own timeout should lower it (e.g. `2`) so one flaky
20782
+ * vault cannot exhaust the whole budget.
20783
+ *
20784
+ * @default 5
20785
+ */
20786
+ maxRetries?: number;
20787
+ /**
20788
+ * Base backoff delay in **milliseconds** for the per-vault retry
20789
+ * schedule (see `maxRetries`).
20790
+ *
20791
+ * @default 2000
20792
+ */
20793
+ baseDelay?: number;
20794
+ }
20795
+
20759
20796
  /**
20760
20797
  * Table of Contents
20761
20798
  * 1) Subaccounts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@augustdigital/sdk",
3
- "version": "8.25.0",
3
+ "version": "8.26.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/sdk.d.ts",
6
6
  "keywords": [