@kasufinance/kasu-sdk 2.11.0 → 2.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.
- package/README.md +37 -2
- package/dist/bundle.cjs.js +1516 -71
- package/dist/bundle.esm.js +1511 -73
- package/dist/domain/index.d.ts +6 -2
- package/dist/domain/index.js +2 -0
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/pool-tvl.d.ts +62 -0
- package/dist/domain/pool-tvl.js +22 -0
- package/dist/domain/pool-tvl.js.map +1 -0
- package/dist/domain/yield-history.d.ts +122 -0
- package/dist/domain/yield-history.js +95 -0
- package/dist/domain/yield-history.js.map +1 -0
- package/dist/facade/chain-configs.js +24 -0
- package/dist/facade/chain-configs.js.map +1 -1
- package/dist/facade/kasu.d.ts +2 -0
- package/dist/facade/kasu.js +13 -5
- package/dist/facade/kasu.js.map +1 -1
- package/dist/facade/strategies.d.ts +20 -0
- package/dist/facade/strategies.js +22 -0
- package/dist/facade/strategies.js.map +1 -1
- package/dist/facade/types.d.ts +20 -0
- package/dist/facade/user-portfolio.d.ts +32 -0
- package/dist/facade/user-portfolio.js +34 -0
- package/dist/facade/user-portfolio.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/kasu-sdk.d.ts +9 -1
- package/dist/kasu-sdk.js +2 -2
- package/dist/kasu-sdk.js.map +1 -1
- package/dist/sdk-config.d.ts +20 -0
- package/dist/sdk-config.js +3 -1
- package/dist/sdk-config.js.map +1 -1
- package/dist/services/DataService/data-service.d.ts +81 -1
- package/dist/services/DataService/data-service.js +211 -6
- package/dist/services/DataService/data-service.js.map +1 -1
- package/dist/services/DataService/tvl-history.d.ts +296 -0
- package/dist/services/DataService/tvl-history.js +558 -0
- package/dist/services/DataService/tvl-history.js.map +1 -0
- package/dist/services/Portfolio/portfolio.d.ts +37 -0
- package/dist/services/Portfolio/portfolio.js +136 -56
- package/dist/services/Portfolio/portfolio.js.map +1 -1
- package/dist/services/Portfolio/queries.js +15 -3
- package/dist/services/Portfolio/queries.js.map +1 -1
- package/dist/services/Portfolio/types.d.ts +8 -0
- package/dist/services/Portfolio/yield-history.d.ts +162 -0
- package/dist/services/Portfolio/yield-history.js +378 -0
- package/dist/services/Portfolio/yield-history.js.map +1 -0
- package/dist/services/shared.d.ts +16 -0
- package/dist/services/shared.js +30 -0
- package/dist/services/shared.js.map +1 -1
- package/package.json +1 -1
- package/src/domain/index.ts +15 -10
- package/src/domain/pool-tvl.ts +66 -0
- package/src/domain/yield-history.ts +194 -0
- package/src/facade/chain-configs.ts +26 -0
- package/src/facade/kasu.ts +20 -2
- package/src/facade/strategies.ts +26 -0
- package/src/facade/types.ts +20 -0
- package/src/facade/user-portfolio.ts +40 -3
- package/src/index.ts +11 -0
- package/src/kasu-sdk.ts +19 -2
- package/src/sdk-config.ts +22 -0
- package/src/services/DataService/data-service.ts +296 -6
- package/src/services/DataService/tvl-history.ts +853 -0
- package/src/services/Portfolio/portfolio.ts +263 -153
- package/src/services/Portfolio/queries.ts +15 -3
- package/src/services/Portfolio/types.ts +10 -0
- package/src/services/Portfolio/yield-history.ts +541 -0
- package/src/services/shared.ts +38 -1
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { readItems, readSingleton } from '@directus/sdk';
|
|
2
2
|
import { Provider } from '@ethersproject/abstract-provider';
|
|
3
|
-
import { Signer } from 'ethers';
|
|
4
|
-
import { formatUnits } from 'ethers/lib/utils';
|
|
3
|
+
import { BigNumber, providers, Signer } from 'ethers';
|
|
4
|
+
import { formatUnits, isAddress } from 'ethers/lib/utils';
|
|
5
5
|
import { GraphQLClient } from 'graphql-request';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
ISystemVariablesAbi,
|
|
9
|
+
ISystemVariablesAbi__factory,
|
|
8
10
|
KasuPoolExternalTVLAbi,
|
|
9
11
|
KasuPoolExternalTVLAbi__factory,
|
|
10
12
|
} from '../../contracts';
|
|
13
|
+
import { poolTvl, PoolTvlHistoryEntry } from '../../domain/pool-tvl';
|
|
11
14
|
import { epochRateToApy } from '../../domain/rates';
|
|
12
15
|
import { SdkConfig } from '../../sdk-config';
|
|
13
16
|
import { getSystemVariablesQuery } from '../Locking/queries';
|
|
14
17
|
import { SystemVariables } from '../Locking/types';
|
|
15
|
-
import { filterArray } from '../shared';
|
|
18
|
+
import { filterArray, isEmptyHistoryError } from '../shared';
|
|
16
19
|
|
|
17
20
|
import {
|
|
18
21
|
createDirectusClient,
|
|
@@ -44,6 +47,26 @@ import {
|
|
|
44
47
|
TrancheConfigurationSubgraphResult,
|
|
45
48
|
TrancheSubgraphResult,
|
|
46
49
|
} from './subgraph-types';
|
|
50
|
+
import {
|
|
51
|
+
buildPoolTvlHistory,
|
|
52
|
+
Cancellation,
|
|
53
|
+
ClearingsScope,
|
|
54
|
+
createOffChainTvlReader,
|
|
55
|
+
distinctBlocks,
|
|
56
|
+
fetchOffChainTvlAtBlocks,
|
|
57
|
+
fetchPoolBalancesAtBlocks,
|
|
58
|
+
fetchPoolClearings,
|
|
59
|
+
GraphRequest,
|
|
60
|
+
OffChainTvlReader,
|
|
61
|
+
PastBlockCache,
|
|
62
|
+
PoolTvlHistoryOptions,
|
|
63
|
+
resolvePoolTvlHistoryWindow,
|
|
64
|
+
runCancellable,
|
|
65
|
+
TVL_HISTORY_CACHE_DEPTH,
|
|
66
|
+
TVL_HISTORY_DEFAULT_TIMEOUT_MS,
|
|
67
|
+
TVL_HISTORY_RPC_TIMEOUT_MS,
|
|
68
|
+
validatePoolTvlHistoryOptions,
|
|
69
|
+
} from './tvl-history';
|
|
47
70
|
import {
|
|
48
71
|
BadAndDoubtfulDebts,
|
|
49
72
|
FinancialReportingDocuments,
|
|
@@ -58,21 +81,44 @@ import {
|
|
|
58
81
|
TrancheData,
|
|
59
82
|
} from './types';
|
|
60
83
|
|
|
84
|
+
/** Thrown by `getPoolTvlHistory` when no provider that serves past state is configured. */
|
|
85
|
+
export const NO_HISTORY_PROVIDER_MESSAGE =
|
|
86
|
+
'getPoolTvlHistory: no history provider; pass historyProvider or set historyRpcUrls to RPCs that serve past state';
|
|
87
|
+
|
|
61
88
|
export class DataService {
|
|
62
89
|
private readonly _graph: GraphQLClient;
|
|
63
90
|
private readonly _externalTvlAbi: KasuPoolExternalTVLAbi;
|
|
91
|
+
private readonly _systemVariablesAbi: ISystemVariablesAbi;
|
|
64
92
|
private readonly _directus: KasuDirectusClient;
|
|
65
93
|
private _directusPoolOverview: PoolOverviewDirectus[] | undefined;
|
|
94
|
+
private readonly _explicitHistoryProvider: Provider | undefined;
|
|
95
|
+
private _builtHistoryProviders: Provider[] | undefined;
|
|
96
|
+
/** Immutable past-block reads, kept for the life of this instance. */
|
|
97
|
+
private readonly _tvlHistoryCache = new PastBlockCache();
|
|
66
98
|
|
|
99
|
+
/**
|
|
100
|
+
* @param historyProvider - a provider that SERVES PAST STATE (an archive
|
|
101
|
+
* RPC), used only by `getPoolTvlHistory` for the off-chain TVL at old
|
|
102
|
+
* blocks. Without one, providers are built on `SdkConfig.historyRpcUrls`
|
|
103
|
+
* (tried in order); with neither, that one call throws
|
|
104
|
+
* `NO_HISTORY_PROVIDER_MESSAGE`. Never the default provider: a pruned
|
|
105
|
+
* node refuses old blocks.
|
|
106
|
+
*/
|
|
67
107
|
constructor(
|
|
68
108
|
private _kasuConfig: SdkConfig,
|
|
69
109
|
signerOrProvider: Signer | Provider,
|
|
110
|
+
historyProvider?: Provider,
|
|
70
111
|
) {
|
|
71
112
|
this._externalTvlAbi = KasuPoolExternalTVLAbi__factory.connect(
|
|
72
113
|
_kasuConfig.contracts.ExternalTVL,
|
|
73
114
|
signerOrProvider,
|
|
74
115
|
);
|
|
116
|
+
this._systemVariablesAbi = ISystemVariablesAbi__factory.connect(
|
|
117
|
+
_kasuConfig.contracts.SystemVariables,
|
|
118
|
+
signerOrProvider,
|
|
119
|
+
);
|
|
75
120
|
this._graph = new GraphQLClient(_kasuConfig.subgraphUrl);
|
|
121
|
+
this._explicitHistoryProvider = historyProvider;
|
|
76
122
|
|
|
77
123
|
this._directus = createDirectusClient(_kasuConfig.directusUrl);
|
|
78
124
|
}
|
|
@@ -196,6 +242,249 @@ export class DataService {
|
|
|
196
242
|
return tvlMap;
|
|
197
243
|
}
|
|
198
244
|
|
|
245
|
+
/**
|
|
246
|
+
* The pool's TVL — the total lent into it by everyone — at every cycle
|
|
247
|
+
* boundary, then as of now: the data behind a TVL chart.
|
|
248
|
+
*
|
|
249
|
+
* One `clearing` point per FINISHED clearing, read at the block it
|
|
250
|
+
* finished in (`LendingPoolClearing.endBlockNumber`), then one `current`
|
|
251
|
+
* point. Every point is `poolTvl(onChain, offChain)`, the definition
|
|
252
|
+
* `getPoolOverview` uses for `totalValueLocked.total`:
|
|
253
|
+
*
|
|
254
|
+
* - on-chain: `lendingPool.balance` from the subgraph, read with
|
|
255
|
+
* time-travel queries (`block: { number }`);
|
|
256
|
+
* - off-chain: `ExternalTVL.externalTVLOfPool(pool)` at the same block,
|
|
257
|
+
* through the HISTORY providers (see the constructor). 0 before the
|
|
258
|
+
* contract was deployed (decided by `getCode`, and only if the same
|
|
259
|
+
* provider shows the pool's own code there); any failed read throws.
|
|
260
|
+
*
|
|
261
|
+
* The `current` point mixes two sources: the subgraph's latest balance
|
|
262
|
+
* (and its latest block time) and `externalTVLOfPool` at `latest` on the
|
|
263
|
+
* history provider. `getPoolOverview` reads the same two, the off-chain
|
|
264
|
+
* one on the default provider, so the last point equals
|
|
265
|
+
* `totalValueLocked.total` when both reads succeed. They can differ when
|
|
266
|
+
* `getPoolOverview`'s off-chain read FAILS — it then falls back to 0 —
|
|
267
|
+
* or when the figure moves between the two calls.
|
|
268
|
+
*
|
|
269
|
+
* Units: `tvl` in display units of the deployment's own stable token
|
|
270
|
+
* (USDC on Base and XDC USDC, AUDD on XDC AUDD); `timestamp` in unix
|
|
271
|
+
* seconds. Ascending in time. See `PoolTvlHistoryEntry`.
|
|
272
|
+
*
|
|
273
|
+
* Cost: one subgraph request for the clearings (per thousand), one per
|
|
274
|
+
* hundred distinct uncached clearing blocks, a `currentEpochNumber` read;
|
|
275
|
+
* then, on the history providers, one `externalTVLOfPool` per distinct
|
|
276
|
+
* uncached block plus the latest, and one to about log2(n) `getCode`s —
|
|
277
|
+
* at most `TVL_HISTORY_RPC_CONCURRENCY` in flight. Final blocks are
|
|
278
|
+
* cached on this instance, so a second call reads only what is new.
|
|
279
|
+
* `timeoutMs` (default 30 s) bounds the whole call.
|
|
280
|
+
*
|
|
281
|
+
* `[]` for a pool the subgraph does not know, for a window with no epoch
|
|
282
|
+
* in it, and on a LITE deployment whose subgraph lacks the entities. On a
|
|
283
|
+
* full deployment that schema error throws, like every network, indexing
|
|
284
|
+
* or past-state failure.
|
|
285
|
+
*
|
|
286
|
+
* @throws RangeError / TypeError on a malformed option or a pool id that
|
|
287
|
+
* is not an address, and `NO_HISTORY_PROVIDER_MESSAGE` when the
|
|
288
|
+
* deployment has an `ExternalTVL` contract but no history provider — all
|
|
289
|
+
* before any network read. `PoolTvlHistoryTimeoutError` /
|
|
290
|
+
* `PoolTvlHistoryAbortedError` on the deadline or the signal,
|
|
291
|
+
* `PastStateUnavailableError` from a pruned history provider.
|
|
292
|
+
*/
|
|
293
|
+
async getPoolTvlHistory(
|
|
294
|
+
poolId: string,
|
|
295
|
+
options: PoolTvlHistoryOptions = {},
|
|
296
|
+
): Promise<PoolTvlHistoryEntry[]> {
|
|
297
|
+
validatePoolTvlHistoryOptions(options);
|
|
298
|
+
if (!isAddress(poolId)) {
|
|
299
|
+
throw new TypeError(
|
|
300
|
+
`getPoolTvlHistory: poolId must be an address, got ${String(poolId)}`,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
const pool = poolId.toLowerCase();
|
|
304
|
+
const externalTvl = this._externalTvlAddress();
|
|
305
|
+
const rpcs = externalTvl ? this._historyRpcs() : [];
|
|
306
|
+
if (externalTvl && rpcs.length === 0) {
|
|
307
|
+
throw new Error(NO_HISTORY_PROVIDER_MESSAGE);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return await runCancellable(
|
|
311
|
+
options.timeoutMs ?? TVL_HISTORY_DEFAULT_TIMEOUT_MS,
|
|
312
|
+
options.signal,
|
|
313
|
+
(cancellation) =>
|
|
314
|
+
this._readPoolTvlHistory(
|
|
315
|
+
pool,
|
|
316
|
+
options,
|
|
317
|
+
externalTvl
|
|
318
|
+
? this._createOffChainTvlReader({
|
|
319
|
+
externalTvl,
|
|
320
|
+
pool,
|
|
321
|
+
rpcs,
|
|
322
|
+
cancellation,
|
|
323
|
+
})
|
|
324
|
+
: null,
|
|
325
|
+
externalTvl ?? '',
|
|
326
|
+
cancellation,
|
|
327
|
+
),
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private async _readPoolTvlHistory(
|
|
332
|
+
pool: string,
|
|
333
|
+
options: PoolTvlHistoryOptions,
|
|
334
|
+
reader: OffChainTvlReader | null,
|
|
335
|
+
externalTvl: string,
|
|
336
|
+
cancellation: Cancellation,
|
|
337
|
+
): Promise<PoolTvlHistoryEntry[]> {
|
|
338
|
+
const graph: GraphRequest = <T>(
|
|
339
|
+
document: string,
|
|
340
|
+
variables: Record<string, unknown>,
|
|
341
|
+
signal?: AbortSignal,
|
|
342
|
+
): Promise<T> =>
|
|
343
|
+
this._graph.request<T>({ document, variables, signal });
|
|
344
|
+
const scope: ClearingsScope =
|
|
345
|
+
options.fromEpoch === undefined && options.count !== undefined
|
|
346
|
+
? { kind: 'newest', count: options.count }
|
|
347
|
+
: { kind: 'from', fromEpoch: options.fromEpoch ?? 0 };
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const [currentEpochNumber, { clearings, snapshot }] =
|
|
351
|
+
await Promise.all([
|
|
352
|
+
this._systemVariablesAbi.currentEpochNumber(),
|
|
353
|
+
fetchPoolClearings(graph, pool, scope, cancellation),
|
|
354
|
+
]);
|
|
355
|
+
const currentEpoch = currentEpochNumber.toNumber();
|
|
356
|
+
|
|
357
|
+
const window = resolvePoolTvlHistoryWindow(options, currentEpoch);
|
|
358
|
+
const poolNow = snapshot.pool;
|
|
359
|
+
if (!window || !poolNow) return [];
|
|
360
|
+
const head = snapshot.meta?.block;
|
|
361
|
+
if (!head || typeof head.timestamp !== 'number') {
|
|
362
|
+
throw new Error(
|
|
363
|
+
'getPoolTvlHistory: the subgraph returned no latest block time',
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const inWindow = clearings.filter(
|
|
368
|
+
(c) =>
|
|
369
|
+
c.epochId >= window.fromEpoch &&
|
|
370
|
+
c.epochId <= window.toEpoch,
|
|
371
|
+
);
|
|
372
|
+
const blocks = distinctBlocks(inWindow);
|
|
373
|
+
const withCurrent = window.toEpoch >= currentEpoch;
|
|
374
|
+
const cacheable = (block: number): boolean =>
|
|
375
|
+
block <= head.number - TVL_HISTORY_CACHE_DEPTH;
|
|
376
|
+
|
|
377
|
+
const [balances, offChainRead] = await Promise.all([
|
|
378
|
+
this._balancesAtBlocks(graph, pool, blocks, cacheable, cancellation),
|
|
379
|
+
reader
|
|
380
|
+
? fetchOffChainTvlAtBlocks(reader, blocks, {
|
|
381
|
+
withLatest: withCurrent,
|
|
382
|
+
cancellation,
|
|
383
|
+
cache: this._tvlHistoryCache,
|
|
384
|
+
cacheKey: (kind, block) =>
|
|
385
|
+
`${kind}|${externalTvl}|${pool}|${String(block)}`,
|
|
386
|
+
cacheable,
|
|
387
|
+
})
|
|
388
|
+
: {
|
|
389
|
+
offChain: new Map(
|
|
390
|
+
blocks.map((b) => [b, BigNumber.from(0)]),
|
|
391
|
+
),
|
|
392
|
+
latest: null,
|
|
393
|
+
},
|
|
394
|
+
]);
|
|
395
|
+
|
|
396
|
+
return buildPoolTvlHistory({
|
|
397
|
+
clearings: inWindow,
|
|
398
|
+
balances,
|
|
399
|
+
offChain: offChainRead.offChain,
|
|
400
|
+
current: withCurrent
|
|
401
|
+
? {
|
|
402
|
+
epochId: currentEpoch,
|
|
403
|
+
timestamp: head.timestamp,
|
|
404
|
+
balance: poolNow.balance,
|
|
405
|
+
offChain: offChainRead.latest ?? BigNumber.from(0),
|
|
406
|
+
}
|
|
407
|
+
: null,
|
|
408
|
+
decimals: this._kasuConfig.stableAssetDecimals,
|
|
409
|
+
});
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (isEmptyHistoryError(error, this._kasuConfig.isLiteDeployment)) {
|
|
412
|
+
return [];
|
|
413
|
+
}
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** The pool's balance at each block: cached final blocks, then one batch. */
|
|
419
|
+
private async _balancesAtBlocks(
|
|
420
|
+
graph: GraphRequest,
|
|
421
|
+
pool: string,
|
|
422
|
+
blocks: readonly number[],
|
|
423
|
+
cacheable: (block: number) => boolean,
|
|
424
|
+
cancellation: Cancellation,
|
|
425
|
+
): Promise<Map<number, string>> {
|
|
426
|
+
const key = (block: number): string =>
|
|
427
|
+
`balance|${this._kasuConfig.subgraphUrl}|${pool}|${String(block)}`;
|
|
428
|
+
const balances = new Map<number, string>();
|
|
429
|
+
const missing: number[] = [];
|
|
430
|
+
for (const block of blocks) {
|
|
431
|
+
const hit = cacheable(block)
|
|
432
|
+
? this._tvlHistoryCache.get<string>(key(block))
|
|
433
|
+
: undefined;
|
|
434
|
+
if (hit === undefined) missing.push(block);
|
|
435
|
+
else balances.set(block, hit);
|
|
436
|
+
}
|
|
437
|
+
if (missing.length > 0) {
|
|
438
|
+
const read = await fetchPoolBalancesAtBlocks(
|
|
439
|
+
graph,
|
|
440
|
+
pool,
|
|
441
|
+
missing,
|
|
442
|
+
cancellation,
|
|
443
|
+
);
|
|
444
|
+
for (const [block, balance] of read.balances) {
|
|
445
|
+
balances.set(block, balance);
|
|
446
|
+
if (cacheable(block)) this._tvlHistoryCache.set(key(block), balance);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return balances;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/** `null` when the deployment has no `ExternalTVL` contract. */
|
|
453
|
+
private _externalTvlAddress(): string | null {
|
|
454
|
+
const address = this._kasuConfig.contracts.ExternalTVL;
|
|
455
|
+
if (!address || /^0x0{40}$/i.test(address)) return null;
|
|
456
|
+
return address.toLowerCase();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** Seam for tests: the real reader over the history RPCs. */
|
|
460
|
+
private _createOffChainTvlReader(
|
|
461
|
+
args: Parameters<typeof createOffChainTvlReader>[0],
|
|
462
|
+
): OffChainTvlReader {
|
|
463
|
+
return createOffChainTvlReader(args);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* The history RPCs, in fallback order: the explicit provider, else ONE
|
|
468
|
+
* provider per `historyRpcUrls` entry — built here and nowhere else,
|
|
469
|
+
* pinned to `SdkConfig.chainId` when known, each with a per-call timeout.
|
|
470
|
+
* Built once per instance.
|
|
471
|
+
*/
|
|
472
|
+
private _historyRpcs(): Provider[] {
|
|
473
|
+
if (this._explicitHistoryProvider) return [this._explicitHistoryProvider];
|
|
474
|
+
if (!this._builtHistoryProviders) {
|
|
475
|
+
// Through the `ethers` namespace, so the consumer's own build
|
|
476
|
+
// (Node or browser) is used — see `defaultProvider` in the facade.
|
|
477
|
+
this._builtHistoryProviders = this._kasuConfig.historyRpcUrls.map(
|
|
478
|
+
(url) =>
|
|
479
|
+
new providers.StaticJsonRpcProvider(
|
|
480
|
+
{ url, timeout: TVL_HISTORY_RPC_TIMEOUT_MS },
|
|
481
|
+
this._kasuConfig.chainId,
|
|
482
|
+
),
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
return this._builtHistoryProviders;
|
|
486
|
+
}
|
|
487
|
+
|
|
199
488
|
async getPoolOverview(
|
|
200
489
|
epochId: string,
|
|
201
490
|
id_in?: string[],
|
|
@@ -436,9 +725,10 @@ export class DataService {
|
|
|
436
725
|
subheading: lendingPoolDirectus?.subheading,
|
|
437
726
|
operatingSince: lendingPoolDirectus?.operatingSince,
|
|
438
727
|
totalValueLocked: {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
728
|
+
// The one TVL definition, shared with getPoolTvlHistory.
|
|
729
|
+
total: poolTvl(
|
|
730
|
+
lendingPoolSubgraph.balance,
|
|
731
|
+
offChainTvl,
|
|
442
732
|
).toString(),
|
|
443
733
|
offchain: offChainTvl,
|
|
444
734
|
},
|