@augustdigital/sdk 8.6.1 → 8.7.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.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Which field-naming schema a deployed August vault subgraph uses:
3
+ * - `'old'` — snake_case fields (`block_number`, `timestamp_`, `transactionHash_`).
4
+ * - `'new'` — camelCase fields (`blockNumber`, `blockTimestamp`, `transactionHash`).
5
+ *
6
+ * The two templates are otherwise equivalent; the SDK aliases new-schema fields
7
+ * back to the old internal names when querying, so the rest of the code only ever
8
+ * sees the old (snake_case) shape.
9
+ */
10
+ export type SubgraphSchema = 'old' | 'new';
11
+ /**
12
+ * True when a GraphQL error array reports an unknown field on the queried type
13
+ * (message contains "has no field") — the signature of a schema mismatch.
14
+ *
15
+ * @param errors - GraphQL `errors` array from a subgraph response (may be undefined).
16
+ * @returns `true` if any error message indicates an unknown field, else `false`.
17
+ */
18
+ export declare function isSubgraphSchemaFieldError(errors: {
19
+ message?: string;
20
+ }[] | undefined): boolean;
21
+ /**
22
+ * Detects whether a deployed subgraph uses the old or new field schema by probing
23
+ * a single field (`blockTimestamp`) that exists only on the new template.
24
+ *
25
+ * This replaces the previous heuristic — "vault symbol present in the deprecated
26
+ * `SUBGRAPH_VAULT_URLS` map ⇒ old schema, else new" — which mis-guessed any
27
+ * newly-added vault whose subgraph shipped on the old template (e.g.
28
+ * `august-citrea-earn-ctusd`). A wrong guess made every subgraph read query
29
+ * non-existent field names, so the subgraph returned GraphQL errors with no
30
+ * `data` and the SDK silently returned empty sets — emptying the Pending
31
+ * Withdrawals and History tabs for that vault (AUGUST-6568).
32
+ *
33
+ * Because the schema is immutable for a fixed subgraph deployment, the result is
34
+ * cached per URL ({@link SCHEMA_TTL_MS}); across a page's positions/history/
35
+ * withdrawals reads this amounts to a single extra lightweight query per subgraph.
36
+ *
37
+ * Side effects: at most one HTTP POST to the subgraph per URL per TTL window.
38
+ *
39
+ * @param goldskyUrl - Fully-qualified subgraph GraphQL endpoint.
40
+ * @param apiKey - Optional Goldsky API key sent as a Bearer token.
41
+ * @returns `'old'` or `'new'`. Defaults to `'new'` when the URL is empty or the
42
+ * probe is inconclusive (network error / unexpected response) — the schema the
43
+ * majority of current vaults use.
44
+ */
45
+ export declare function resolveSubgraphSchema(goldskyUrl: string, apiKey?: string): Promise<SubgraphSchema>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isSubgraphSchemaFieldError = isSubgraphSchemaFieldError;
4
+ exports.resolveSubgraphSchema = resolveSubgraphSchema;
5
+ const core_1 = require("../../core");
6
+ const fetcher_1 = require("./fetcher");
7
+ const SCHEMA_CACHE_PREFIX = 'subgraph-schema-';
8
+ // A subgraph's schema is fixed for a given deployment/version, so one probe can
9
+ // serve every subsequent read for a long time. Cache generously.
10
+ const SCHEMA_TTL_MS = 60 * 60 * 1000; // 1 hour
11
+ /**
12
+ * True when a GraphQL error array reports an unknown field on the queried type
13
+ * (message contains "has no field") — the signature of a schema mismatch.
14
+ *
15
+ * @param errors - GraphQL `errors` array from a subgraph response (may be undefined).
16
+ * @returns `true` if any error message indicates an unknown field, else `false`.
17
+ */
18
+ function isSubgraphSchemaFieldError(errors) {
19
+ return Boolean(errors?.some((e) => e?.message?.includes('has no field')));
20
+ }
21
+ /**
22
+ * Detects whether a deployed subgraph uses the old or new field schema by probing
23
+ * a single field (`blockTimestamp`) that exists only on the new template.
24
+ *
25
+ * This replaces the previous heuristic — "vault symbol present in the deprecated
26
+ * `SUBGRAPH_VAULT_URLS` map ⇒ old schema, else new" — which mis-guessed any
27
+ * newly-added vault whose subgraph shipped on the old template (e.g.
28
+ * `august-citrea-earn-ctusd`). A wrong guess made every subgraph read query
29
+ * non-existent field names, so the subgraph returned GraphQL errors with no
30
+ * `data` and the SDK silently returned empty sets — emptying the Pending
31
+ * Withdrawals and History tabs for that vault (AUGUST-6568).
32
+ *
33
+ * Because the schema is immutable for a fixed subgraph deployment, the result is
34
+ * cached per URL ({@link SCHEMA_TTL_MS}); across a page's positions/history/
35
+ * withdrawals reads this amounts to a single extra lightweight query per subgraph.
36
+ *
37
+ * Side effects: at most one HTTP POST to the subgraph per URL per TTL window.
38
+ *
39
+ * @param goldskyUrl - Fully-qualified subgraph GraphQL endpoint.
40
+ * @param apiKey - Optional Goldsky API key sent as a Bearer token.
41
+ * @returns `'old'` or `'new'`. Defaults to `'new'` when the URL is empty or the
42
+ * probe is inconclusive (network error / unexpected response) — the schema the
43
+ * majority of current vaults use.
44
+ */
45
+ async function resolveSubgraphSchema(goldskyUrl, apiKey) {
46
+ if (!goldskyUrl)
47
+ return 'new';
48
+ const key = `${SCHEMA_CACHE_PREFIX}${goldskyUrl}`;
49
+ const cached = core_1.CACHE.get(key);
50
+ if (cached)
51
+ return cached;
52
+ try {
53
+ // `blockTimestamp` is a new-schema-only field. If the subgraph rejects it
54
+ // with an unknown-field error, the deployment is old-schema. Schema
55
+ // validation is static, so this is authoritative even when the entity has
56
+ // zero rows.
57
+ const result = await (0, fetcher_1.fetchSubgraph)(goldskyUrl, `{ withdrawalRequesteds(first: 1) { blockTimestamp } }`, apiKey);
58
+ let schema = 'new';
59
+ if (result.status === 200) {
60
+ const json = (await result.json());
61
+ if (isSubgraphSchemaFieldError(json?.errors)) {
62
+ schema = 'old';
63
+ }
64
+ }
65
+ core_1.CACHE.set(key, schema, { ttl: SCHEMA_TTL_MS });
66
+ return schema;
67
+ }
68
+ catch (e) {
69
+ // Don't cache an inconclusive probe — let the next call retry. Default to
70
+ // 'new' so freshly-deployed vaults keep working when the probe is flaky.
71
+ core_1.Logger.log.warn('resolveSubgraphSchema', `probe failed for ${goldskyUrl}, defaulting to new schema: ${e instanceof Error ? e.message : 'unknown error'}`);
72
+ return 'new';
73
+ }
74
+ }
75
+ //# sourceMappingURL=schema.js.map
@@ -19,5 +19,7 @@ export declare function getSubgraphAllWithdrawals(pool: IAddress, provider: ICon
19
19
  withdrawalProcesseds: ISubgraphWithdrawProccessed[];
20
20
  }>;
21
21
  export declare function getSubgraphUserHistory(user: IAddress, provider: IContractRunner, pool: IAddress, slackWebookUrl?: string): Promise<ISubgraphUserHistoryItem[]>;
22
- export declare function getSubgraphVaultHistory(provider: IContractRunner, pool: IAddress, slackWebookUrl?: string): Promise<ISubgraphUserHistoryItem[]>;
22
+ export declare function getSubgraphVaultHistory(provider: IContractRunner, pool: IAddress, slackWebookUrl?: string, opts?: {
23
+ sinceTs?: number;
24
+ }): Promise<ISubgraphUserHistoryItem[]>;
23
25
  export declare function getSubgraphUserTransfers(user: IAddress, provider: IContractRunner, pool: IAddress, slackWebookUrl?: string): Promise<ISubgraphTransfer[]>;
@@ -7,14 +7,46 @@ exports.getSubgraphAllWithdrawals = getSubgraphAllWithdrawals;
7
7
  exports.getSubgraphUserHistory = getSubgraphUserHistory;
8
8
  exports.getSubgraphVaultHistory = getSubgraphVaultHistory;
9
9
  exports.getSubgraphUserTransfers = getSubgraphUserTransfers;
10
+ const ethers_1 = require("ethers");
10
11
  const slack_1 = require("../../core/logger/slack");
11
12
  const fetcher_1 = require("./fetcher");
13
+ const schema_1 = require("./schema");
12
14
  const core_1 = require("../../core");
13
15
  const fetcher_2 = require("../../modules/vaults/fetcher");
14
16
  /**
15
17
  * Utils
16
18
  */
17
19
  const GOLDSKY_API_KEY = 'cmd0lz6qf35lg01ty7u20aijy';
20
+ // Dedupe window for "Missing Subgraph" alerts (per pool).
21
+ const MISSING_SUBGRAPH_ALERT_TTL_MS = 60 * 60 * 1000; // 1 hour
22
+ /**
23
+ * Emit a "Missing Subgraph" Slack alert at most once per pool per TTL.
24
+ *
25
+ * `getDefaultSubgraphUrl` now returns `undefined` for vaults with no metadata or
26
+ * hardcoded subgraph URL, which makes the readers' `if (!goldskyUrl)` branches
27
+ * reachable (previously the fabricated URL was always truthy, so they were dead
28
+ * code). Portfolio/history flows loop over every vault, so alerting on each read
29
+ * would flood the webhook for every metadata-less vault. Deduping per pool
30
+ * surfaces a genuinely-missing subgraph without the flood.
31
+ *
32
+ * @param args.source - Reader function name, for the alert body.
33
+ * @param args.pool - Vault address (used as the dedupe key, lowercased).
34
+ * @param args.chainId - Chain id for the alert context.
35
+ * @param args.slackWebookUrl - Optional override webhook URL.
36
+ */
37
+ function alertMissingSubgraphOnce(args) {
38
+ const key = `missing-subgraph-alert-${args.pool.toLowerCase()}`;
39
+ if (core_1.CACHE.has(key))
40
+ return;
41
+ core_1.CACHE.set(key, true, { ttl: MISSING_SUBGRAPH_ALERT_TTL_MS });
42
+ slack_1.SLACK.error({
43
+ title: 'Missing Subgraph',
44
+ error: `#${args.source}: goldsky url is undefined`,
45
+ poolAddress: args.pool,
46
+ chainId: args.chainId,
47
+ slackWebookUrl: args.slackWebookUrl || '',
48
+ });
49
+ }
18
50
  /**
19
51
  * earnAUSD exists on both Ethereum and Monad, so it needs a _monad suffix
20
52
  * to distinguish its Monad subgraph from the Ethereum one.
@@ -269,18 +301,21 @@ async function getSubgraphWithdrawRequests(pool, provider, slackWebookUrl = slac
269
301
  if (!goldskyUrl) {
270
302
  const chainId = await (0, core_1.getChainId)(provider);
271
303
  core_1.Logger.log.warn('getSubgraphWithdrawRequests', `goldsky url is undefined: ${vaultSymbol}::${pool}`);
272
- slack_1.SLACK.error({
273
- title: 'Missing Subgraph',
274
- error: '#getSubgraphWithdrawRequests: goldsky url is undefined',
275
- poolAddress: pool,
276
- chainId: chainId,
277
- slackWebookUrl: slackWebookUrl || '',
304
+ alertMissingSubgraphOnce({
305
+ source: 'getSubgraphWithdrawRequests',
306
+ pool,
307
+ chainId,
308
+ slackWebookUrl,
278
309
  });
279
310
  return [];
280
311
  }
281
312
  const tokenizedVault = (await (0, core_1.fetchTokenizedVault)(pool))?.[0];
282
313
  const version = (0, core_1.getVaultVersionV2)(tokenizedVault);
283
- const useOldSchema = isOldSubgraphSchema(vaultSymbol);
314
+ // Probe the deployed subgraph to learn its actual field schema (cached per
315
+ // URL) instead of guessing from the vault symbol. See resolveSubgraphSchema —
316
+ // the old symbol-map heuristic silently emptied this set for vaults deployed
317
+ // on the old template but absent from SUBGRAPH_VAULT_URLS (AUGUST-6568).
318
+ const useOldSchema = (await (0, schema_1.resolveSubgraphSchema)(goldskyUrl, GOLDSKY_API_KEY)) === 'old';
284
319
  const orderByTimestamp = useOldSchema ? 'timestamp_' : 'blockTimestamp';
285
320
  const withdrawalRequestedProps = useOldSchema
286
321
  ? version === 'evm-2'
@@ -331,18 +366,19 @@ async function getSubgraphWithdrawProccessed(pool, provider, slackWebookUrl = sl
331
366
  if (!goldskyUrl) {
332
367
  const chainId = await (0, core_1.getChainId)(provider);
333
368
  core_1.Logger.log.error('getSubgraphWithdrawProccessed.missing-subgraph-url', new Error('goldsky url is undefined'), { chainId, vaultSymbol, pool });
334
- slack_1.SLACK.error({
335
- title: 'Missing Subgraph',
336
- error: '#getSubgraphWithdrawProccessed: goldsky url is undefined',
337
- poolAddress: pool,
338
- chainId: chainId,
339
- slackWebookUrl: slackWebookUrl || '',
369
+ alertMissingSubgraphOnce({
370
+ source: 'getSubgraphWithdrawProccessed',
371
+ pool,
372
+ chainId,
373
+ slackWebookUrl,
340
374
  });
341
375
  return [];
342
376
  }
343
377
  const tokenizedVault = (await (0, core_1.fetchTokenizedVault)(pool))?.[0];
344
378
  const version = (0, core_1.getVaultVersionV2)(tokenizedVault);
345
- const useOldSchema = isOldSubgraphSchema(vaultSymbol);
379
+ // Probe the deployed subgraph's actual field schema (cached per URL) rather
380
+ // than guessing from the vault symbol — see resolveSubgraphSchema (AUGUST-6568).
381
+ const useOldSchema = (await (0, schema_1.resolveSubgraphSchema)(goldskyUrl, GOLDSKY_API_KEY)) === 'old';
346
382
  const orderByTimestamp = useOldSchema ? 'timestamp_' : 'blockTimestamp';
347
383
  const withdrawalProcessedProps = useOldSchema
348
384
  ? version === 'evm-2'
@@ -408,18 +444,19 @@ async function getSubgraphAllWithdrawals(pool, provider, slackWebookUrl = slack_
408
444
  if (!goldskyUrl) {
409
445
  const chainId = await (0, core_1.getChainId)(provider);
410
446
  core_1.Logger.log.error('getSubgraphAllWithdrawals', `goldsky url is undefined: ${vaultSymbol}::${pool}`);
411
- slack_1.SLACK.error({
412
- title: 'Missing Subgraph',
413
- error: '#getSubgraphAllWithdrawals: goldsky url is undefined',
414
- poolAddress: pool,
415
- chainId: chainId,
416
- slackWebookUrl: slackWebookUrl || '',
447
+ alertMissingSubgraphOnce({
448
+ source: 'getSubgraphAllWithdrawals',
449
+ pool,
450
+ chainId,
451
+ slackWebookUrl,
417
452
  });
418
453
  return errorReturnObj;
419
454
  }
420
455
  const tokenizedVault = (await (0, core_1.fetchTokenizedVault)(pool))?.[0];
421
456
  const version = (0, core_1.getVaultVersionV2)(tokenizedVault);
422
- const useOldSchema = isOldSubgraphSchema(vaultSymbol);
457
+ // Probe the deployed subgraph's actual field schema (cached per URL) rather
458
+ // than guessing from the vault symbol — see resolveSubgraphSchema (AUGUST-6568).
459
+ const useOldSchema = (await (0, schema_1.resolveSubgraphSchema)(goldskyUrl, GOLDSKY_API_KEY)) === 'old';
423
460
  const orderByTimestamp = useOldSchema ? 'timestamp_' : 'blockTimestamp';
424
461
  const withdrawalRequestedProps = useOldSchema
425
462
  ? version === 'evm-2'
@@ -540,6 +577,15 @@ async function getArchivedVaultUserHistory(user, provider, pool) {
540
577
  }
541
578
  async function getSubgraphUserHistory(user, provider, pool, slackWebookUrl = slack_1.DEFAULT_SLACK_WEBHOOK_URL) {
542
579
  try {
580
+ // This path is EVM-only: it reads chain id and network from an EVM
581
+ // JsonRpcProvider (eth_chainId / getNetwork) and queries an EVM subgraph.
582
+ // A non-EVM (Solana / Stellar) vault address routed here would call
583
+ // eth_chainId against a Solana/Stellar RPC and fail with a 400
584
+ // ("eth_chainId is not available on SOLANA_MAINNET"). Bail early so non-EVM
585
+ // vaults are simply skipped rather than throwing on every history fetch.
586
+ if (!(0, ethers_1.isAddress)(pool)) {
587
+ return [];
588
+ }
543
589
  // Archived vaults: serve history from the backend Mongo snapshot instead of
544
590
  // the (retired) subgraph. The archived set is fetched from the backend (the
545
591
  // routing source of truth) so a vault is only rerouted once its history is
@@ -573,18 +619,19 @@ async function getSubgraphUserHistory(user, provider, pool, slackWebookUrl = sla
573
619
  (0, core_1.getDefaultSubgraphUrl)(networkValue.name, vaultSymbol);
574
620
  if (!goldskyUrl) {
575
621
  core_1.Logger.log.error('getSubgraphUserHistory', `goldsky url is undefined: ${vaultSymbol}::${pool}`);
576
- slack_1.SLACK.error({
577
- title: 'Missing Subgraph',
578
- error: '#getSubgraphUserHistory: goldsky url is undefined',
579
- poolAddress: pool,
580
- chainId: chainId,
581
- slackWebookUrl: slackWebookUrl || '',
622
+ alertMissingSubgraphOnce({
623
+ source: 'getSubgraphUserHistory',
624
+ pool,
625
+ chainId,
626
+ slackWebookUrl,
582
627
  });
583
628
  return requests;
584
629
  }
585
630
  const tokenizedVault = (await (0, core_1.fetchTokenizedVault)(pool))?.[0];
586
631
  const version = (0, core_1.getVaultVersionV2)(tokenizedVault);
587
- const useOldSchema = isOldSubgraphSchema(vaultSymbol);
632
+ // Probe the deployed subgraph's actual field schema (cached per URL) rather
633
+ // than guessing from the vault symbol — see resolveSubgraphSchema (AUGUST-6568).
634
+ const useOldSchema = (await (0, schema_1.resolveSubgraphSchema)(goldskyUrl, GOLDSKY_API_KEY)) === 'old';
588
635
  const orderByTimestamp = useOldSchema ? 'timestamp_' : 'blockTimestamp';
589
636
  const depositProps = useOldSchema
590
637
  ? version === 'evm-2'
@@ -670,7 +717,7 @@ async function getSubgraphUserHistory(user, provider, pool, slackWebookUrl = sla
670
717
  return [];
671
718
  }
672
719
  }
673
- async function getSubgraphVaultHistory(provider, pool, slackWebookUrl = slack_1.DEFAULT_SLACK_WEBHOOK_URL) {
720
+ async function getSubgraphVaultHistory(provider, pool, slackWebookUrl = slack_1.DEFAULT_SLACK_WEBHOOK_URL, opts = {}) {
674
721
  try {
675
722
  // setup
676
723
  const requests = [];
@@ -695,18 +742,19 @@ async function getSubgraphVaultHistory(provider, pool, slackWebookUrl = slack_1.
695
742
  if (!goldskyUrl) {
696
743
  const chainId = await (0, core_1.getChainId)(provider);
697
744
  core_1.Logger.log.error('getSubgraphVaultHistory', `goldsky url is undefined: ${vaultSymbol}::${pool}`);
698
- slack_1.SLACK.error({
699
- title: 'Missing Subgraph',
700
- error: '#getSubgraphVaultHistory: goldsky url is undefined',
701
- poolAddress: pool,
702
- chainId: chainId,
703
- slackWebookUrl: slackWebookUrl || '',
745
+ alertMissingSubgraphOnce({
746
+ source: 'getSubgraphVaultHistory',
747
+ pool,
748
+ chainId,
749
+ slackWebookUrl,
704
750
  });
705
751
  return requests;
706
752
  }
707
753
  const tokenizedVault = (await (0, core_1.fetchTokenizedVault)(pool))?.[0];
708
754
  const version = (0, core_1.getVaultVersionV2)(tokenizedVault);
709
- const useOldSchema = isOldSubgraphSchema(vaultSymbol);
755
+ // Probe the deployed subgraph's actual field schema (cached per URL) rather
756
+ // than guessing from the vault symbol — see resolveSubgraphSchema (AUGUST-6568).
757
+ const useOldSchema = (await (0, schema_1.resolveSubgraphSchema)(goldskyUrl, GOLDSKY_API_KEY)) === 'old';
710
758
  const orderByTimestamp = useOldSchema ? 'timestamp_' : 'blockTimestamp';
711
759
  const withdrawProps = useOldSchema
712
760
  ? version === 'evm-2'
@@ -728,45 +776,91 @@ async function getSubgraphVaultHistory(provider, pool, slackWebookUrl = slack_1.
728
776
  ? EVM_V2_DEPOSIT_QUERY_PROPS
729
777
  : DEPOSIT_QUERY_PROPS
730
778
  : NEW_DEPOSIT_QUERY_PROPS;
731
- const result = await (0, fetcher_1.fetchSubgraph)(goldskyUrl, `{
779
+ // Each of the four event entities is capped at 1000 rows per request, so
780
+ // a busy vault would silently truncate. Page all four in lockstep on a
781
+ // shared `skip` cursor (rows are ordered newest-first) and keep going while
782
+ // any entity returned a full page. When `sinceTs` is set we can stop early:
783
+ // once an entity's oldest row in the page predates the window, no later
784
+ // page can contain in-window rows for it.
785
+ const PAGE_SIZE = 1000;
786
+ const accumulated = {
787
+ withdraws: [],
788
+ withdrawalRequesteds: [],
789
+ withdrawalProcesseds: [],
790
+ deposits: [],
791
+ };
792
+ let skip = 0;
793
+ for (;;) {
794
+ const result = await (0, fetcher_1.fetchSubgraph)(goldskyUrl, `{
732
795
  withdraws (
733
- first: 1000,
796
+ first: ${PAGE_SIZE},
797
+ skip: ${skip},
734
798
  orderBy: ${orderByTimestamp},
735
799
  orderDirection: desc,
736
800
  ) {
737
801
  ${withdrawProps}
738
802
  }
739
803
  withdrawalRequesteds (
740
- first: 1000,
804
+ first: ${PAGE_SIZE},
805
+ skip: ${skip},
741
806
  orderBy: ${orderByTimestamp},
742
807
  orderDirection: desc,
743
808
  ) {
744
809
  ${withdrawalRequestedProps}
745
810
  }
746
811
  withdrawalProcesseds (
747
- first: 1000,
812
+ first: ${PAGE_SIZE},
813
+ skip: ${skip},
748
814
  orderBy: ${orderByTimestamp},
749
815
  orderDirection: desc,
750
816
  ) {
751
817
  ${withdrawalProcessedProps}
752
818
  }
753
819
  deposits (
754
- first: 1000,
820
+ first: ${PAGE_SIZE},
821
+ skip: ${skip},
755
822
  orderBy: ${orderByTimestamp},
756
823
  orderDirection: desc,
757
824
  ) {
758
825
  ${depositProps}
759
826
  }
760
827
  }`, GOLDSKY_API_KEY);
761
- if (result.status !== 200) {
762
- core_1.Logger.log.error('getSubgraphVaultHistory', new Error(`HTTP ${result.status} ${result.statusText}`), { status: result.status, statusText: result.statusText });
763
- return requests;
828
+ if (result.status !== 200) {
829
+ core_1.Logger.log.error('getSubgraphVaultHistory', new Error(`HTTP ${result.status} ${result.statusText}`), { status: result.status, statusText: result.statusText, skip });
830
+ // First page failed → nothing to return. A later page failing still
831
+ // yields whatever earlier pages accumulated.
832
+ if (skip === 0)
833
+ return requests;
834
+ break;
835
+ }
836
+ const json = (await result.json());
837
+ const page = json.data;
838
+ accumulated.withdraws.push(...(page?.withdraws ?? []));
839
+ accumulated.withdrawalRequesteds.push(...(page?.withdrawalRequesteds ?? []));
840
+ accumulated.withdrawalProcesseds.push(...(page?.withdrawalProcesseds ?? []));
841
+ accumulated.deposits.push(...(page?.deposits ?? []));
842
+ const entities = [
843
+ page?.withdraws,
844
+ page?.withdrawalRequesteds,
845
+ page?.withdrawalProcesseds,
846
+ page?.deposits,
847
+ ];
848
+ const needMore = entities.some((rows) => {
849
+ if ((rows?.length ?? 0) < PAGE_SIZE)
850
+ return false;
851
+ if (opts.sinceTs === undefined)
852
+ return true;
853
+ const oldest = Number(rows?.[rows.length - 1]?.timestamp_);
854
+ return !Number.isFinite(oldest) || oldest >= opts.sinceTs;
855
+ });
856
+ if (!needMore)
857
+ break;
858
+ skip += PAGE_SIZE;
764
859
  }
765
- const json = (await result.json());
766
860
  // Format data
767
861
  const chainId = await (0, core_1.getChainId)(provider);
768
862
  const decimals = await (0, core_1.getDecimals)(provider, pool);
769
- const formattedRequests = sortUserHistory(json.data, chainId, decimals, pool);
863
+ const formattedRequests = sortUserHistory(accumulated, chainId, decimals, pool);
770
864
  return formattedRequests;
771
865
  }
772
866
  catch (e) {
@@ -804,16 +898,17 @@ async function getSubgraphUserTransfers(user, provider, pool, slackWebookUrl = s
804
898
  if (!goldskyUrl) {
805
899
  const chainId = await (0, core_1.getChainId)(provider);
806
900
  core_1.Logger.log.error('getSubgraphUserTransfers', `goldsky url is undefined: ${vaultSymbol}::${pool}`);
807
- slack_1.SLACK.error({
808
- title: 'Missing Subgraph',
809
- error: '#getSubgraphUserTransfers: goldsky url is undefined',
810
- poolAddress: pool,
811
- chainId: chainId,
812
- slackWebookUrl: slackWebookUrl || '',
901
+ alertMissingSubgraphOnce({
902
+ source: 'getSubgraphUserTransfers',
903
+ pool,
904
+ chainId,
905
+ slackWebookUrl,
813
906
  });
814
907
  return userTransfers;
815
908
  }
816
- const useOldSchema = isOldSubgraphSchema(vaultSymbol);
909
+ // Probe the deployed subgraph's actual field schema (cached per URL) rather
910
+ // than guessing from the vault symbol — see resolveSubgraphSchema (AUGUST-6568).
911
+ const useOldSchema = (await (0, schema_1.resolveSubgraphSchema)(goldskyUrl, GOLDSKY_API_KEY)) === 'old';
817
912
  const orderByTimestamp = useOldSchema ? 'timestamp_' : 'blockTimestamp';
818
913
  const transferProps = useOldSchema
819
914
  ? TRANSFER_QUERY_PROPS
@@ -524,3 +524,40 @@ export interface ISwapRouterNativeDepositOptions extends ISwapRouterBaseOptions
524
524
  /** Native token amount in wei. Sent as `msg.value`. */
525
525
  amount: bigint;
526
526
  }
527
+ /**
528
+ * Options for {@link swapRouterDeposit} — the high-level, explicit SwapRouter
529
+ * deposit entry point. It reads the vault's reference asset and decimals
530
+ * on-chain, then picks the correct router path from `depositAsset`, so callers
531
+ * do not hand-build swap legs or pre-resolve decimals themselves.
532
+ *
533
+ * Prefer this over relying on the implicit routing inside `vaultDeposit`: here
534
+ * the caller opts into the SwapRouter deliberately, so a vault is never
535
+ * silently routed through a swap it does not need — the failure mode that broke
536
+ * native multi-asset deposits when a multi-asset vault was added to
537
+ * `VAULTS_USING_SWAP_ROUTER`.
538
+ *
539
+ * Unlike {@link ISwapRouterBaseOptions}, `receiver` is optional here and
540
+ * defaults to the signer's resolved EOA.
541
+ */
542
+ export interface ISwapRouterDepositOptions extends Omit<ISwapRouterBaseOptions, 'receiver'> {
543
+ /**
544
+ * Token being deposited, held by the signer. Its relationship to the vault's
545
+ * reference asset selects the router path:
546
+ * - equals the reference asset → direct router deposit (no swap);
547
+ * - the zero address → native-token deposit (only when the reference asset
548
+ * is the chain's wrapped-native token);
549
+ * - any other ERC-20 → a swap to the reference asset via the chain's
550
+ * whitelisted DEX aggregator, bundled into the deposit.
551
+ */
552
+ depositAsset: IAddress;
553
+ /** Amount in `depositAsset`'s smallest unit (decimal string or bigint). */
554
+ amount: string | bigint;
555
+ /** Address that receives the minted shares. Defaults to the signer's EOA. */
556
+ receiver?: IAddress;
557
+ /**
558
+ * Max slippage in basis points for the swap leg. Only used when `depositAsset`
559
+ * differs from the reference asset; passed through to the swap quote, whose
560
+ * own default applies when omitted.
561
+ */
562
+ slippageBps?: number;
563
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@augustdigital/sdk",
3
- "version": "8.6.1",
3
+ "version": "8.7.1",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/sdk.d.ts",
6
6
  "keywords": [