@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.
- package/lib/adapters/evm/index.d.ts +24 -1
- package/lib/adapters/evm/index.js +26 -0
- package/lib/adapters/solana/vault.actions.d.ts +18 -0
- package/lib/adapters/solana/vault.actions.js +43 -2
- package/lib/core/analytics/method-taxonomy.js +2 -0
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/core/constants/swap-router.d.ts +26 -8
- package/lib/core/constants/swap-router.js +31 -15
- package/lib/core/helpers/swap-router.d.ts +16 -4
- package/lib/core/helpers/swap-router.js +19 -6
- package/lib/core/helpers/vaults.d.ts +23 -1
- package/lib/core/helpers/vaults.js +24 -2
- package/lib/main.d.ts +26 -0
- package/lib/main.js +22 -0
- package/lib/modules/vaults/main.d.ts +46 -0
- package/lib/modules/vaults/main.js +88 -7
- package/lib/modules/vaults/write.actions.d.ts +51 -2
- package/lib/modules/vaults/write.actions.js +149 -44
- package/lib/sdk.d.ts +1181 -923
- package/lib/services/subgraph/schema.d.ts +45 -0
- package/lib/services/subgraph/schema.js +75 -0
- package/lib/services/subgraph/vaults.d.ts +3 -1
- package/lib/services/subgraph/vaults.js +148 -53
- package/lib/types/vaults.d.ts +37 -0
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
-
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
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
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
762
|
-
|
|
763
|
-
|
|
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(
|
|
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
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
slackWebookUrl: slackWebookUrl || '',
|
|
901
|
+
alertMissingSubgraphOnce({
|
|
902
|
+
source: 'getSubgraphUserTransfers',
|
|
903
|
+
pool,
|
|
904
|
+
chainId,
|
|
905
|
+
slackWebookUrl,
|
|
813
906
|
});
|
|
814
907
|
return userTransfers;
|
|
815
908
|
}
|
|
816
|
-
|
|
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
|
package/lib/types/vaults.d.ts
CHANGED
|
@@ -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
|
+
}
|