@augustdigital/sdk 8.11.0 → 8.13.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.
@@ -16,7 +16,21 @@ function getAddressChainType(address) {
16
16
  return 'sui';
17
17
  return 'evm';
18
18
  }
19
- // @todo: move to backend
19
+ /**
20
+ * Classify a vault's protocol version from its address alone.
21
+ *
22
+ * @deprecated Use {@link getVaultVersionV2} instead. This V1 classifier can only
23
+ * recognise a multi-asset vault (`evm-2`) if its address is hard-coded in the
24
+ * static `MULTI_ASSET_VAULTS` list. Any multi-asset vault missing from that list
25
+ * is silently misclassified as `evm-1`, which broke deposit preview/simulation in
26
+ * the 2026-07-06 Sentora incident. `getVaultVersionV2` reads the backend
27
+ * `internal_type` (falling back to the static list only as a safety net), so new
28
+ * vaults classify correctly without a code change. Retained as a non-breaking
29
+ * shim; scheduled for removal in the next major.
30
+ *
31
+ * @param vault - The vault contract address (EVM `0x…`, Solana, or Stellar).
32
+ * @returns The vault version slug, or `undefined` for empty/invalid input.
33
+ */
20
34
  function getVaultVersion(vault) {
21
35
  if (!vault)
22
36
  return;
@@ -8,7 +8,7 @@ export declare const isBadVault: (address?: string) => boolean;
8
8
  import { getAddressChainType, getVaultVersion, getVaultVersionV2 } from './vault-version';
9
9
  export { getAddressChainType, getVaultVersion, getVaultVersionV2 };
10
10
  export declare const REWARD_DISTRIBUTOR_ADDRESS: (chainId: number) => string[];
11
- export declare const AVAX_PRICE_FEED_ADDRESS: (chainId: number) => "0xFF3EEb22B5E3dE6e705b44749C2559d704923FD7" | "0x";
11
+ export declare const AVAX_PRICE_FEED_ADDRESS: (chainId: number) => "0x" | "0xFF3EEb22B5E3dE6e705b44749C2559d704923FD7";
12
12
  export declare function getVaultSymbol(vault: IAddress, provider: IContractRunner): Promise<string | undefined>;
13
13
  /**
14
14
  * @deprecated
@@ -11,9 +11,11 @@ export * from './constants/web3';
11
11
  export * from './constants/vaults';
12
12
  export * from './constants/swap-router';
13
13
  export * from './helpers/web3';
14
+ export * from './helpers/multicall';
14
15
  export * from './helpers/vaults';
15
16
  export * from './helpers/chain-error';
16
17
  export * from './helpers/core';
17
18
  export * from './helpers/adapters';
18
19
  export * from './helpers/signer';
19
20
  export * from './helpers/swap-router';
21
+ export * from './helpers/revert-decode';
package/lib/core/index.js CHANGED
@@ -27,10 +27,12 @@ __exportStar(require("./constants/web3"), exports);
27
27
  __exportStar(require("./constants/vaults"), exports);
28
28
  __exportStar(require("./constants/swap-router"), exports);
29
29
  __exportStar(require("./helpers/web3"), exports);
30
+ __exportStar(require("./helpers/multicall"), exports);
30
31
  __exportStar(require("./helpers/vaults"), exports);
31
32
  __exportStar(require("./helpers/chain-error"), exports);
32
33
  __exportStar(require("./helpers/core"), exports);
33
34
  __exportStar(require("./helpers/adapters"), exports);
34
35
  __exportStar(require("./helpers/signer"), exports);
35
36
  __exportStar(require("./helpers/swap-router"), exports);
37
+ __exportStar(require("./helpers/revert-decode"), exports);
36
38
  //# sourceMappingURL=index.js.map
package/lib/main.d.ts CHANGED
@@ -279,6 +279,52 @@ export declare class AugustSDK extends AugustBase {
279
279
  untilTs?: number;
280
280
  types?: ('withdraw-request' | 'deposit' | 'withdraw-processed' | 'redeem')[];
281
281
  }): Promise<import("./types").IVaultUserHistoryItem[]>;
282
+ /**
283
+ * Resolve the ERC-20 tokens currently whitelisted for the `SwapRouter` on a
284
+ * chain — the any-token set a deposit UI can offer as swap-and-deposit inputs.
285
+ *
286
+ * Delegates to {@link AugustVaults.getSwapRouterWhitelistedTokens}, which
287
+ * reconstructs the set from the router's `TokenEnabled` events and verifies
288
+ * each against the on-chain `whitelistedTokens` mapping (not enumerable).
289
+ * Returns `[]` when no router is deployed on `chainId` or no provider is
290
+ * configured for it.
291
+ *
292
+ * @param chainId - EVM chain ID to resolve the allowlist for.
293
+ * @param options - Optional `fromBlock` to override the deploy-block scan floor.
294
+ * @returns Checksummed whitelisted token addresses; `[]` when unavailable.
295
+ * @throws AugustSDKError when an `eth_getLogs` scan chunk fails.
296
+ * @example
297
+ * ```ts
298
+ * const tokens = await augustSdk.getSwapRouterWhitelistedTokens(1);
299
+ * ```
300
+ */
301
+ getSwapRouterWhitelistedTokens(chainId: number, options?: {
302
+ fromBlock?: number;
303
+ }): Promise<IAddress[]>;
304
+ /**
305
+ * Resolve the vaults currently eligible for the any-token `SwapRouter`
306
+ * deposit surface on a chain — every vault the router has `enableVault`-ed
307
+ * whose on-chain registration is still live.
308
+ *
309
+ * Delegates to {@link AugustVaults.getSwapRouterEligibleVaults}, which
310
+ * reconstructs the set from the router's `VaultEnabled` events and verifies
311
+ * each vault's current `vaultInfo` registration (not enumerable). Returns
312
+ * `[]` when no router is deployed on `chainId` or no provider is configured
313
+ * for it. On-chain eligibility only — app-level policy (e.g. OVault
314
+ * exclusions) remains the caller's responsibility.
315
+ *
316
+ * @param chainId - EVM chain ID to resolve eligible vaults for.
317
+ * @param options - Optional `fromBlock` to override the deploy-block scan floor.
318
+ * @returns Checksummed eligible vault addresses; `[]` when unavailable.
319
+ * @throws AugustSDKError when an `eth_getLogs` scan chunk fails.
320
+ * @example
321
+ * ```ts
322
+ * const vaults = await augustSdk.getSwapRouterEligibleVaults(1);
323
+ * ```
324
+ */
325
+ getSwapRouterEligibleVaults(chainId: number, options?: {
326
+ fromBlock?: number;
327
+ }): Promise<IAddress[]>;
282
328
  /**
283
329
  * Get lifetime PnL for a user in a specific vault.
284
330
  * Calculates realized and unrealized PnL based on deposit/withdrawal history and current position.
package/lib/main.js CHANGED
@@ -343,6 +343,52 @@ class AugustSDK extends core_1.AugustBase {
343
343
  async getVaultActivity(props) {
344
344
  return await this.vaults.getVaultActivity(props);
345
345
  }
346
+ /**
347
+ * Resolve the ERC-20 tokens currently whitelisted for the `SwapRouter` on a
348
+ * chain — the any-token set a deposit UI can offer as swap-and-deposit inputs.
349
+ *
350
+ * Delegates to {@link AugustVaults.getSwapRouterWhitelistedTokens}, which
351
+ * reconstructs the set from the router's `TokenEnabled` events and verifies
352
+ * each against the on-chain `whitelistedTokens` mapping (not enumerable).
353
+ * Returns `[]` when no router is deployed on `chainId` or no provider is
354
+ * configured for it.
355
+ *
356
+ * @param chainId - EVM chain ID to resolve the allowlist for.
357
+ * @param options - Optional `fromBlock` to override the deploy-block scan floor.
358
+ * @returns Checksummed whitelisted token addresses; `[]` when unavailable.
359
+ * @throws AugustSDKError when an `eth_getLogs` scan chunk fails.
360
+ * @example
361
+ * ```ts
362
+ * const tokens = await augustSdk.getSwapRouterWhitelistedTokens(1);
363
+ * ```
364
+ */
365
+ async getSwapRouterWhitelistedTokens(chainId, options) {
366
+ return await this.vaults.getSwapRouterWhitelistedTokens(chainId, options);
367
+ }
368
+ /**
369
+ * Resolve the vaults currently eligible for the any-token `SwapRouter`
370
+ * deposit surface on a chain — every vault the router has `enableVault`-ed
371
+ * whose on-chain registration is still live.
372
+ *
373
+ * Delegates to {@link AugustVaults.getSwapRouterEligibleVaults}, which
374
+ * reconstructs the set from the router's `VaultEnabled` events and verifies
375
+ * each vault's current `vaultInfo` registration (not enumerable). Returns
376
+ * `[]` when no router is deployed on `chainId` or no provider is configured
377
+ * for it. On-chain eligibility only — app-level policy (e.g. OVault
378
+ * exclusions) remains the caller's responsibility.
379
+ *
380
+ * @param chainId - EVM chain ID to resolve eligible vaults for.
381
+ * @param options - Optional `fromBlock` to override the deploy-block scan floor.
382
+ * @returns Checksummed eligible vault addresses; `[]` when unavailable.
383
+ * @throws AugustSDKError when an `eth_getLogs` scan chunk fails.
384
+ * @example
385
+ * ```ts
386
+ * const vaults = await augustSdk.getSwapRouterEligibleVaults(1);
387
+ * ```
388
+ */
389
+ async getSwapRouterEligibleVaults(chainId, options) {
390
+ return await this.vaults.getSwapRouterEligibleVaults(chainId, options);
391
+ }
346
392
  /**
347
393
  * Get lifetime PnL for a user in a specific vault.
348
394
  * Calculates realized and unrealized PnL based on deposit/withdrawal history and current position.
@@ -53,12 +53,22 @@ export declare function getVaultSubaccountLoans(vault: VaultAddress | IVault, op
53
53
  * @returns Detailed allocation data with exposure categorization
54
54
  */
55
55
  export declare function getVaultAllocations(vault: IAddress, options: IVaultBaseOptions): Promise<IVaultAllocations>;
56
- export declare function getVaultAvailableRedemptions({ vault, wallet, options, }: {
56
+ export declare function getVaultAvailableRedemptions({ vault, wallet, options, prefetchedReads, }: {
57
57
  vault: IAddress;
58
58
  wallet?: IAddress;
59
59
  options: IVaultBaseOptions & {
60
60
  verbose?: boolean;
61
61
  };
62
+ /**
63
+ * Values already read via a Multicall3 batch (see
64
+ * `modules/vaults/prefetch.ts`). When `lagDuration` is present the
65
+ * per-vault `lagDuration()` RPC is skipped; when omitted, behavior is
66
+ * exactly the pre-batching per-call path.
67
+ * @internal
68
+ */
69
+ prefetchedReads?: {
70
+ lagDuration?: number;
71
+ };
62
72
  }): Promise<{
63
73
  processedWithdrawals: ISubgraphWithdrawProccessed[];
64
74
  requestedWithdrawals: import("../../types").ISubgraphWithdrawRequest[];
@@ -71,15 +81,28 @@ export declare function getVaultRedemptionHistory({ vault, wallet, lookbackBlock
71
81
  lookbackBlocks?: number;
72
82
  options: IVaultBaseOptions;
73
83
  }): Promise<IVaultRedemptionHistoryItem[]>;
84
+ export { getSwapRouterDepositResult } from '../../core/helpers/swap-router';
74
85
  /**
75
86
  * Vault Positions
76
87
  */
77
- export declare function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, options, }: {
88
+ export declare function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, options, prefetchedReads, }: {
78
89
  vault: IAddress;
79
90
  wallet?: IAddress;
80
91
  solanaWallet?: string;
81
92
  stellarWallet?: string;
82
93
  options: IVaultBaseOptions;
94
+ /**
95
+ * Per-vault values already read via a Multicall3 batch at the fan-out call
96
+ * sites (see `modules/vaults/prefetch.ts`). Supplied fields skip the
97
+ * corresponding `balanceOf` / `lagDuration` RPC for the vault matching the
98
+ * `vault` parameter; when omitted, behavior is exactly the pre-batching
99
+ * per-call path.
100
+ * @internal
101
+ */
102
+ prefetchedReads?: {
103
+ balance?: bigint;
104
+ lagDuration?: number;
105
+ };
83
106
  }): Promise<IVaultPosition[]>;
84
107
  /**
85
108
  * @deprecated use getVaultHistoricalTimeseries instead
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.getSwapRouterDepositResult = void 0;
39
40
  exports.getVault = getVault;
40
41
  exports.getVaultLoans = getVaultLoans;
41
42
  exports.getVaultSubaccountLoans = getVaultSubaccountLoans;
@@ -548,7 +549,7 @@ async function getVaultAllocations(vault, options) {
548
549
  unfilteredTokens: unfilteredTokens,
549
550
  };
550
551
  }
551
- async function getVaultAvailableRedemptions({ vault, wallet, options, }) {
552
+ async function getVaultAvailableRedemptions({ vault, wallet, options, prefetchedReads, }) {
552
553
  try {
553
554
  // Stellar vaults don't support on-chain redemptions yet
554
555
  if ((0, utils_3.isStellarAddress)(vault)) {
@@ -592,8 +593,9 @@ async function getVaultAvailableRedemptions({ vault, wallet, options, }) {
592
593
  else {
593
594
  decimals = await (0, core_1.getDecimals)(provider, vault);
594
595
  }
595
- const ld = await vaultContract.lagDuration();
596
- const lagDuration = Number(ld);
596
+ const lagDuration = typeof prefetchedReads?.lagDuration === 'number'
597
+ ? prefetchedReads.lagDuration
598
+ : Number(await vaultContract.lagDuration());
597
599
  const { withdrawalRequesteds, withdrawalProcesseds } = await (0, subgraph_1.getSubgraphAllWithdrawals)(vault, provider);
598
600
  // format
599
601
  const availableRedemptions = [];
@@ -991,10 +993,15 @@ async function getVaultRedemptionHistory({ vault, wallet, lookbackBlocks, option
991
993
  throw e;
992
994
  }
993
995
  }
996
+ // Moved to core/helpers/swap-router.ts to break the adapters/evm ↔
997
+ // modules/vaults/getters import cycle (CLAUDE.md §9); re-exported here for
998
+ // back-compat with existing importers.
999
+ var swap_router_1 = require("../../core/helpers/swap-router");
1000
+ Object.defineProperty(exports, "getSwapRouterDepositResult", { enumerable: true, get: function () { return swap_router_1.getSwapRouterDepositResult; } });
994
1001
  /**
995
1002
  * Vault Positions
996
1003
  */
997
- async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, options, }) {
1004
+ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, options, prefetchedReads, }) {
998
1005
  try {
999
1006
  const tokenizedVaults = await (0, core_1.fetchTokenizedVault)(vault ? vault : undefined, options?.headers, false, false);
1000
1007
  if (!tokenizedVaults || tokenizedVaults.length === 0) {
@@ -1100,24 +1107,37 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, o
1100
1107
  abi: abis_1.ABI_LENDING_POOL_V2,
1101
1108
  address: v.address,
1102
1109
  });
1110
+ // Batched reads only apply to the vault this call was made for —
1111
+ // guard against a multi-row backend response reusing them.
1112
+ const prefetched = v.address?.toLowerCase?.() === String(vault).toLowerCase()
1113
+ ? prefetchedReads
1114
+ : undefined;
1103
1115
  // get vault metadata and balance based on version
1104
1116
  let bal = BigInt(0);
1105
1117
  if (version === 'evm-2') {
1106
1118
  const receiptAddress = await (0, core_1.getReceiptTokenAddress)(provider, vault);
1107
1119
  decimals = await (0, core_1.getDecimals)(provider, receiptAddress, false);
1108
1120
  if (wallet && (0, ethers_1.isAddress)(wallet)) {
1109
- const receiptContract = (0, core_1.createContract)({
1110
- provider,
1111
- abi: TokenizedVaultV2Receipt_1.ABI_TOKENIZED_VAULT_V2_RECEIPT,
1112
- address: receiptAddress,
1113
- });
1114
- bal = await receiptContract.balanceOf(wallet);
1121
+ if (typeof prefetched?.balance === 'bigint') {
1122
+ bal = prefetched.balance;
1123
+ }
1124
+ else {
1125
+ const receiptContract = (0, core_1.createContract)({
1126
+ provider,
1127
+ abi: TokenizedVaultV2Receipt_1.ABI_TOKENIZED_VAULT_V2_RECEIPT,
1128
+ address: receiptAddress,
1129
+ });
1130
+ bal = await receiptContract.balanceOf(wallet);
1131
+ }
1115
1132
  }
1116
1133
  }
1117
1134
  else {
1118
1135
  decimals = await (0, core_1.getDecimals)(provider, vault);
1119
1136
  if (wallet && (0, ethers_1.isAddress)(wallet)) {
1120
- bal = await vaultContract.balanceOf(wallet);
1137
+ bal =
1138
+ typeof prefetched?.balance === 'bigint'
1139
+ ? prefetched.balance
1140
+ : await vaultContract.balanceOf(wallet);
1121
1141
  }
1122
1142
  }
1123
1143
  const balance = (0, core_1.toNormalizedBn)(bal, decimals);
@@ -1126,6 +1146,7 @@ async function getVaultPositions({ vault, wallet, solanaWallet, stellarWallet, o
1126
1146
  vault: v.address,
1127
1147
  wallet,
1128
1148
  options,
1149
+ prefetchedReads: prefetched,
1129
1150
  });
1130
1151
  // get aggregate redemption amount
1131
1152
  const aggregateAvailableRedemptions = availableRedemptions.reduce((acc, curr) => acc + BigInt(curr.amount.raw), BigInt(0));
@@ -1859,6 +1880,15 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
1859
1880
  if (!tokenizedVault) {
1860
1881
  throw new Error(`Vault ${vault} not found`);
1861
1882
  }
1883
+ // getDecimals resolves to `undefined` on a failed/absent decimals() read.
1884
+ // Unguarded, that undefined flows into `10 ** decimals` (=> NaN) and
1885
+ // toNormalizedBn(x, undefined), and the first BigInt(NaN) throws the opaque
1886
+ // "The number NaN cannot be converted to a BigInt" error. Decimals must come
1887
+ // from the chain — never assume a fallback — so fail fast with actionable
1888
+ // context. Transient RPC blips recover on the caller's retry.
1889
+ if (typeof decimals !== 'number' || !Number.isFinite(decimals)) {
1890
+ throw new Error(`Failed to resolve decimals for vault ${vault}`);
1891
+ }
1862
1892
  // Fetch LayerZero deposits/redeems for supported vaults
1863
1893
  const lzVaultKey = (0, deposits_1.isLayerZeroVault)(vault);
1864
1894
  let lzDeposits = [];
@@ -50,6 +50,17 @@ export declare class AugustVaults extends AugustBase {
50
50
  * Includes closed vaults that are still visible (`status: 'closed'`,
51
51
  * `is_visible: true`) so consumers can render positions held in closed
52
52
  * vaults; closed vaults with `is_visible: false` are excluded.
53
+ *
54
+ * RPC volume (worst case, wallet enrichment path): the per-vault
55
+ * `balanceOf`/`lagDuration` reads are aggregated into chunked Multicall3
56
+ * `aggregate3` batches per chain (10 calls per chunk) on chains where the
57
+ * canonical Multicall3 deployment is verified; other chains (e.g. Citrea
58
+ * 4114) keep one `eth_call` per read. Measured with
59
+ * `benchmarks/request-counter.js` on a cold cache for a wallet with 62
60
+ * vaults across Ethereum + Avalanche: 83 RPC HTTP requests before batching,
61
+ * 54 after (~35% fewer; 124 per-vault `eth_call`s collapse into ~13
62
+ * `aggregate3` calls — the remaining requests are the per-vault cached
63
+ * decimals/receipt-token reads on a cold cache).
53
64
  * @param options Filtering and enrichment configuration
54
65
  * @returns Array of vault objects with optional loans/allocations/positions
55
66
  */
@@ -202,7 +213,18 @@ export declare class AugustVaults extends AugustBase {
202
213
  lookbackBlocks?: number;
203
214
  }): Promise<import("../../types").IVaultRedemptionHistoryItem[]>;
204
215
  /**
216
+ * Fetch the wallet's positions across vaults — a single vault, one chain,
217
+ * or every chain with a configured provider.
205
218
  *
219
+ * RPC volume (worst case, multi-vault paths): the two non-cacheable
220
+ * per-vault reads (`balanceOf`, `lagDuration`) are aggregated into chunked
221
+ * Multicall3 `aggregate3` batches per chain (10 calls per chunk) on chains
222
+ * where the canonical deployment is verified; unverified chains (e.g.
223
+ * Citrea 4114) and non-EVM vaults keep one `eth_call` per read. Measured
224
+ * with `benchmarks/request-counter.js` on a cold cache for a wallet with 62
225
+ * vaults across Ethereum + Avalanche: 83 RPC HTTP requests before batching,
226
+ * 54 after (~35% fewer). A vault whose batched read fails (e.g. paused)
227
+ * falls back to its own per-vault reads — never to a zeroed balance.
206
228
  */
207
229
  getVaultPositions({ vault, wallet, chainId, showAllVaults, solanaWallet, stellarWallet, options, }: {
208
230
  vault?: IAddress;
@@ -306,6 +328,59 @@ export declare class AugustVaults extends AugustBase {
306
328
  untilTs?: number;
307
329
  types?: IVaultUserHistoryItem['type'][];
308
330
  }): Promise<IVaultUserHistoryItem[]>;
331
+ /**
332
+ * Resolve the ERC-20 tokens currently whitelisted for the `SwapRouter` on a
333
+ * chain — the any-token set a deposit UI can offer as swap-and-deposit inputs,
334
+ * each verified `true` against the router's on-chain allowlist.
335
+ *
336
+ * Reconstructs the set from the router's `TokenEnabled` event history and
337
+ * re-checks `whitelistedTokens(token)` for each (the mapping is not
338
+ * enumerable). See {@link getSwapRouterWhitelistedTokens} for the full
339
+ * two-stage behavior and RPC cost. Uses this instance's configured provider
340
+ * for `chainId`; returns `[]` (no RPC) when no router is deployed there or no
341
+ * provider is configured.
342
+ *
343
+ * @param chainId - EVM chain ID to resolve the allowlist for.
344
+ * @param options - Optional `fromBlock` to override the deploy-block scan floor.
345
+ * @returns Checksummed whitelisted token addresses; `[]` when unavailable.
346
+ * Callers still exclude a target vault's natively-accepted assets (those
347
+ * deposit without a swap).
348
+ * @throws AugustSDKError when an `eth_getLogs` scan chunk fails.
349
+ * @example
350
+ * ```ts
351
+ * const tokens = await augustSdk.getSwapRouterWhitelistedTokens(1);
352
+ * ```
353
+ */
354
+ getSwapRouterWhitelistedTokens(chainId: number, options?: {
355
+ fromBlock?: number;
356
+ }): Promise<IAddress[]>;
357
+ /**
358
+ * Resolve the vaults currently eligible for the any-token `SwapRouter`
359
+ * deposit surface on a chain — every vault the router has `enableVault`-ed
360
+ * whose on-chain registration is still live.
361
+ *
362
+ * Reconstructs the set from the router's `VaultEnabled` event history and
363
+ * re-checks `vaultInfo(vault).referenceAsset != 0` for each (the mapping is
364
+ * not enumerable). See {@link getSwapRouterEligibleVaults} for the full
365
+ * two-stage behavior and RPC cost. Uses this instance's configured provider
366
+ * for `chainId`; returns `[]` (no RPC) when no router is deployed there or no
367
+ * provider is configured.
368
+ *
369
+ * On-chain eligibility only — app-level policy (e.g. excluding vaults with
370
+ * their own OVault deposit flow) remains the caller's responsibility.
371
+ *
372
+ * @param chainId - EVM chain ID to resolve eligible vaults for.
373
+ * @param options - Optional `fromBlock` to override the deploy-block scan floor.
374
+ * @returns Checksummed eligible vault addresses; `[]` when unavailable.
375
+ * @throws AugustSDKError when an `eth_getLogs` scan chunk fails.
376
+ * @example
377
+ * ```ts
378
+ * const vaults = await augustSdk.getSwapRouterEligibleVaults(1);
379
+ * ```
380
+ */
381
+ getSwapRouterEligibleVaults(chainId: number, options?: {
382
+ fromBlock?: number;
383
+ }): Promise<IAddress[]>;
309
384
  /**
310
385
  * @function getStakingPositions gets all available reward staking positions
311
386
  * @param wallet optionally passed user wallet address
@@ -66,6 +66,7 @@ const constants_1 = require("../../adapters/sui/constants");
66
66
  const vaults_2 = require("../../core/constants/vaults");
67
67
  const write_actions_1 = require("./write.actions");
68
68
  const read_actions_1 = require("./read.actions");
69
+ const prefetch_1 = require("./prefetch");
69
70
  /**
70
71
  * Vault operations class handling multi-chain vault queries and user positions.
71
72
  * Supports both EVM and Solana vaults with unified interface.
@@ -120,6 +121,17 @@ class AugustVaults extends core_1.AugustBase {
120
121
  * Includes closed vaults that are still visible (`status: 'closed'`,
121
122
  * `is_visible: true`) so consumers can render positions held in closed
122
123
  * vaults; closed vaults with `is_visible: false` are excluded.
124
+ *
125
+ * RPC volume (worst case, wallet enrichment path): the per-vault
126
+ * `balanceOf`/`lagDuration` reads are aggregated into chunked Multicall3
127
+ * `aggregate3` batches per chain (10 calls per chunk) on chains where the
128
+ * canonical Multicall3 deployment is verified; other chains (e.g. Citrea
129
+ * 4114) keep one `eth_call` per read. Measured with
130
+ * `benchmarks/request-counter.js` on a cold cache for a wallet with 62
131
+ * vaults across Ethereum + Avalanche: 83 RPC HTTP requests before batching,
132
+ * 54 after (~35% fewer; 124 per-vault `eth_call`s collapse into ~13
133
+ * `aggregate3` calls — the remaining requests are the per-vault cached
134
+ * decimals/receipt-token reads on a cold cache).
123
135
  * @param options Filtering and enrichment configuration
124
136
  * @returns Array of vault objects with optional loans/allocations/positions
125
137
  */
@@ -248,6 +260,14 @@ class AugustVaults extends core_1.AugustBase {
248
260
  if ((options.wallet && (0, ethers_1.isAddress)(options.wallet)) ||
249
261
  (options.solanaWallet &&
250
262
  utils_1.SolanaUtils.isSolanaAddress(options.solanaWallet))) {
263
+ // Batch the per-vault balanceOf/lagDuration reads (Multicall3, grouped
264
+ // by chain) before fanning out — vaults the batch couldn't serve fall
265
+ // back to their own reads inside getVaultPositions.
266
+ const prefetchedReads = await (0, prefetch_1.prefetchVaultPositionReads)({
267
+ vaults: vaultsPerAvailableProviders,
268
+ wallet: options.wallet,
269
+ providers: this.providers,
270
+ });
251
271
  // fetch all positions for user wallet
252
272
  const vaultResponses = await (0, core_1.promiseSettle)(vaultsPerAvailableProviders.map((v) => (0, getters_1.getVaultPositions)({
253
273
  vault: v.address,
@@ -262,6 +282,7 @@ class AugustVaults extends core_1.AugustBase {
262
282
  augustKey: this.keys?.august,
263
283
  subgraphKey: this.keys?.graph,
264
284
  },
285
+ prefetchedReads: prefetchedReads.get(String(v.address).toLowerCase()),
265
286
  })), 5, // 5 retries
266
287
  2000);
267
288
  // return vault object with position property
@@ -609,7 +630,18 @@ class AugustVaults extends core_1.AugustBase {
609
630
  });
610
631
  }
611
632
  /**
633
+ * Fetch the wallet's positions across vaults — a single vault, one chain,
634
+ * or every chain with a configured provider.
612
635
  *
636
+ * RPC volume (worst case, multi-vault paths): the two non-cacheable
637
+ * per-vault reads (`balanceOf`, `lagDuration`) are aggregated into chunked
638
+ * Multicall3 `aggregate3` batches per chain (10 calls per chunk) on chains
639
+ * where the canonical deployment is verified; unverified chains (e.g.
640
+ * Citrea 4114) and non-EVM vaults keep one `eth_call` per read. Measured
641
+ * with `benchmarks/request-counter.js` on a cold cache for a wallet with 62
642
+ * vaults across Ethereum + Avalanche: 83 RPC HTTP requests before batching,
643
+ * 54 after (~35% fewer). A vault whose batched read fails (e.g. paused)
644
+ * falls back to its own per-vault reads — never to a zeroed balance.
613
645
  */
614
646
  async getVaultPositions({ vault, wallet, chainId, showAllVaults, solanaWallet, stellarWallet, options, }) {
615
647
  // if (!this.authorized) throw new Error('Not authorized.');
@@ -646,6 +678,13 @@ class AugustVaults extends core_1.AugustBase {
646
678
  const allVaults = await (0, core_1.fetchTokenizedVaults)(undefined, this.headers, false, false);
647
679
  if (chainId) {
648
680
  const vaultsPerChain = allVaults.filter((v) => v.chain === chainId);
681
+ // Batch balanceOf/lagDuration via Multicall3 before the per-vault
682
+ // fan-out; un-batched vaults fall back to their own reads.
683
+ const prefetchedReads = await (0, prefetch_1.prefetchVaultPositionReads)({
684
+ vaults: vaultsPerChain,
685
+ wallet,
686
+ providers: this.providers,
687
+ });
649
688
  const vaultResponses = await Promise.all(vaultsPerChain.map((v) => (0, getters_1.getVaultPositions)({
650
689
  vault: v.address,
651
690
  solanaWallet,
@@ -660,6 +699,7 @@ class AugustVaults extends core_1.AugustBase {
660
699
  subgraphKey: this.keys?.graph,
661
700
  headers: this.headers,
662
701
  },
702
+ prefetchedReads: prefetchedReads.get(String(v.address).toLowerCase()),
663
703
  })));
664
704
  const flattened = vaultResponses.flat();
665
705
  let final;
@@ -682,6 +722,13 @@ class AugustVaults extends core_1.AugustBase {
682
722
  // outer catch, and silently return PENDING (then get filtered out).
683
723
  (v?.chain_type === 'solana' && !!this.solanaService) ||
684
724
  (this.providers?.[v?.chain] ? true : false));
725
+ // Batch balanceOf/lagDuration via Multicall3, grouped by chain, before
726
+ // the per-vault fan-out; un-batched vaults fall back to their own reads.
727
+ const prefetchedReads = await (0, prefetch_1.prefetchVaultPositionReads)({
728
+ vaults: vaultsPerAvailableProviders,
729
+ wallet,
730
+ providers: this.providers,
731
+ });
685
732
  const vaultResponses = await Promise.all(vaultsPerAvailableProviders.map((v) => (0, getters_1.getVaultPositions)({
686
733
  vault: v.address,
687
734
  solanaWallet,
@@ -696,6 +743,7 @@ class AugustVaults extends core_1.AugustBase {
696
743
  subgraphKey: this.keys?.graph,
697
744
  headers: this.headers,
698
745
  },
746
+ prefetchedReads: prefetchedReads.get(String(v.address).toLowerCase()),
699
747
  })));
700
748
  const flattened = vaultResponses.flat();
701
749
  let final;
@@ -734,6 +782,12 @@ class AugustVaults extends core_1.AugustBase {
734
782
  // deposited token address; propagate it so consumers render the right
735
783
  // asset symbol instead of guessing the vault's first deposit asset.
736
784
  assetIn: h.assetIn,
785
+ // withdraw-request rows carry the burned share amount. Normalize with
786
+ // the vault decimals (the share token's own decimals) so UIs can show
787
+ // the receipt-token quantity that was actually burned.
788
+ shares: h.shares
789
+ ? (0, core_1.toNormalizedBn)(BigInt(h.shares), h.decimals)
790
+ : undefined,
737
791
  }));
738
792
  }
739
793
  // Helper function to fetch history for a vault
@@ -1016,6 +1070,79 @@ class AugustVaults extends core_1.AugustBase {
1016
1070
  return true;
1017
1071
  });
1018
1072
  }
1073
+ /**
1074
+ * Resolve the ERC-20 tokens currently whitelisted for the `SwapRouter` on a
1075
+ * chain — the any-token set a deposit UI can offer as swap-and-deposit inputs,
1076
+ * each verified `true` against the router's on-chain allowlist.
1077
+ *
1078
+ * Reconstructs the set from the router's `TokenEnabled` event history and
1079
+ * re-checks `whitelistedTokens(token)` for each (the mapping is not
1080
+ * enumerable). See {@link getSwapRouterWhitelistedTokens} for the full
1081
+ * two-stage behavior and RPC cost. Uses this instance's configured provider
1082
+ * for `chainId`; returns `[]` (no RPC) when no router is deployed there or no
1083
+ * provider is configured.
1084
+ *
1085
+ * @param chainId - EVM chain ID to resolve the allowlist for.
1086
+ * @param options - Optional `fromBlock` to override the deploy-block scan floor.
1087
+ * @returns Checksummed whitelisted token addresses; `[]` when unavailable.
1088
+ * Callers still exclude a target vault's natively-accepted assets (those
1089
+ * deposit without a swap).
1090
+ * @throws AugustSDKError when an `eth_getLogs` scan chunk fails.
1091
+ * @example
1092
+ * ```ts
1093
+ * const tokens = await augustSdk.getSwapRouterWhitelistedTokens(1);
1094
+ * ```
1095
+ */
1096
+ async getSwapRouterWhitelistedTokens(chainId, options) {
1097
+ if (!(0, core_1.getSwapRouterAddress)(chainId))
1098
+ return [];
1099
+ const rpcUrl = this.providers?.[chainId];
1100
+ if (!rpcUrl) {
1101
+ core_1.Logger.log.warn('getSwapRouterWhitelistedTokens', `no provider configured for chain ${chainId}`);
1102
+ return [];
1103
+ }
1104
+ return (0, core_1.getSwapRouterWhitelistedTokens)(chainId, {
1105
+ rpcUrl,
1106
+ fromBlock: options?.fromBlock,
1107
+ });
1108
+ }
1109
+ /**
1110
+ * Resolve the vaults currently eligible for the any-token `SwapRouter`
1111
+ * deposit surface on a chain — every vault the router has `enableVault`-ed
1112
+ * whose on-chain registration is still live.
1113
+ *
1114
+ * Reconstructs the set from the router's `VaultEnabled` event history and
1115
+ * re-checks `vaultInfo(vault).referenceAsset != 0` for each (the mapping is
1116
+ * not enumerable). See {@link getSwapRouterEligibleVaults} for the full
1117
+ * two-stage behavior and RPC cost. Uses this instance's configured provider
1118
+ * for `chainId`; returns `[]` (no RPC) when no router is deployed there or no
1119
+ * provider is configured.
1120
+ *
1121
+ * On-chain eligibility only — app-level policy (e.g. excluding vaults with
1122
+ * their own OVault deposit flow) remains the caller's responsibility.
1123
+ *
1124
+ * @param chainId - EVM chain ID to resolve eligible vaults for.
1125
+ * @param options - Optional `fromBlock` to override the deploy-block scan floor.
1126
+ * @returns Checksummed eligible vault addresses; `[]` when unavailable.
1127
+ * @throws AugustSDKError when an `eth_getLogs` scan chunk fails.
1128
+ * @example
1129
+ * ```ts
1130
+ * const vaults = await augustSdk.getSwapRouterEligibleVaults(1);
1131
+ * ```
1132
+ */
1133
+ async getSwapRouterEligibleVaults(chainId, options) {
1134
+ if (!(0, core_1.getSwapRouterAddress)(chainId))
1135
+ return [];
1136
+ const rpcUrl = this.providers?.[chainId];
1137
+ if (!rpcUrl) {
1138
+ core_1.Logger.log.warn('getSwapRouterEligibleVaults', `no provider configured for chain ${chainId}`);
1139
+ return [];
1140
+ }
1141
+ return (0, core_1.getSwapRouterEligibleVaults)(chainId, {
1142
+ rpcUrl,
1143
+ fromBlock: options?.fromBlock,
1144
+ });
1145
+ }
1019
1146
  /**
1020
1147
  * @function getStakingPositions gets all available reward staking positions
1021
1148
  * @param wallet optionally passed user wallet address