@augustdigital/sdk 8.13.0 → 8.15.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.
@@ -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.13.0";
6
+ export declare const SDK_VERSION = "8.15.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.13.0';
9
+ exports.SDK_VERSION = '8.15.0';
10
10
  //# sourceMappingURL=version.js.map
@@ -15,7 +15,7 @@
15
15
  * ```
16
16
  */
17
17
  /** Stable error codes attached to SDK errors. */
18
- export type AugustErrorCode = 'AUTH_MISSING_KEY' | 'AUTH_INVALID_KEY' | 'AUTH_FORBIDDEN' | 'AUTH_UNAUTHORIZED' | 'INVALID_URL' | 'INVALID_INPUT' | 'INVALID_CHAIN' | 'INVALID_ADDRESS' | 'ACCOUNT_NOT_FUNDED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'UNKNOWN';
18
+ export type AugustErrorCode = 'AUTH_MISSING_KEY' | 'AUTH_INVALID_KEY' | 'AUTH_FORBIDDEN' | 'AUTH_UNAUTHORIZED' | 'INVALID_URL' | 'INVALID_INPUT' | 'INVALID_CHAIN' | 'INVALID_ADDRESS' | 'ACCOUNT_NOT_FUNDED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'HISTORY_UNAVAILABLE' | 'UNKNOWN';
19
19
  /** Options accepted by every SDK error constructor. */
20
20
  export interface AugustErrorOptions {
21
21
  /** Original error preserved for stack-trace chaining. */
@@ -72,5 +72,43 @@ export declare class AugustServerError extends AugustSDKError {
72
72
  constructor(status: number, message: string, options?: AugustErrorOptions);
73
73
  toJSON(): Record<string, unknown>;
74
74
  }
75
+ /**
76
+ * Transaction/position history required to compute a value could not be
77
+ * retrieved or was empty, so the computed result would be wrong rather than
78
+ * merely missing.
79
+ *
80
+ * Motivation — the 2026-07-09 lifetime-PnL over-inflation incident. Lifetime
81
+ * PnL is `currentPosition + totalWithdrawn - totalDeposited`. Every
82
+ * history-fetch failure (deleted subgraph, subgraph deployed against the wrong
83
+ * ABI, missing archive backfill, deposits indexed on a different
84
+ * chain/contract) collapses the deposit/withdrawal history to empty. With
85
+ * `deposited = 0` and `withdrawn = 0` but a live on-chain `position > 0`, the
86
+ * formula degenerates to reporting the entire position as profit. Throwing this
87
+ * typed error makes that unrecoverable state fail loudly instead of returning
88
+ * an inflated number that looks plausible.
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * try {
93
+ * await sdk.vaults.getVaultUserLifetimePnl({ vault, wallet, options });
94
+ * } catch (err) {
95
+ * if (err instanceof AugustHistoryUnavailableError) {
96
+ * // History backfill is broken — surface "temporarily unavailable",
97
+ * // never a fabricated PnL number.
98
+ * showUnavailable(err.context);
99
+ * } else throw err;
100
+ * }
101
+ * ```
102
+ */
103
+ export declare class AugustHistoryUnavailableError extends AugustSDKError {
104
+ /**
105
+ * @param message - Human-readable description of the missing history. Callers
106
+ * pass the same `#<method>::<vault>::<wallet>` prefix the surrounding getter
107
+ * uses so log grouping stays consistent.
108
+ * @param options - Standard SDK error options. `context` should carry the
109
+ * `{ vault, wallet }` (or equivalent) identifiers that scope the failure.
110
+ */
111
+ constructor(message: string, options?: AugustErrorOptions);
112
+ }
75
113
  /** Type guard. Works across realms (e.g. Web Worker / VM contexts). */
76
114
  export declare function isAugustSDKError(err: unknown): err is AugustSDKError;
@@ -16,7 +16,7 @@
16
16
  * ```
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.AugustServerError = exports.AugustRateLimitError = exports.AugustValidationError = exports.AugustTimeoutError = exports.AugustNetworkError = exports.AugustAuthError = exports.AugustSDKError = void 0;
19
+ exports.AugustHistoryUnavailableError = exports.AugustServerError = exports.AugustRateLimitError = exports.AugustValidationError = exports.AugustTimeoutError = exports.AugustNetworkError = exports.AugustAuthError = exports.AugustSDKError = void 0;
20
20
  exports.isAugustSDKError = isAugustSDKError;
21
21
  /** Base class for all SDK errors. */
22
22
  class AugustSDKError extends Error {
@@ -131,6 +131,48 @@ class AugustServerError extends AugustSDKError {
131
131
  }
132
132
  }
133
133
  exports.AugustServerError = AugustServerError;
134
+ /**
135
+ * Transaction/position history required to compute a value could not be
136
+ * retrieved or was empty, so the computed result would be wrong rather than
137
+ * merely missing.
138
+ *
139
+ * Motivation — the 2026-07-09 lifetime-PnL over-inflation incident. Lifetime
140
+ * PnL is `currentPosition + totalWithdrawn - totalDeposited`. Every
141
+ * history-fetch failure (deleted subgraph, subgraph deployed against the wrong
142
+ * ABI, missing archive backfill, deposits indexed on a different
143
+ * chain/contract) collapses the deposit/withdrawal history to empty. With
144
+ * `deposited = 0` and `withdrawn = 0` but a live on-chain `position > 0`, the
145
+ * formula degenerates to reporting the entire position as profit. Throwing this
146
+ * typed error makes that unrecoverable state fail loudly instead of returning
147
+ * an inflated number that looks plausible.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * try {
152
+ * await sdk.vaults.getVaultUserLifetimePnl({ vault, wallet, options });
153
+ * } catch (err) {
154
+ * if (err instanceof AugustHistoryUnavailableError) {
155
+ * // History backfill is broken — surface "temporarily unavailable",
156
+ * // never a fabricated PnL number.
157
+ * showUnavailable(err.context);
158
+ * } else throw err;
159
+ * }
160
+ * ```
161
+ */
162
+ class AugustHistoryUnavailableError extends AugustSDKError {
163
+ /**
164
+ * @param message - Human-readable description of the missing history. Callers
165
+ * pass the same `#<method>::<vault>::<wallet>` prefix the surrounding getter
166
+ * uses so log grouping stays consistent.
167
+ * @param options - Standard SDK error options. `context` should carry the
168
+ * `{ vault, wallet }` (or equivalent) identifiers that scope the failure.
169
+ */
170
+ constructor(message, options) {
171
+ super('HISTORY_UNAVAILABLE', message, options);
172
+ this.name = 'AugustHistoryUnavailableError';
173
+ }
174
+ }
175
+ exports.AugustHistoryUnavailableError = AugustHistoryUnavailableError;
134
176
  /** Type guard. Works across realms (e.g. Web Worker / VM contexts). */
135
177
  function isAugustSDKError(err) {
136
178
  return (err instanceof AugustSDKError ||
@@ -46,3 +46,44 @@ export declare const SUBGRAPH_POOL_URLS: (apiKey: string, chainId?: number, pool
46
46
  * generated fallback.
47
47
  */
48
48
  export declare const getDefaultSubgraphUrl: (_network: string, _symbol: string) => string | undefined;
49
+ /**
50
+ * Resolve whether a single fee (management or performance) is currently waived.
51
+ *
52
+ * Backend-driven, per AUGUST-5584. This centralizes logic that previously lived —
53
+ * and diverged — across two upshift-app components. The resolution is deliberately
54
+ * split between the backend and the SDK:
55
+ *
56
+ * - The backend `isFeeWaivedToggle` (`platform_fee_override.is_fee_waived`) is both
57
+ * the master enable AND the TVL latch. Retool config takes precedence over live
58
+ * TVL, and the backend flips this flag `false` once live TVL crosses the
59
+ * configured threshold. That flip is one-way: if TVL later falls back below the
60
+ * threshold the waiver does NOT re-apply. The SDK therefore does no TVL math and
61
+ * never reads `latest_reported_tvl` here — trusting the flag preserves the latch.
62
+ * - The **date** window is time-dependent, so it cannot be a static DB flag; the SDK
63
+ * evaluates it live against the clock.
64
+ *
65
+ * Resolution:
66
+ * - Toggle off → never waived (also the only "hide" path once TVL trips the latch).
67
+ * - Toggle on, both date and TVL blank → indefinite manual waiver → waived.
68
+ * - Toggle on with a date window → waived while `now < waivedUntilDate`.
69
+ * - Toggle on with a TVL threshold configured → waived (the backend keeps the toggle
70
+ * on only while live TVL is under the threshold).
71
+ *
72
+ * @param isFeeWaivedToggle - Backend master/TVL-latch flag. `false` ⇒ never waived.
73
+ * @param waivedUntilDate - ISO datetime string the waiver runs until, or `null` for
74
+ * no date window. An unparseable string yields `NaN`, which is never `> now`, so
75
+ * it correctly contributes no date-based waiver.
76
+ * @param waivedUntilTvl - Configured TVL threshold in whole USD, or `null`. Used only
77
+ * to detect that a TVL-based waiver was set (values `<= 0` are treated as unset);
78
+ * the threshold comparison itself is owned by the backend.
79
+ * @param now - Clock, injected for deterministic testing. Defaults to `new Date()`.
80
+ * @returns `true` if the UI should present this fee as "Fee Waived" right now.
81
+ * @example
82
+ * ```ts
83
+ * // Toggle on, 30-day window still open → waived.
84
+ * resolveFeeWaived(true, '2099-01-01T00:00:00Z', null); // true
85
+ * // Toggle off overrides everything → not waived.
86
+ * resolveFeeWaived(false, '2099-01-01T00:00:00Z', 10_000_000); // false
87
+ * ```
88
+ */
89
+ export declare function resolveFeeWaived(isFeeWaivedToggle: boolean, waivedUntilDate: string | null, waivedUntilTvl: number | null, now?: Date): boolean;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getDefaultSubgraphUrl = exports.SUBGRAPH_POOL_URLS = exports.AVAX_PRICE_FEED_ADDRESS = exports.REWARD_DISTRIBUTOR_ADDRESS = exports.getVaultVersionV2 = exports.getVaultVersion = exports.getAddressChainType = exports.isBadVault = exports.isBadTransaction = void 0;
4
4
  exports.getVaultSymbol = getVaultSymbol;
5
+ exports.resolveFeeWaived = resolveFeeWaived;
5
6
  const ethers_1 = require("ethers");
6
7
  const vaults_1 = require("../constants/vaults");
7
8
  const web3_1 = require("./web3");
@@ -179,4 +180,56 @@ const getDefaultSubgraphUrl = (_network, _symbol) => {
179
180
  return undefined;
180
181
  };
181
182
  exports.getDefaultSubgraphUrl = getDefaultSubgraphUrl;
183
+ /**
184
+ * Resolve whether a single fee (management or performance) is currently waived.
185
+ *
186
+ * Backend-driven, per AUGUST-5584. This centralizes logic that previously lived —
187
+ * and diverged — across two upshift-app components. The resolution is deliberately
188
+ * split between the backend and the SDK:
189
+ *
190
+ * - The backend `isFeeWaivedToggle` (`platform_fee_override.is_fee_waived`) is both
191
+ * the master enable AND the TVL latch. Retool config takes precedence over live
192
+ * TVL, and the backend flips this flag `false` once live TVL crosses the
193
+ * configured threshold. That flip is one-way: if TVL later falls back below the
194
+ * threshold the waiver does NOT re-apply. The SDK therefore does no TVL math and
195
+ * never reads `latest_reported_tvl` here — trusting the flag preserves the latch.
196
+ * - The **date** window is time-dependent, so it cannot be a static DB flag; the SDK
197
+ * evaluates it live against the clock.
198
+ *
199
+ * Resolution:
200
+ * - Toggle off → never waived (also the only "hide" path once TVL trips the latch).
201
+ * - Toggle on, both date and TVL blank → indefinite manual waiver → waived.
202
+ * - Toggle on with a date window → waived while `now < waivedUntilDate`.
203
+ * - Toggle on with a TVL threshold configured → waived (the backend keeps the toggle
204
+ * on only while live TVL is under the threshold).
205
+ *
206
+ * @param isFeeWaivedToggle - Backend master/TVL-latch flag. `false` ⇒ never waived.
207
+ * @param waivedUntilDate - ISO datetime string the waiver runs until, or `null` for
208
+ * no date window. An unparseable string yields `NaN`, which is never `> now`, so
209
+ * it correctly contributes no date-based waiver.
210
+ * @param waivedUntilTvl - Configured TVL threshold in whole USD, or `null`. Used only
211
+ * to detect that a TVL-based waiver was set (values `<= 0` are treated as unset);
212
+ * the threshold comparison itself is owned by the backend.
213
+ * @param now - Clock, injected for deterministic testing. Defaults to `new Date()`.
214
+ * @returns `true` if the UI should present this fee as "Fee Waived" right now.
215
+ * @example
216
+ * ```ts
217
+ * // Toggle on, 30-day window still open → waived.
218
+ * resolveFeeWaived(true, '2099-01-01T00:00:00Z', null); // true
219
+ * // Toggle off overrides everything → not waived.
220
+ * resolveFeeWaived(false, '2099-01-01T00:00:00Z', 10_000_000); // false
221
+ * ```
222
+ */
223
+ function resolveFeeWaived(isFeeWaivedToggle, waivedUntilDate, waivedUntilTvl, now = new Date()) {
224
+ // Master toggle off ⇒ never waived. Also satisfies the one-way TVL latch: the
225
+ // backend has already flipped this false when live TVL crossed the threshold.
226
+ if (!isFeeWaivedToggle)
227
+ return false;
228
+ const isDateActive = waivedUntilDate != null && new Date(waivedUntilDate) > now;
229
+ const isTvlConfigured = waivedUntilTvl != null && waivedUntilTvl > 0;
230
+ // Toggle on but neither trigger configured ⇒ indefinite manual waiver.
231
+ if (waivedUntilDate == null && !isTvlConfigured)
232
+ return true;
233
+ return isDateActive || isTvlConfigured;
234
+ }
182
235
  //# sourceMappingURL=vaults.js.map
@@ -273,6 +273,72 @@ export declare function getYieldLastRealizedOn({ vault, options, }: {
273
273
  vault: IAddress;
274
274
  options: IVaultBaseOptions;
275
275
  }): Promise<number>;
276
+ /** Inputs the lifetime-PnL history guard inspects. */
277
+ export interface PnlHistoryConsistencyParams {
278
+ /** Vault contract address, used for the error message and context. */
279
+ vault: IAddress;
280
+ /** User wallet address, used for the error message and context. */
281
+ wallet: IAddress;
282
+ /**
283
+ * Whether the resolved deposit history contains at least one deposit record
284
+ * (subgraph deposits or LayerZero cross-chain deposits).
285
+ */
286
+ hasDeposits: boolean;
287
+ /** Total withdrawn, in raw base units (asset decimals). */
288
+ totalWithdrawnRaw: bigint;
289
+ /** Current on-chain position value, in raw base units (asset decimals). */
290
+ currentPositionRaw: bigint;
291
+ }
292
+ /**
293
+ * Guard against the 2026-07-09 lifetime-PnL over-inflation failure mode.
294
+ *
295
+ * Lifetime PnL is `currentPosition + totalWithdrawn - totalDeposited`. When the
296
+ * deposit/withdrawal history fails to load it collapses to empty, so both
297
+ * `totalDeposited` and `totalWithdrawn` read as zero. If the wallet still holds
298
+ * a live on-chain position, the formula degenerates to reporting the entire
299
+ * position as pure profit — a plausible-looking but wrong number.
300
+ *
301
+ * This guard rejects exactly that state: a non-zero position with no recorded
302
+ * deposits *and* no recorded withdrawals. Any of the following is treated as
303
+ * consistent and passes:
304
+ * - deposits are present (the normal, computable case);
305
+ * - withdrawals are present (a fully-exited-then-re-entered position is still
306
+ * computable — the withdrawal records prove history loaded);
307
+ * - the current position is zero (the wallet never interacted or fully exited,
308
+ * so a zero PnL is correct, not inflated).
309
+ *
310
+ * Pure and synchronous — no RPC, no I/O — so it is safe to call inside the hot
311
+ * path with no added latency.
312
+ *
313
+ * Known imprecision — vault shares are transferable ERC-20 receipt tokens, so a
314
+ * wallet that *received* shares via a direct token transfer (never deposited,
315
+ * never withdrew) legitimately has `position > 0` with no history records and
316
+ * will throw `HISTORY_UNAVAILABLE` even though its history loaded fine. This is
317
+ * not a regression — the pre-guard formula reported that same position as 100%
318
+ * profit, which was also wrong — but the error message is misleading for that
319
+ * (rare) path. If receipt-token transfer becomes a real user flow, distinguish
320
+ * it here (e.g. by checking for a share-transfer event) rather than widening
321
+ * the guard.
322
+ *
323
+ * @param params - See {@link PnlHistoryConsistencyParams}.
324
+ * @returns `void` when the history is consistent.
325
+ * @throws {@link AugustHistoryUnavailableError} with code `HISTORY_UNAVAILABLE`
326
+ * and `context: { vault, wallet }` when a non-zero position has neither
327
+ * deposits nor withdrawals recorded. The message carries the
328
+ * `#getVaultUserLifetimePnl::<vault>::<wallet>` prefix so it groups with the
329
+ * other errors leaving the getter.
330
+ * @example
331
+ * ```ts
332
+ * assertPnlHistoryConsistent({
333
+ * vault,
334
+ * wallet,
335
+ * hasDeposits: depositsByAsset.size > 0,
336
+ * totalWithdrawnRaw,
337
+ * currentPositionRaw: currentPositionValue.raw,
338
+ * });
339
+ * ```
340
+ */
341
+ export declare function assertPnlHistoryConsistent({ vault, wallet, hasDeposits, totalWithdrawnRaw, currentPositionRaw, }: PnlHistoryConsistencyParams): void;
276
342
  /**
277
343
  * Calculate lifetime PnL for a user in a specific vault.
278
344
  *
@@ -53,6 +53,7 @@ exports.getUserPoints = getUserPoints;
53
53
  exports.registerUserForPoints = registerUserForPoints;
54
54
  exports.fetchPointsLeaderboard = fetchPointsLeaderboard;
55
55
  exports.getYieldLastRealizedOn = getYieldLastRealizedOn;
56
+ exports.assertPnlHistoryConsistent = assertPnlHistoryConsistent;
56
57
  exports.getVaultUserLifetimePnl = getVaultUserLifetimePnl;
57
58
  exports.getVaultPnl = getVaultPnl;
58
59
  exports.getVaultHistoricalTimeseries = getVaultHistoricalTimeseries;
@@ -85,6 +86,7 @@ const date_utils_1 = require("./utils/date-utils");
85
86
  const call_data_decoder_1 = require("./utils/call-data-decoder");
86
87
  const deposits_1 = require("../../services/layerzero/deposits");
87
88
  const redeems_1 = require("../../services/layerzero/redeems");
89
+ const errors_1 = require("../../core/errors");
88
90
  /**
89
91
  * Vault Data Getters
90
92
  *
@@ -1840,6 +1842,62 @@ async function getYieldLastRealizedOn({ vault, options, }) {
1840
1842
  throw new Error(`#getYieldLastRealizedOn::${vault}:${errorMessage}`);
1841
1843
  }
1842
1844
  }
1845
+ /**
1846
+ * Guard against the 2026-07-09 lifetime-PnL over-inflation failure mode.
1847
+ *
1848
+ * Lifetime PnL is `currentPosition + totalWithdrawn - totalDeposited`. When the
1849
+ * deposit/withdrawal history fails to load it collapses to empty, so both
1850
+ * `totalDeposited` and `totalWithdrawn` read as zero. If the wallet still holds
1851
+ * a live on-chain position, the formula degenerates to reporting the entire
1852
+ * position as pure profit — a plausible-looking but wrong number.
1853
+ *
1854
+ * This guard rejects exactly that state: a non-zero position with no recorded
1855
+ * deposits *and* no recorded withdrawals. Any of the following is treated as
1856
+ * consistent and passes:
1857
+ * - deposits are present (the normal, computable case);
1858
+ * - withdrawals are present (a fully-exited-then-re-entered position is still
1859
+ * computable — the withdrawal records prove history loaded);
1860
+ * - the current position is zero (the wallet never interacted or fully exited,
1861
+ * so a zero PnL is correct, not inflated).
1862
+ *
1863
+ * Pure and synchronous — no RPC, no I/O — so it is safe to call inside the hot
1864
+ * path with no added latency.
1865
+ *
1866
+ * Known imprecision — vault shares are transferable ERC-20 receipt tokens, so a
1867
+ * wallet that *received* shares via a direct token transfer (never deposited,
1868
+ * never withdrew) legitimately has `position > 0` with no history records and
1869
+ * will throw `HISTORY_UNAVAILABLE` even though its history loaded fine. This is
1870
+ * not a regression — the pre-guard formula reported that same position as 100%
1871
+ * profit, which was also wrong — but the error message is misleading for that
1872
+ * (rare) path. If receipt-token transfer becomes a real user flow, distinguish
1873
+ * it here (e.g. by checking for a share-transfer event) rather than widening
1874
+ * the guard.
1875
+ *
1876
+ * @param params - See {@link PnlHistoryConsistencyParams}.
1877
+ * @returns `void` when the history is consistent.
1878
+ * @throws {@link AugustHistoryUnavailableError} with code `HISTORY_UNAVAILABLE`
1879
+ * and `context: { vault, wallet }` when a non-zero position has neither
1880
+ * deposits nor withdrawals recorded. The message carries the
1881
+ * `#getVaultUserLifetimePnl::<vault>::<wallet>` prefix so it groups with the
1882
+ * other errors leaving the getter.
1883
+ * @example
1884
+ * ```ts
1885
+ * assertPnlHistoryConsistent({
1886
+ * vault,
1887
+ * wallet,
1888
+ * hasDeposits: depositsByAsset.size > 0,
1889
+ * totalWithdrawnRaw,
1890
+ * currentPositionRaw: currentPositionValue.raw,
1891
+ * });
1892
+ * ```
1893
+ */
1894
+ function assertPnlHistoryConsistent({ vault, wallet, hasDeposits, totalWithdrawnRaw, currentPositionRaw, }) {
1895
+ const positionExists = currentPositionRaw > BigInt(0);
1896
+ const noHistory = !hasDeposits && totalWithdrawnRaw === BigInt(0);
1897
+ if (positionExists && noHistory) {
1898
+ throw new errors_1.AugustHistoryUnavailableError(`#getVaultUserLifetimePnl::${vault}::${wallet}:transaction history unavailable — a live position exists but no deposits or withdrawals were indexed, so lifetime PnL cannot be computed without over-reporting the entire position as profit`, { context: { vault, wallet } });
1899
+ }
1900
+ }
1843
1901
  /**
1844
1902
  * Calculate lifetime PnL for a user in a specific vault.
1845
1903
  *
@@ -2171,6 +2229,17 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
2171
2229
  totalWithdrawnRaw += totalWithdrawalsInShares;
2172
2230
  }
2173
2231
  const totalWithdrawn = (0, core_1.toNormalizedBn)(totalWithdrawnRaw, decimals);
2232
+ // Guard the 2026-07-09 over-inflation incident: a live position with no
2233
+ // deposits *and* no withdrawals means the history failed to load, and the
2234
+ // PnL formula would report the whole position as profit. Fail loudly with a
2235
+ // typed error instead of returning a fabricated number.
2236
+ assertPnlHistoryConsistent({
2237
+ vault,
2238
+ wallet,
2239
+ hasDeposits: depositsByAsset.size > 0,
2240
+ totalWithdrawnRaw,
2241
+ currentPositionRaw: BigInt(currentPositionValue.raw),
2242
+ });
2174
2243
  const tokenPriceUsd = await pricePromise;
2175
2244
  // Validate token price and log warning if invalid
2176
2245
  const effectiveTokenPrice = tokenPriceUsd > 0 ? tokenPriceUsd : 1;
@@ -2223,6 +2292,11 @@ async function getVaultUserLifetimePnl({ vault, wallet, options, }) {
2223
2292
  }
2224
2293
  catch (e) {
2225
2294
  core_1.Logger.log.error('getVaultUserLifetimePnl', e, { vault, wallet });
2295
+ // Preserve the typed history guard so callers can distinguish an
2296
+ // unavailable-history failure from a generic getter error. Its message
2297
+ // already carries the `#getVaultUserLifetimePnl::vault::wallet` prefix.
2298
+ if (e instanceof errors_1.AugustHistoryUnavailableError)
2299
+ throw e;
2226
2300
  const errorMessage = e instanceof Error ? e.message : String(e);
2227
2301
  throw new Error(`#getVaultUserLifetimePnl::${vault}::${wallet}:${errorMessage}`);
2228
2302
  }
@@ -330,6 +330,7 @@ function buildBackendVault(tokenizedVault, overrides) {
330
330
  historical_snapshots: tokenizedVault.historical_snapshots || [],
331
331
  address: tokenizedVault.address,
332
332
  historical_apy: tokenizedVault.historical_apy,
333
+ historical_compound_apy: tokenizedVault.historical_compound_apy,
333
334
  max_drawdown: tokenizedVault.max_drawdown,
334
335
  logoUrl: tokenizedVault.vault_logo_url || '/img/strategist/august.svg',
335
336
  description: tokenizedVault.description || '',
@@ -353,10 +354,10 @@ function buildBackendVault(tokenizedVault, overrides) {
353
354
  fees: {
354
355
  performance: tokenizedVault.weekly_performance_fee_bps || 0,
355
356
  management: tokenizedVault.platform_fee_override?.management_fee || 0,
356
- isManagementWaived: false,
357
+ isManagementWaived: (0, vaults_1.resolveFeeWaived)(tokenizedVault.platform_fee_override?.is_fee_waived ?? false, tokenizedVault.management_fee_waived_until_date ?? null, tokenizedVault.management_fee_waived_until_tvl ?? null),
357
358
  managementFeeWaivedUntilDate: tokenizedVault.management_fee_waived_until_date ?? null,
358
359
  managementFeeWaivedUntilTvl: tokenizedVault.management_fee_waived_until_tvl ?? null,
359
- isPerformanceWaived: false,
360
+ isPerformanceWaived: (0, vaults_1.resolveFeeWaived)(tokenizedVault.platform_fee_override?.is_fee_waived ?? false, tokenizedVault.performance_fee_waived_until_date ?? null, tokenizedVault.performance_fee_waived_until_tvl ?? null),
360
361
  performanceFeeWaivedUntilDate: tokenizedVault.performance_fee_waived_until_date ?? null,
361
362
  performanceFeeWaivedUntilTvl: tokenizedVault.performance_fee_waived_until_tvl ?? null,
362
363
  },
@@ -466,10 +467,10 @@ async function buildFormattedVault(provider, tokenizedVault, contractCalls) {
466
467
  fees: {
467
468
  performance: tokenizedVault.weekly_performance_fee_bps,
468
469
  management: tokenizedVault.platform_fee_override.management_fee,
469
- isManagementWaived: tokenizedVault.platform_fee_override.is_fee_waived,
470
+ isManagementWaived: (0, vaults_1.resolveFeeWaived)(tokenizedVault.platform_fee_override.is_fee_waived, tokenizedVault.management_fee_waived_until_date, tokenizedVault.management_fee_waived_until_tvl),
470
471
  managementFeeWaivedUntilDate: tokenizedVault.management_fee_waived_until_date,
471
472
  managementFeeWaivedUntilTvl: tokenizedVault.management_fee_waived_until_tvl,
472
- isPerformanceWaived: tokenizedVault.platform_fee_override.is_fee_waived,
473
+ isPerformanceWaived: (0, vaults_1.resolveFeeWaived)(tokenizedVault.platform_fee_override.is_fee_waived, tokenizedVault.performance_fee_waived_until_date, tokenizedVault.performance_fee_waived_until_tvl),
473
474
  performanceFeeWaivedUntilDate: tokenizedVault.performance_fee_waived_until_date,
474
475
  performanceFeeWaivedUntilTvl: tokenizedVault.performance_fee_waived_until_tvl,
475
476
  },
@@ -600,6 +601,7 @@ async function buildFormattedVault(provider, tokenizedVault, contractCalls) {
600
601
  maxDepositAmount: maxDepositAmount,
601
602
  receipt: receipt,
602
603
  historical_apy: tokenizedVault?.historical_apy,
604
+ historical_compound_apy: tokenizedVault?.historical_compound_apy,
603
605
  historical_snapshots: tokenizedVault?.historical_snapshots || [],
604
606
  max_drawdown: tokenizedVault?.max_drawdown,
605
607
  composabilityIntegrations: mapComposabilityIntegrations(tokenizedVault),