@augustdigital/sdk 8.15.0 → 8.16.1

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.
@@ -154,14 +154,38 @@ export interface IVaultAllocations {
154
154
  positions: any[];
155
155
  }>;
156
156
  }
157
+ /**
158
+ * A vault's APY breakdown.
159
+ *
160
+ * **Unit contract: every rate on this type is a PERCENTAGE, not a decimal
161
+ * fraction.** `7.95` means 7.95%. The backend reports these as decimal fractions
162
+ * (`0.0795`); the SDK multiplies by 100 exactly once, uniformly across every rate
163
+ * field, before returning them. Consumers must therefore divide by 100 at most once
164
+ * — and must apply the same treatment to `apy`, `liquidApy`, `pointsApy`,
165
+ * `campaignApy` and `underlyingApy`, because they all arrive in the same unit.
166
+ *
167
+ * This is stated explicitly because the convention going undocumented let a
168
+ * consumer scale a subset of these fields twice, making them contribute 1/100th of
169
+ * their value to a displayed total (AUGUST-6967). The unit is uniform here by
170
+ * design; treating some of these fields as decimals and others as percentages is
171
+ * always a bug on the consumer side.
172
+ */
157
173
  export interface IVaultApy {
174
+ /** Base vault APY as a percentage (7.95 = 7.95%). */
158
175
  apy: number;
176
+ /** Human-readable note on how the APY is derived; empty or null when unset. */
159
177
  explainer: string;
178
+ /** Liquid-yield component as a percentage. */
160
179
  liquidApy: number;
180
+ /** Points-program APY as a percentage, or null when the vault has no points. */
161
181
  pointsApy: number | null;
182
+ /** Campaign-boost APY as a percentage, or null when no campaign is running. */
162
183
  campaignApy: number | null;
184
+ /** Claimable rewards, in the reward token's own units — not a percentage. */
163
185
  rewardsClaimable: number;
186
+ /** Compounded rewards, in the reward token's own units — not a percentage. */
164
187
  rewardsCompounded: number;
188
+ /** Underlying-asset APY as a percentage (e.g. staking yield on the deposit asset). */
165
189
  underlyingApy: number;
166
190
  }
167
191
  export interface IVaultStrategist {
@@ -188,6 +212,63 @@ export interface IVaultAdapterConfig {
188
212
  tokens?: IAddress[];
189
213
  bridgeId?: number;
190
214
  }
215
+ /**
216
+ * Provenance timestamps for a vault's server-side data, mapped from the backend
217
+ * `freshness` object.
218
+ *
219
+ * A vault's share price and its APY come from two different pipelines: the SDK
220
+ * reads share price live from chain (`totalAssets()/totalSupply()`), while APY is a
221
+ * backend column recomputed on a schedule. These stamps make the remaining gap
222
+ * visible so a UI can render "data as of X" instead of implying both numbers are
223
+ * equally current.
224
+ *
225
+ * The three fields are **independently nullable and independently meaningful** —
226
+ * none of them is a fallback for another. In particular, when
227
+ * `shareRatioSnapshotAt` is later than `apyComputedAt`, a share-price snapshot has
228
+ * landed that the APY has not yet been recomputed from: that ordering is the
229
+ * staleness signal.
230
+ *
231
+ * Every value is an RFC 3339 UTC string with a trailing `Z`, exactly as the backend
232
+ * sends it — the SDK does not reformat or reinterpret them. Keep the `Z` if you
233
+ * pass these anywhere: `new Date('2026-07-25T00:01:46')` (no `Z`) is parsed as
234
+ * *local* time and silently shifts by the viewer's UTC offset.
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * const vault = await sdk.vaults.getVault(address);
239
+ * const { apyComputedAt, shareRatioSnapshotAt } = vault.freshness ?? {};
240
+ * // Only comparable when BOTH are present.
241
+ * const apyLagsSharePrice =
242
+ * !!apyComputedAt &&
243
+ * !!shareRatioSnapshotAt &&
244
+ * new Date(shareRatioSnapshotAt) > new Date(apyComputedAt);
245
+ * ```
246
+ */
247
+ export interface IVaultFreshness {
248
+ /**
249
+ * When the backend last recomputed `historical_apy`, TVL and drawdown for this
250
+ * vault. Null if never computed.
251
+ */
252
+ apyComputedAt: string | null;
253
+ /**
254
+ * Timestamp of the newest persisted share-price snapshot the backend holds. Null
255
+ * only when the vault has no snapshot yet.
256
+ *
257
+ * Populated regardless of whether the read loaded snapshots — the backend stamps
258
+ * it from a scalar `max(snapshot_datetime)`, so it is present even on
259
+ * `load_snapshots=false` reads (the basic and sub-accounts views). When it is
260
+ * null, treat it as absent: do not fall back to `apyComputedAt` or `cachedAt` in
261
+ * its place.
262
+ */
263
+ shareRatioSnapshotAt: string | null;
264
+ /**
265
+ * When the backend assembled the response body this vault came from, before its
266
+ * own response cache. Note this does **not** account for the SDK's client-side
267
+ * LRU (10–15 min on `fetchTokenizedVault` / `fetchTokenizedVaults`), so a vault
268
+ * served from that cache can be older than this stamp suggests is possible.
269
+ */
270
+ cachedAt: string | null;
271
+ }
191
272
  export interface IVault {
192
273
  max_drawdown?: number | null;
193
274
  historical_apy?: Record<number, number> | null;
@@ -278,6 +359,13 @@ export interface IVault {
278
359
  defaultApyHorizon?: 7 | 30 | null;
279
360
  latest_reported_tvl?: number | null;
280
361
  cachedAt?: string | null;
362
+ /**
363
+ * Provenance timestamps for the vault's server-side data — when its APY was last
364
+ * recomputed, when its newest share-price snapshot was taken, and when the
365
+ * backend built the response. Null (or a null member) when the backend did not
366
+ * report that stamp; see {@link IVaultFreshness}.
367
+ */
368
+ freshness?: IVaultFreshness | null;
281
369
  showCapFilled?: boolean | null;
282
370
  /** Controls how the vault's APY is displayed; null when the backend has not set an override. */
283
371
  apyOverride?: IApyOverride | null;
@@ -446,6 +446,47 @@ export interface IApyOverride {
446
446
  */
447
447
  is_show_compound_apy?: boolean;
448
448
  }
449
+ /**
450
+ * Per-vault provenance timestamps returned by the backend on every tokenized-vault
451
+ * read (list and single-vault, on both the `api.augustdigital.io/tokenized_vault`
452
+ * and `api.upshift.finance/tokenized_vaults` surfaces).
453
+ *
454
+ * The three stamps describe *different* pipelines and are independently nullable —
455
+ * one of them cannot stand in for another. Consumers use the ordering between them
456
+ * to detect that the vault's share price has moved ahead of its APY: a
457
+ * `share_ratio_snapshot_at` later than `apy_computed_at` means a share-price
458
+ * snapshot has landed that the APY has not yet been recomputed from.
459
+ *
460
+ * All values are RFC 3339 UTC strings with a trailing `Z` (e.g.
461
+ * `'2026-07-25T00:01:46Z'`). The trailing `Z` matters: `new Date()` parses an
462
+ * offset-less date-time as *local* time, which would silently shift the stamp by
463
+ * the viewer's UTC offset.
464
+ *
465
+ * @see ITokenizedVault.freshness
466
+ */
467
+ export interface ITokenizedVaultFreshness {
468
+ /**
469
+ * When `historical_apy`, TVL and drawdown were last recomputed, or null if they
470
+ * have never been computed for this vault.
471
+ */
472
+ apy_computed_at: string | null;
473
+ /**
474
+ * `snapshot_datetime` of the newest persisted share-price snapshot, or null when
475
+ * the vault has no snapshot yet.
476
+ *
477
+ * The backend stamps this from a scalar `max(snapshot_datetime)`, so it is
478
+ * populated independently of `load_snapshots` — including on the basic and
479
+ * sub-accounts views (`load_snapshots=false`), which is what every app call site
480
+ * sends. Never substitute another timestamp for it.
481
+ */
482
+ share_ratio_snapshot_at: string | null;
483
+ /**
484
+ * When the backend assembled the response body, before it entered the backend's
485
+ * own response cache. Distinct from the top-level `cached_at` only in scope:
486
+ * this one travels with the vault object.
487
+ */
488
+ cached_at: string | null;
489
+ }
449
490
  export interface ITokenizedVaultStrategist {
450
491
  strategist_name: string;
451
492
  strategist_logo: string;
@@ -596,6 +637,12 @@ export interface ITokenizedVault {
596
637
  daily_pnl_per_share: Record<string, number>;
597
638
  latest_reported_tvl: number | null;
598
639
  cached_at?: string | null;
640
+ /**
641
+ * Per-vault provenance timestamps (APY recompute, newest share-price snapshot,
642
+ * response assembly). Absent on older/cached responses predating the backend
643
+ * change that added it.
644
+ */
645
+ freshness?: ITokenizedVaultFreshness | null;
599
646
  instant_redeem_config: null | {
600
647
  subaccount_address: string;
601
648
  output_asset_symbol: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@augustdigital/sdk",
3
- "version": "8.15.0",
3
+ "version": "8.16.1",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/sdk.d.ts",
6
6
  "keywords": [
@@ -66,6 +66,7 @@
66
66
  "test:jest:watch": "jest --config jest.config.unit.js --watch",
67
67
  "test:jest:coverage": "jest --config jest.config.unit.js --coverage",
68
68
  "test:forknet": "node tests/forknet/run.mjs",
69
+ "test:solana-idl": "jest --config jest.config.idl-drift.js",
69
70
  "clean": "rm -rf ./lib",
70
71
  "format": "biome check --write .",
71
72
  "lint-sdk": "lint-staged",