@kasufinance/kasu-sdk 2.12.0 → 2.13.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.
Files changed (62) hide show
  1. package/README.md +26 -1
  2. package/dist/bundle.cjs.js +974 -83
  3. package/dist/bundle.esm.js +971 -85
  4. package/dist/domain/index.d.ts +2 -0
  5. package/dist/domain/index.js +1 -0
  6. package/dist/domain/index.js.map +1 -1
  7. package/dist/domain/pool-tvl.d.ts +62 -0
  8. package/dist/domain/pool-tvl.js +22 -0
  9. package/dist/domain/pool-tvl.js.map +1 -0
  10. package/dist/domain/requests.d.ts +11 -10
  11. package/dist/facade/chain-configs.js +24 -0
  12. package/dist/facade/chain-configs.js.map +1 -1
  13. package/dist/facade/kasu.d.ts +2 -0
  14. package/dist/facade/kasu.js +13 -5
  15. package/dist/facade/kasu.js.map +1 -1
  16. package/dist/facade/strategies.d.ts +20 -0
  17. package/dist/facade/strategies.js +22 -0
  18. package/dist/facade/strategies.js.map +1 -1
  19. package/dist/facade/types.d.ts +20 -0
  20. package/dist/index.d.ts +4 -0
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/kasu-sdk.d.ts +9 -1
  24. package/dist/kasu-sdk.js +2 -2
  25. package/dist/kasu-sdk.js.map +1 -1
  26. package/dist/sdk-config.d.ts +20 -0
  27. package/dist/sdk-config.js +3 -1
  28. package/dist/sdk-config.js.map +1 -1
  29. package/dist/services/DataService/data-service.d.ts +81 -1
  30. package/dist/services/DataService/data-service.js +211 -6
  31. package/dist/services/DataService/data-service.js.map +1 -1
  32. package/dist/services/DataService/tvl-history.d.ts +296 -0
  33. package/dist/services/DataService/tvl-history.js +558 -0
  34. package/dist/services/DataService/tvl-history.js.map +1 -0
  35. package/dist/services/Portfolio/yield-history.d.ts +1 -16
  36. package/dist/services/Portfolio/yield-history.js +2 -29
  37. package/dist/services/Portfolio/yield-history.js.map +1 -1
  38. package/dist/services/UserLending/subgraph-types.d.ts +2 -8
  39. package/dist/services/UserLending/types.d.ts +10 -0
  40. package/dist/services/UserLending/user-lending.d.ts +14 -0
  41. package/dist/services/shared.d.ts +16 -0
  42. package/dist/services/shared.js +30 -0
  43. package/dist/services/shared.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/domain/index.ts +3 -0
  46. package/src/domain/pool-tvl.ts +66 -0
  47. package/src/domain/requests.ts +37 -23
  48. package/src/facade/chain-configs.ts +26 -0
  49. package/src/facade/kasu.ts +20 -2
  50. package/src/facade/strategies.ts +26 -0
  51. package/src/facade/types.ts +20 -0
  52. package/src/index.ts +9 -0
  53. package/src/kasu-sdk.ts +19 -2
  54. package/src/sdk-config.ts +22 -0
  55. package/src/services/DataService/data-service.ts +296 -6
  56. package/src/services/DataService/tvl-history.ts +853 -0
  57. package/src/services/Portfolio/yield-history.ts +2 -35
  58. package/src/services/UserLending/queries.ts +2 -7
  59. package/src/services/UserLending/subgraph-types.ts +2 -8
  60. package/src/services/UserLending/types.ts +10 -0
  61. package/src/services/UserLending/user-lending.ts +98 -40
  62. package/src/services/shared.ts +38 -1
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var utils$2 = require('ethers/lib/utils');
4
3
  var ethers = require('ethers');
4
+ var utils$2 = require('ethers/lib/utils');
5
5
 
6
6
  const defaultJsonSerializer = JSON;
7
7
 
@@ -15481,6 +15481,28 @@ class KasuPoolExternalTVLAbi__factory {
15481
15481
  }
15482
15482
  KasuPoolExternalTVLAbi__factory.abi = _abi;
15483
15483
 
15484
+ /**
15485
+ * A pool's TVL — the total lent into it by everyone — and the shape of its
15486
+ * history. Pure: the reads live in `services/DataService/tvl-history.ts`.
15487
+ */
15488
+ /**
15489
+ * The ONE definition of a pool's TVL: what lenders have lent on chain
15490
+ * (`lendingPool.balance` in the subgraph) plus the off-chain amount the
15491
+ * pool's `ExternalTVL` contract records (`externalTVLOfPool`).
15492
+ *
15493
+ * Both inputs, and the result, are in DISPLAY units of the deployment's own
15494
+ * stable token (USDC on Base and XDC USDC, AUDD on XDC AUDD) — `'1.5'` is
15495
+ * one and a half tokens, not 1.5 minor units. Convert before formatting as
15496
+ * USD.
15497
+ *
15498
+ * `getPoolOverview` (its `totalValueLocked.total`) and `getPoolTvlHistory`
15499
+ * (every point) both call this, so the chart and the figure above it cannot
15500
+ * be computed two ways.
15501
+ */
15502
+ function poolTvl(onChainBalance, offChainTvl) {
15503
+ return parseFloat(onChainBalance) + parseFloat(offChainTvl);
15504
+ }
15505
+
15484
15506
  /**
15485
15507
  * Epochs in a year — the single source of truth for every rate conversion in
15486
15508
  * the SDK and in every consumer that undoes one.
@@ -15720,6 +15742,36 @@ function filterArray(array, id_in) {
15720
15742
  });
15721
15743
  }
15722
15744
  }
15745
+ /**
15746
+ * A GraphQL VALIDATION error naming an entity, field or argument this
15747
+ * deployment's subgraph does not have. A network error, a timeout or an
15748
+ * indexing error is not one.
15749
+ */
15750
+ function isMissingEntityError(error) {
15751
+ if (typeof error !== 'object' || error === null)
15752
+ return false;
15753
+ const response = error.response;
15754
+ const errors = response === null || response === void 0 ? void 0 : response.errors;
15755
+ if (!Array.isArray(errors) || errors.length === 0)
15756
+ return false;
15757
+ return errors.every((e) => {
15758
+ const message = e === null || e === void 0 ? void 0 : e.message;
15759
+ return (typeof message === 'string' &&
15760
+ /has no field|cannot query field|unknown (type|field|argument)|no such (type|field)/i.test(message));
15761
+ });
15762
+ }
15763
+ /**
15764
+ * The only failure a history read (`getYieldHistory`, `getPoolTvlHistory`)
15765
+ * turns into `[]`: a missing-schema error
15766
+ * on a LITE deployment, whose subgraph may predate the entities. On a full
15767
+ * deployment the entities are part of the schema, so the same error means a
15768
+ * broken query or subgraph and THROWS — an empty chart there would hide it.
15769
+ * Everything else (network, timeout, indexing) throws on every deployment, so
15770
+ * a consumer can tell "no history here" from "history unavailable".
15771
+ */
15772
+ function isEmptyHistoryError(error, liteDeployment) {
15773
+ return liteDeployment && isMissingEntityError(error);
15774
+ }
15723
15775
 
15724
15776
  /**
15725
15777
  * The error a CMS-only call raises on a deployment configured without
@@ -15909,11 +15961,569 @@ const getLendingPoolWithdrawalAndDepositsQuery = gql `
15909
15961
  }
15910
15962
  `;
15911
15963
 
15964
+ /** Clearings per subgraph page (the subgraph's own row cap). */
15965
+ const TVL_HISTORY_PAGE_SIZE = 1000;
15966
+ /**
15967
+ * A runaway guard on the clearings pagination, not a working limit: a pool
15968
+ * clears once per epoch, so 20 pages would be twenty thousand cycles.
15969
+ */
15970
+ const TVL_HISTORY_MAX_PAGES = 20;
15971
+ /**
15972
+ * How many past blocks one time-travel request reads, as GraphQL aliases. A
15973
+ * pool with ~120 clearings costs two such requests.
15974
+ */
15975
+ const TVL_HISTORY_BLOCKS_PER_REQUEST = 100;
15976
+ /** At most this many past-state RPC reads are in flight at once. */
15977
+ const TVL_HISTORY_RPC_CONCURRENCY = 8;
15978
+ /** Attempts per past-state RPC read (each over every history RPC in turn). */
15979
+ const TVL_HISTORY_RPC_ATTEMPTS = 3;
15980
+ /** Back-off before the second attempt; doubled for each one after. */
15981
+ const TVL_HISTORY_RPC_RETRY_DELAY_MS = 500;
15982
+ /** Per-call timeout of the history providers the SDK builds itself. */
15983
+ const TVL_HISTORY_RPC_TIMEOUT_MS = 10000;
15984
+ /** Default `timeoutMs` of a whole `getPoolTvlHistory` call. */
15985
+ const TVL_HISTORY_DEFAULT_TIMEOUT_MS = 30000;
15986
+ /**
15987
+ * Blocks at least this far below the subgraph's head are treated as final
15988
+ * and their reads are cached; nearer ones are read again every time.
15989
+ */
15990
+ const TVL_HISTORY_CACHE_DEPTH = 64;
15991
+ /** Entries per SDK instance before the cache starts again from empty. */
15992
+ const TVL_HISTORY_CACHE_LIMIT = 20000;
15993
+ /** `getPoolTvlHistory` took longer than its `timeoutMs`. */
15994
+ class PoolTvlHistoryTimeoutError extends Error {
15995
+ constructor(timeoutMs) {
15996
+ super(`getPoolTvlHistory: timed out after ${String(timeoutMs)} ms`);
15997
+ this.timeoutMs = timeoutMs;
15998
+ this.name = 'PoolTvlHistoryTimeoutError';
15999
+ }
16000
+ }
16001
+ /** `getPoolTvlHistory` was abandoned through its `signal`. */
16002
+ class PoolTvlHistoryAbortedError extends Error {
16003
+ constructor() {
16004
+ super('getPoolTvlHistory: aborted');
16005
+ this.name = 'PoolTvlHistoryAbortedError';
16006
+ }
16007
+ }
16008
+ /**
16009
+ * A history provider answered a past block as if nothing were deployed — no
16010
+ * code even for the pool, which cleared in that very block. A pruned node
16011
+ * does that; trusting it would draw the off-chain TVL as 0.
16012
+ */
16013
+ class PastStateUnavailableError extends Error {
16014
+ constructor(block) {
16015
+ super(`getPoolTvlHistory: history provider does not serve past state (no code for the pool at block ${String(block)}, where it cleared)`);
16016
+ this.block = block;
16017
+ this.name = 'PastStateUnavailableError';
16018
+ }
16019
+ }
16020
+ /**
16021
+ * Throws `RangeError` / `TypeError` on a malformed option.
16022
+ * `getPoolTvlHistory` calls it ONCE, BEFORE any network read;
16023
+ * `resolvePoolTvlHistoryWindow` assumes it has run.
16024
+ */
16025
+ function validatePoolTvlHistoryOptions(options) {
16026
+ const { fromEpoch, count, timeoutMs, signal } = options;
16027
+ if (fromEpoch !== undefined &&
16028
+ (!Number.isSafeInteger(fromEpoch) || fromEpoch < 0)) {
16029
+ throw new RangeError(`getPoolTvlHistory: fromEpoch must be a non-negative integer, got ${String(fromEpoch)}`);
16030
+ }
16031
+ if (count !== undefined && (!Number.isSafeInteger(count) || count < 1)) {
16032
+ throw new RangeError(`getPoolTvlHistory: count must be a positive integer, got ${String(count)}`);
16033
+ }
16034
+ if (timeoutMs !== undefined &&
16035
+ (typeof timeoutMs !== 'number' ||
16036
+ !Number.isFinite(timeoutMs) ||
16037
+ timeoutMs <= 0)) {
16038
+ throw new RangeError(`getPoolTvlHistory: timeoutMs must be a positive number, got ${String(timeoutMs)}`);
16039
+ }
16040
+ if (signal !== undefined &&
16041
+ (typeof signal !== 'object' ||
16042
+ typeof signal.aborted !== 'boolean')) {
16043
+ throw new TypeError('getPoolTvlHistory: signal must be an AbortSignal');
16044
+ }
16045
+ }
16046
+ /** The epoch window. Options must already have passed validation. */
16047
+ function resolvePoolTvlHistoryWindow(options, currentEpoch) {
16048
+ const { fromEpoch, count } = options;
16049
+ let from;
16050
+ let to = currentEpoch;
16051
+ if (fromEpoch !== undefined) {
16052
+ from = fromEpoch;
16053
+ if (count !== undefined) {
16054
+ to = Math.min(currentEpoch, fromEpoch + count - 1);
16055
+ }
16056
+ }
16057
+ else {
16058
+ from = count !== undefined ? Math.max(0, currentEpoch - count + 1) : 0;
16059
+ }
16060
+ return from > to ? null : { fromEpoch: from, toEpoch: to };
16061
+ }
16062
+ /**
16063
+ * Runs `work` under a deadline and an optional caller signal. Whichever comes
16064
+ * first rejects the returned promise with its typed error at once, aborts
16065
+ * `cancellation.signal` (cancelling in-flight subgraph requests) and makes
16066
+ * every later `throwIfCancelled()` throw, so no new read starts. An RPC call
16067
+ * already sent cannot be recalled; its answer is dropped.
16068
+ */
16069
+ function runCancellable(timeoutMs, external, work) {
16070
+ return __awaiter(this, void 0, void 0, function* () {
16071
+ if (external === null || external === void 0 ? void 0 : external.aborted)
16072
+ throw new PoolTvlHistoryAbortedError();
16073
+ const controller = new AbortController();
16074
+ let reason;
16075
+ let rejectCancelled = () => undefined;
16076
+ const cancelled = new Promise((_, reject) => {
16077
+ rejectCancelled = reject;
16078
+ });
16079
+ const cancel = (error) => {
16080
+ if (reason)
16081
+ return;
16082
+ reason = error;
16083
+ controller.abort();
16084
+ rejectCancelled(error);
16085
+ };
16086
+ const timer = setTimeout(() => {
16087
+ cancel(new PoolTvlHistoryTimeoutError(timeoutMs));
16088
+ }, timeoutMs);
16089
+ const onAbort = () => {
16090
+ cancel(new PoolTvlHistoryAbortedError());
16091
+ };
16092
+ external === null || external === void 0 ? void 0 : external.addEventListener('abort', onAbort);
16093
+ const cancellation = {
16094
+ signal: controller.signal,
16095
+ throwIfCancelled: () => {
16096
+ if (reason)
16097
+ throw reason;
16098
+ },
16099
+ };
16100
+ try {
16101
+ const running = work(cancellation);
16102
+ // The losing side must never surface as an unhandled rejection.
16103
+ running.catch(() => undefined);
16104
+ return yield Promise.race([running, cancelled]);
16105
+ }
16106
+ finally {
16107
+ clearTimeout(timer);
16108
+ external === null || external === void 0 ? void 0 : external.removeEventListener('abort', onAbort);
16109
+ }
16110
+ });
16111
+ }
16112
+ // ---------------------------------------------------------------------------
16113
+ // Subgraph reads
16114
+ // ---------------------------------------------------------------------------
16115
+ /** Above any epoch number: "no upper bound" for the clearings query. */
16116
+ const NO_EPOCH_BOUND = '1000000000000';
16117
+ /**
16118
+ * The pool's FINISHED clearings, plus — on the first page only — its balance
16119
+ * now and the subgraph's latest block.
16120
+ *
16121
+ * Two modes over one document: ascending from `$epochGte` (a `fromEpoch`
16122
+ * window, or everything), or descending below `$epochLt` (the newest N, for
16123
+ * a `count`-only window, so it never pages through the whole history).
16124
+ */
16125
+ const poolTvlClearingsQuery = gql `
16126
+ query PoolTvlClearings(
16127
+ $poolId: ID!
16128
+ $pool: String!
16129
+ $epochGte: BigInt!
16130
+ $epochLt: BigInt!
16131
+ $direction: OrderDirection!
16132
+ $first: Int!
16133
+ $withSnapshot: Boolean!
16134
+ ) {
16135
+ clearings: lendingPoolClearings(
16136
+ first: $first
16137
+ orderBy: epochId
16138
+ orderDirection: $direction
16139
+ where: {
16140
+ lendingPool: $pool
16141
+ isFinished: true
16142
+ epochId_gte: $epochGte
16143
+ epochId_lt: $epochLt
16144
+ }
16145
+ ) {
16146
+ epochId
16147
+ endBlockNumber
16148
+ endTimestamp
16149
+ }
16150
+ pool: lendingPool(id: $poolId) @include(if: $withSnapshot) {
16151
+ balance
16152
+ }
16153
+ meta: _meta @include(if: $withSnapshot) {
16154
+ block {
16155
+ number
16156
+ timestamp
16157
+ }
16158
+ }
16159
+ }
16160
+ `;
16161
+ /** The time-travel alias for one block: `b<block>`. */
16162
+ function blockAlias(block) {
16163
+ return `b${String(block)}`;
16164
+ }
16165
+ /**
16166
+ * One time-travel request: the pool's balance at the END of each block, one
16167
+ * alias per block. Block numbers are written into the document, so each must
16168
+ * be a positive safe integer — anything else throws rather than reaching the
16169
+ * query text. The pool id travels as a variable.
16170
+ */
16171
+ function poolBalancesAtBlocksQuery(blocks) {
16172
+ const fields = blocks.map((block) => {
16173
+ if (!Number.isSafeInteger(block) || block <= 0) {
16174
+ throw new Error(`getPoolTvlHistory: refusing block number ${String(block)}`);
16175
+ }
16176
+ return `${blockAlias(block)}: lendingPool(id: $poolId, block: { number: ${String(block)} }) { balance }`;
16177
+ });
16178
+ return `query PoolTvlAtBlocks($poolId: ID!) { ${fields.join(' ')} }`;
16179
+ }
16180
+ function parseClearing(row) {
16181
+ const clearing = {
16182
+ epochId: Number(row.epochId),
16183
+ block: Number(row.endBlockNumber),
16184
+ timestamp: Number(row.endTimestamp),
16185
+ };
16186
+ if (!Number.isSafeInteger(clearing.epochId) ||
16187
+ clearing.epochId < 0 ||
16188
+ !Number.isSafeInteger(clearing.block) ||
16189
+ clearing.block <= 0 ||
16190
+ !Number.isSafeInteger(clearing.timestamp) ||
16191
+ clearing.timestamp <= 0) {
16192
+ throw new Error(`getPoolTvlHistory: malformed clearing row ${JSON.stringify(row)}`);
16193
+ }
16194
+ return clearing;
16195
+ }
16196
+ /**
16197
+ * The scope's finished clearings, ascending by epoch, and the snapshot from
16198
+ * the first page. Normally ONE request.
16199
+ */
16200
+ function fetchPoolClearings(request, poolId, scope, cancellation) {
16201
+ return __awaiter(this, void 0, void 0, function* () {
16202
+ const clearings = [];
16203
+ let snapshot = {};
16204
+ let gte = scope.kind === 'from' ? scope.fromEpoch : 0;
16205
+ let lt;
16206
+ let requests = 0;
16207
+ for (;;) {
16208
+ cancellation.throwIfCancelled();
16209
+ if (requests >= TVL_HISTORY_MAX_PAGES) {
16210
+ throw new Error(`getPoolTvlHistory: more than ${String(TVL_HISTORY_MAX_PAGES)} pages of clearings for one pool`);
16211
+ }
16212
+ const first = requests === 0;
16213
+ const remaining = scope.kind === 'newest'
16214
+ ? scope.count - clearings.length
16215
+ : TVL_HISTORY_PAGE_SIZE;
16216
+ const pageSize = Math.min(TVL_HISTORY_PAGE_SIZE, remaining);
16217
+ const page = yield request(poolTvlClearingsQuery, {
16218
+ poolId,
16219
+ pool: poolId,
16220
+ epochGte: String(gte),
16221
+ epochLt: lt === undefined ? NO_EPOCH_BOUND : String(lt),
16222
+ direction: scope.kind === 'newest' ? 'desc' : 'asc',
16223
+ first: pageSize,
16224
+ withSnapshot: first,
16225
+ }, cancellation.signal);
16226
+ requests += 1;
16227
+ if (first)
16228
+ snapshot = { pool: page.pool, meta: page.meta };
16229
+ const rows = page.clearings.map(parseClearing);
16230
+ clearings.push(...rows);
16231
+ const last = rows.at(-1);
16232
+ if (rows.length < pageSize || last === undefined)
16233
+ break;
16234
+ if (scope.kind === 'newest') {
16235
+ if (clearings.length >= scope.count)
16236
+ break;
16237
+ if (lt !== undefined && last.epochId >= lt) {
16238
+ throw new Error('getPoolTvlHistory: the clearings cursor did not advance');
16239
+ }
16240
+ lt = last.epochId;
16241
+ }
16242
+ else {
16243
+ if (last.epochId < gte) {
16244
+ throw new Error('getPoolTvlHistory: the clearings cursor did not advance');
16245
+ }
16246
+ gte = last.epochId + 1;
16247
+ }
16248
+ }
16249
+ clearings.sort((a, b) => a.epochId - b.epochId);
16250
+ return { clearings, snapshot, requests };
16251
+ });
16252
+ }
16253
+ /**
16254
+ * The pool's balance (display units, the subgraph's decimal string) at the end
16255
+ * of each block, `TVL_HISTORY_BLOCKS_PER_REQUEST` blocks per request. A block
16256
+ * at which the subgraph has no such pool THROWS: a finished clearing of the
16257
+ * pool happened in it, so a missing pool means a broken read, not a zero.
16258
+ */
16259
+ function fetchPoolBalancesAtBlocks(request, poolId, blocks, cancellation) {
16260
+ return __awaiter(this, void 0, void 0, function* () {
16261
+ const chunks = [];
16262
+ for (let i = 0; i < blocks.length; i += TVL_HISTORY_BLOCKS_PER_REQUEST) {
16263
+ chunks.push(blocks.slice(i, i + TVL_HISTORY_BLOCKS_PER_REQUEST));
16264
+ }
16265
+ cancellation.throwIfCancelled();
16266
+ const pages = yield Promise.all(chunks.map((chunk) => request(poolBalancesAtBlocksQuery(chunk), { poolId }, cancellation.signal).then((page) => ({ chunk, page }))));
16267
+ const balances = new Map();
16268
+ for (const { chunk, page } of pages) {
16269
+ for (const block of chunk) {
16270
+ const state = page[blockAlias(block)];
16271
+ if (!state) {
16272
+ throw new Error(`getPoolTvlHistory: the subgraph has no state for the pool at block ${String(block)}`);
16273
+ }
16274
+ balances.set(block, state.balance);
16275
+ }
16276
+ }
16277
+ return { balances, requests: chunks.length };
16278
+ });
16279
+ }
16280
+ const EXTERNAL_TVL_INTERFACE = new utils$2.Interface(KasuPoolExternalTVLAbi__factory.abi);
16281
+ function hasCode(code) {
16282
+ if (typeof code !== 'string' || !/^0x[0-9a-fA-F]*$/.test(code)) {
16283
+ throw new Error(`getPoolTvlHistory: getCode returned ${String(code)}, not hex`);
16284
+ }
16285
+ return code !== '0x';
16286
+ }
16287
+ /**
16288
+ * The real reader: each read goes to the history RPCs IN ORDER until one
16289
+ * answers, and the whole chain is retried `attempts` times with back-off.
16290
+ * After the last attempt the last error propagates — never a value. Each
16291
+ * attempt first checks the cancellation, so a timed-out call stops here.
16292
+ */
16293
+ function createOffChainTvlReader({ externalTvl, pool, rpcs, cancellation, attempts = TVL_HISTORY_RPC_ATTEMPTS, delayMs = TVL_HISTORY_RPC_RETRY_DELAY_MS, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), }) {
16294
+ if (rpcs.length === 0) {
16295
+ throw new Error('getPoolTvlHistory: no history RPC to read from');
16296
+ }
16297
+ const data = EXTERNAL_TVL_INTERFACE.encodeFunctionData('externalTVLOfPool', [pool]);
16298
+ const read = (op) => __awaiter(this, void 0, void 0, function* () {
16299
+ let lastError;
16300
+ for (let attempt = 1; attempt <= attempts; attempt++) {
16301
+ for (const rpc of rpcs) {
16302
+ cancellation.throwIfCancelled();
16303
+ try {
16304
+ return yield op(rpc);
16305
+ }
16306
+ catch (error) {
16307
+ lastError = error;
16308
+ }
16309
+ }
16310
+ if (attempt < attempts) {
16311
+ yield sleep(delayMs * Math.pow(2, (attempt - 1)));
16312
+ }
16313
+ }
16314
+ cancellation.throwIfCancelled();
16315
+ throw lastError;
16316
+ });
16317
+ return {
16318
+ readAt: (block) => read((rpc) => __awaiter(this, void 0, void 0, function* () {
16319
+ const result = yield rpc.call({ to: externalTvl, data }, block);
16320
+ const [value] = EXTERNAL_TVL_INTERFACE.decodeFunctionResult('externalTVLOfPool', result);
16321
+ return value;
16322
+ })),
16323
+ hasCodeAt: (block) => read((rpc) => __awaiter(this, void 0, void 0, function* () { return hasCode(yield rpc.getCode(externalTvl, block)); })),
16324
+ poolHasCodeAt: (block) => read((rpc) => __awaiter(this, void 0, void 0, function* () { return hasCode(yield rpc.getCode(pool, block)); })),
16325
+ };
16326
+ }
16327
+ /**
16328
+ * Index of the first block (of `blocks`, ascending) at which the contract has
16329
+ * code; `blocks.length` when it has code at none. A deployed contract keeps
16330
+ * its code (it is a proxy, never self-destructed), so the answer is a single
16331
+ * boundary and a binary search finds it: ONE `getCode` when the contract
16332
+ * already existed at the oldest clearing, about log2(n) otherwise.
16333
+ */
16334
+ function firstBlockWithCode(blocks, hasCodeAt) {
16335
+ return __awaiter(this, void 0, void 0, function* () {
16336
+ if (blocks.length === 0)
16337
+ return 0;
16338
+ if (yield hasCodeAt(blocks[0]))
16339
+ return 0;
16340
+ const last = blocks.length - 1;
16341
+ if (!(yield hasCodeAt(blocks[last])))
16342
+ return blocks.length;
16343
+ let lo = 0; // no code
16344
+ let hi = last; // code
16345
+ while (hi - lo > 1) {
16346
+ const mid = Math.floor((lo + hi) / 2);
16347
+ if (yield hasCodeAt(blocks[mid]))
16348
+ hi = mid;
16349
+ else
16350
+ lo = mid;
16351
+ }
16352
+ return hi;
16353
+ });
16354
+ }
16355
+ /**
16356
+ * `fn` over `items`, at most `limit` in flight; rejects on the first failure
16357
+ * and starts nothing after it (or after `beforeEach` throws).
16358
+ */
16359
+ function mapWithConcurrency(items_1, limit_1, fn_1) {
16360
+ return __awaiter(this, arguments, void 0, function* (items, limit, fn, beforeEach = () => undefined) {
16361
+ const results = new Array(items.length);
16362
+ let next = 0;
16363
+ let failed = false;
16364
+ const worker = () => __awaiter(this, void 0, void 0, function* () {
16365
+ while (!failed && next < items.length) {
16366
+ const index = next++;
16367
+ try {
16368
+ beforeEach();
16369
+ results[index] = yield fn(items[index]);
16370
+ }
16371
+ catch (error) {
16372
+ failed = true;
16373
+ throw error;
16374
+ }
16375
+ }
16376
+ });
16377
+ yield Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
16378
+ return results;
16379
+ });
16380
+ }
16381
+ /**
16382
+ * Immutable past-block reads, per SDK instance. Keys carry the contract (or
16383
+ * subgraph), the pool and the block, so two deployments never collide.
16384
+ */
16385
+ class PastBlockCache {
16386
+ constructor() {
16387
+ this._entries = new Map();
16388
+ }
16389
+ get(key) {
16390
+ return this._entries.get(key);
16391
+ }
16392
+ set(key, value) {
16393
+ if (this._entries.size >= TVL_HISTORY_CACHE_LIMIT) {
16394
+ this._entries.clear();
16395
+ }
16396
+ this._entries.set(key, value);
16397
+ }
16398
+ get size() {
16399
+ return this._entries.size;
16400
+ }
16401
+ }
16402
+ /**
16403
+ * The off-chain TVL (BASE units) at each block, and — with `withLatest` — at
16404
+ * the latest block, all inside ONE bounded pool of reads.
16405
+ *
16406
+ * Before the contract existed there was nothing to record, so those blocks
16407
+ * are 0 — decided by `getCode`, never by catching a revert: an RPC that
16408
+ * refuses past state can surface that refusal as a "call exception". And a
16409
+ * "no code" answer is believed only if the same provider DOES show code for
16410
+ * the pool at the oldest and newest such block (it cleared there); otherwise
16411
+ * `PastStateUnavailableError`. Every other failed read throws.
16412
+ *
16413
+ * `cached` blocks (final, below the head) are answered from `cache` and
16414
+ * stored there after a successful read.
16415
+ */
16416
+ function fetchOffChainTvlAtBlocks(reader_1, blocks_1, _a) {
16417
+ return __awaiter(this, arguments, void 0, function* (reader, blocks, { withLatest = false, cancellation, cache, cacheKey, cacheable = () => false, }) {
16418
+ let rpcReads = 0;
16419
+ const cached = (kind, block, load) => {
16420
+ const key = cache && cacheKey && cacheable(block)
16421
+ ? cacheKey(kind, block)
16422
+ : undefined;
16423
+ const hit = key === undefined ? undefined : cache === null || cache === void 0 ? void 0 : cache.get(key);
16424
+ if (hit !== undefined)
16425
+ return Promise.resolve(hit);
16426
+ cancellation.throwIfCancelled();
16427
+ rpcReads += 1;
16428
+ return load().then((value) => {
16429
+ if (key !== undefined)
16430
+ cache === null || cache === void 0 ? void 0 : cache.set(key, value);
16431
+ return value;
16432
+ });
16433
+ };
16434
+ const deployedFrom = yield firstBlockWithCode(blocks, (block) => cached('code', block, () => reader.hasCodeAt(block)));
16435
+ if (deployedFrom > 0) {
16436
+ for (const block of new Set([blocks[0], blocks[deployedFrom - 1]])) {
16437
+ const poolDeployed = yield cached('pool', block, () => reader.poolHasCodeAt(block));
16438
+ if (!poolDeployed)
16439
+ throw new PastStateUnavailableError(block);
16440
+ }
16441
+ }
16442
+ const offChain = new Map();
16443
+ for (const block of blocks.slice(0, deployedFrom)) {
16444
+ offChain.set(block, ethers.BigNumber.from(0));
16445
+ }
16446
+ const targets = [...blocks.slice(deployedFrom)];
16447
+ if (withLatest)
16448
+ targets.push('latest');
16449
+ const values = yield mapWithConcurrency(targets, TVL_HISTORY_RPC_CONCURRENCY, (block) => {
16450
+ if (block !== 'latest') {
16451
+ return cached('tvl', block, () => reader.readAt(block));
16452
+ }
16453
+ // Never cached: "latest" moves.
16454
+ rpcReads += 1;
16455
+ return reader.readAt('latest');
16456
+ }, () => {
16457
+ cancellation.throwIfCancelled();
16458
+ });
16459
+ let latest = null;
16460
+ targets.forEach((block, i) => {
16461
+ if (block === 'latest')
16462
+ latest = values[i];
16463
+ else
16464
+ offChain.set(block, values[i]);
16465
+ });
16466
+ return { offChain, latest, rpcReads };
16467
+ });
16468
+ }
16469
+ /**
16470
+ * Pure: the reads → the history. One `clearing` point per clearing, ordered by
16471
+ * the block it finished in (then by epoch, for clearings that shared a block),
16472
+ * so the series is ascending in TIME even where epoch numbers jump; then the
16473
+ * `current` point. Each point is `poolTvl(onChain, offChain)` — the same
16474
+ * function `getPoolOverview` uses for `totalValueLocked.total`.
16475
+ */
16476
+ function buildPoolTvlHistory(read) {
16477
+ const offChainDisplay = (value) => utils$2.formatUnits(value, read.decimals);
16478
+ const entries = [...read.clearings]
16479
+ .sort((a, b) => a.block - b.block || a.epochId - b.epochId)
16480
+ .map((clearing) => {
16481
+ const balance = read.balances.get(clearing.block);
16482
+ const offChain = read.offChain.get(clearing.block);
16483
+ if (balance === undefined || offChain === undefined) {
16484
+ throw new Error(`getPoolTvlHistory: no reading at block ${String(clearing.block)}`);
16485
+ }
16486
+ return {
16487
+ epochId: clearing.epochId,
16488
+ timestamp: clearing.timestamp,
16489
+ tvl: poolTvl(balance, offChainDisplay(offChain)),
16490
+ kind: 'clearing',
16491
+ };
16492
+ });
16493
+ if (read.current) {
16494
+ entries.push({
16495
+ epochId: read.current.epochId,
16496
+ timestamp: read.current.timestamp,
16497
+ tvl: poolTvl(read.current.balance, offChainDisplay(read.current.offChain)),
16498
+ kind: 'current',
16499
+ });
16500
+ }
16501
+ return entries;
16502
+ }
16503
+ /** Ascending, de-duplicated clearing blocks: each past block is read ONCE. */
16504
+ function distinctBlocks(clearings) {
16505
+ return [...new Set(clearings.map((c) => c.block))].sort((a, b) => a - b);
16506
+ }
16507
+
16508
+ /** Thrown by `getPoolTvlHistory` when no provider that serves past state is configured. */
16509
+ const NO_HISTORY_PROVIDER_MESSAGE = 'getPoolTvlHistory: no history provider; pass historyProvider or set historyRpcUrls to RPCs that serve past state';
15912
16510
  class DataService {
15913
- constructor(_kasuConfig, signerOrProvider) {
16511
+ /**
16512
+ * @param historyProvider - a provider that SERVES PAST STATE (an archive
16513
+ * RPC), used only by `getPoolTvlHistory` for the off-chain TVL at old
16514
+ * blocks. Without one, providers are built on `SdkConfig.historyRpcUrls`
16515
+ * (tried in order); with neither, that one call throws
16516
+ * `NO_HISTORY_PROVIDER_MESSAGE`. Never the default provider: a pruned
16517
+ * node refuses old blocks.
16518
+ */
16519
+ constructor(_kasuConfig, signerOrProvider, historyProvider) {
15914
16520
  this._kasuConfig = _kasuConfig;
16521
+ /** Immutable past-block reads, kept for the life of this instance. */
16522
+ this._tvlHistoryCache = new PastBlockCache();
15915
16523
  this._externalTvlAbi = KasuPoolExternalTVLAbi__factory.connect(_kasuConfig.contracts.ExternalTVL, signerOrProvider);
16524
+ this._systemVariablesAbi = ISystemVariablesAbi__factory.connect(_kasuConfig.contracts.SystemVariables, signerOrProvider);
15916
16525
  this._graph = new GraphQLClient(_kasuConfig.subgraphUrl);
16526
+ this._explicitHistoryProvider = historyProvider;
15917
16527
  this._directus = createDirectusClient(_kasuConfig.directusUrl);
15918
16528
  }
15919
16529
  getUrlFromFile(fileName) {
@@ -15993,6 +16603,194 @@ class DataService {
15993
16603
  return tvlMap;
15994
16604
  });
15995
16605
  }
16606
+ /**
16607
+ * The pool's TVL — the total lent into it by everyone — at every cycle
16608
+ * boundary, then as of now: the data behind a TVL chart.
16609
+ *
16610
+ * One `clearing` point per FINISHED clearing, read at the block it
16611
+ * finished in (`LendingPoolClearing.endBlockNumber`), then one `current`
16612
+ * point. Every point is `poolTvl(onChain, offChain)`, the definition
16613
+ * `getPoolOverview` uses for `totalValueLocked.total`:
16614
+ *
16615
+ * - on-chain: `lendingPool.balance` from the subgraph, read with
16616
+ * time-travel queries (`block: { number }`);
16617
+ * - off-chain: `ExternalTVL.externalTVLOfPool(pool)` at the same block,
16618
+ * through the HISTORY providers (see the constructor). 0 before the
16619
+ * contract was deployed (decided by `getCode`, and only if the same
16620
+ * provider shows the pool's own code there); any failed read throws.
16621
+ *
16622
+ * The `current` point mixes two sources: the subgraph's latest balance
16623
+ * (and its latest block time) and `externalTVLOfPool` at `latest` on the
16624
+ * history provider. `getPoolOverview` reads the same two, the off-chain
16625
+ * one on the default provider, so the last point equals
16626
+ * `totalValueLocked.total` when both reads succeed. They can differ when
16627
+ * `getPoolOverview`'s off-chain read FAILS — it then falls back to 0 —
16628
+ * or when the figure moves between the two calls.
16629
+ *
16630
+ * Units: `tvl` in display units of the deployment's own stable token
16631
+ * (USDC on Base and XDC USDC, AUDD on XDC AUDD); `timestamp` in unix
16632
+ * seconds. Ascending in time. See `PoolTvlHistoryEntry`.
16633
+ *
16634
+ * Cost: one subgraph request for the clearings (per thousand), one per
16635
+ * hundred distinct uncached clearing blocks, a `currentEpochNumber` read;
16636
+ * then, on the history providers, one `externalTVLOfPool` per distinct
16637
+ * uncached block plus the latest, and one to about log2(n) `getCode`s —
16638
+ * at most `TVL_HISTORY_RPC_CONCURRENCY` in flight. Final blocks are
16639
+ * cached on this instance, so a second call reads only what is new.
16640
+ * `timeoutMs` (default 30 s) bounds the whole call.
16641
+ *
16642
+ * `[]` for a pool the subgraph does not know, for a window with no epoch
16643
+ * in it, and on a LITE deployment whose subgraph lacks the entities. On a
16644
+ * full deployment that schema error throws, like every network, indexing
16645
+ * or past-state failure.
16646
+ *
16647
+ * @throws RangeError / TypeError on a malformed option or a pool id that
16648
+ * is not an address, and `NO_HISTORY_PROVIDER_MESSAGE` when the
16649
+ * deployment has an `ExternalTVL` contract but no history provider — all
16650
+ * before any network read. `PoolTvlHistoryTimeoutError` /
16651
+ * `PoolTvlHistoryAbortedError` on the deadline or the signal,
16652
+ * `PastStateUnavailableError` from a pruned history provider.
16653
+ */
16654
+ getPoolTvlHistory(poolId_1) {
16655
+ return __awaiter(this, arguments, void 0, function* (poolId, options = {}) {
16656
+ var _a;
16657
+ validatePoolTvlHistoryOptions(options);
16658
+ if (!utils$2.isAddress(poolId)) {
16659
+ throw new TypeError(`getPoolTvlHistory: poolId must be an address, got ${String(poolId)}`);
16660
+ }
16661
+ const pool = poolId.toLowerCase();
16662
+ const externalTvl = this._externalTvlAddress();
16663
+ const rpcs = externalTvl ? this._historyRpcs() : [];
16664
+ if (externalTvl && rpcs.length === 0) {
16665
+ throw new Error(NO_HISTORY_PROVIDER_MESSAGE);
16666
+ }
16667
+ return yield runCancellable((_a = options.timeoutMs) !== null && _a !== void 0 ? _a : TVL_HISTORY_DEFAULT_TIMEOUT_MS, options.signal, (cancellation) => this._readPoolTvlHistory(pool, options, externalTvl
16668
+ ? this._createOffChainTvlReader({
16669
+ externalTvl,
16670
+ pool,
16671
+ rpcs,
16672
+ cancellation,
16673
+ })
16674
+ : null, externalTvl !== null && externalTvl !== void 0 ? externalTvl : '', cancellation));
16675
+ });
16676
+ }
16677
+ _readPoolTvlHistory(pool, options, reader, externalTvl, cancellation) {
16678
+ return __awaiter(this, void 0, void 0, function* () {
16679
+ var _a, _b, _c;
16680
+ const graph = (document, variables, signal) => this._graph.request({ document, variables, signal });
16681
+ const scope = options.fromEpoch === undefined && options.count !== undefined
16682
+ ? { kind: 'newest', count: options.count }
16683
+ : { kind: 'from', fromEpoch: (_a = options.fromEpoch) !== null && _a !== void 0 ? _a : 0 };
16684
+ try {
16685
+ const [currentEpochNumber, { clearings, snapshot }] = yield Promise.all([
16686
+ this._systemVariablesAbi.currentEpochNumber(),
16687
+ fetchPoolClearings(graph, pool, scope, cancellation),
16688
+ ]);
16689
+ const currentEpoch = currentEpochNumber.toNumber();
16690
+ const window = resolvePoolTvlHistoryWindow(options, currentEpoch);
16691
+ const poolNow = snapshot.pool;
16692
+ if (!window || !poolNow)
16693
+ return [];
16694
+ const head = (_b = snapshot.meta) === null || _b === void 0 ? void 0 : _b.block;
16695
+ if (!head || typeof head.timestamp !== 'number') {
16696
+ throw new Error('getPoolTvlHistory: the subgraph returned no latest block time');
16697
+ }
16698
+ const inWindow = clearings.filter((c) => c.epochId >= window.fromEpoch &&
16699
+ c.epochId <= window.toEpoch);
16700
+ const blocks = distinctBlocks(inWindow);
16701
+ const withCurrent = window.toEpoch >= currentEpoch;
16702
+ const cacheable = (block) => block <= head.number - TVL_HISTORY_CACHE_DEPTH;
16703
+ const [balances, offChainRead] = yield Promise.all([
16704
+ this._balancesAtBlocks(graph, pool, blocks, cacheable, cancellation),
16705
+ reader
16706
+ ? fetchOffChainTvlAtBlocks(reader, blocks, {
16707
+ withLatest: withCurrent,
16708
+ cancellation,
16709
+ cache: this._tvlHistoryCache,
16710
+ cacheKey: (kind, block) => `${kind}|${externalTvl}|${pool}|${String(block)}`,
16711
+ cacheable,
16712
+ })
16713
+ : {
16714
+ offChain: new Map(blocks.map((b) => [b, ethers.BigNumber.from(0)])),
16715
+ latest: null,
16716
+ },
16717
+ ]);
16718
+ return buildPoolTvlHistory({
16719
+ clearings: inWindow,
16720
+ balances,
16721
+ offChain: offChainRead.offChain,
16722
+ current: withCurrent
16723
+ ? {
16724
+ epochId: currentEpoch,
16725
+ timestamp: head.timestamp,
16726
+ balance: poolNow.balance,
16727
+ offChain: (_c = offChainRead.latest) !== null && _c !== void 0 ? _c : ethers.BigNumber.from(0),
16728
+ }
16729
+ : null,
16730
+ decimals: this._kasuConfig.stableAssetDecimals,
16731
+ });
16732
+ }
16733
+ catch (error) {
16734
+ if (isEmptyHistoryError(error, this._kasuConfig.isLiteDeployment)) {
16735
+ return [];
16736
+ }
16737
+ throw error;
16738
+ }
16739
+ });
16740
+ }
16741
+ /** The pool's balance at each block: cached final blocks, then one batch. */
16742
+ _balancesAtBlocks(graph, pool, blocks, cacheable, cancellation) {
16743
+ return __awaiter(this, void 0, void 0, function* () {
16744
+ const key = (block) => `balance|${this._kasuConfig.subgraphUrl}|${pool}|${String(block)}`;
16745
+ const balances = new Map();
16746
+ const missing = [];
16747
+ for (const block of blocks) {
16748
+ const hit = cacheable(block)
16749
+ ? this._tvlHistoryCache.get(key(block))
16750
+ : undefined;
16751
+ if (hit === undefined)
16752
+ missing.push(block);
16753
+ else
16754
+ balances.set(block, hit);
16755
+ }
16756
+ if (missing.length > 0) {
16757
+ const read = yield fetchPoolBalancesAtBlocks(graph, pool, missing, cancellation);
16758
+ for (const [block, balance] of read.balances) {
16759
+ balances.set(block, balance);
16760
+ if (cacheable(block))
16761
+ this._tvlHistoryCache.set(key(block), balance);
16762
+ }
16763
+ }
16764
+ return balances;
16765
+ });
16766
+ }
16767
+ /** `null` when the deployment has no `ExternalTVL` contract. */
16768
+ _externalTvlAddress() {
16769
+ const address = this._kasuConfig.contracts.ExternalTVL;
16770
+ if (!address || /^0x0{40}$/i.test(address))
16771
+ return null;
16772
+ return address.toLowerCase();
16773
+ }
16774
+ /** Seam for tests: the real reader over the history RPCs. */
16775
+ _createOffChainTvlReader(args) {
16776
+ return createOffChainTvlReader(args);
16777
+ }
16778
+ /**
16779
+ * The history RPCs, in fallback order: the explicit provider, else ONE
16780
+ * provider per `historyRpcUrls` entry — built here and nowhere else,
16781
+ * pinned to `SdkConfig.chainId` when known, each with a per-call timeout.
16782
+ * Built once per instance.
16783
+ */
16784
+ _historyRpcs() {
16785
+ if (this._explicitHistoryProvider)
16786
+ return [this._explicitHistoryProvider];
16787
+ if (!this._builtHistoryProviders) {
16788
+ // Through the `ethers` namespace, so the consumer's own build
16789
+ // (Node or browser) is used — see `defaultProvider` in the facade.
16790
+ this._builtHistoryProviders = this._kasuConfig.historyRpcUrls.map((url) => new ethers.providers.StaticJsonRpcProvider({ url, timeout: TVL_HISTORY_RPC_TIMEOUT_MS }, this._kasuConfig.chainId));
16791
+ }
16792
+ return this._builtHistoryProviders;
16793
+ }
15996
16794
  getPoolOverview(epochId, id_in) {
15997
16795
  return __awaiter(this, void 0, void 0, function* () {
15998
16796
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
@@ -16133,8 +16931,8 @@ class DataService {
16133
16931
  subheading: lendingPoolDirectus === null || lendingPoolDirectus === void 0 ? void 0 : lendingPoolDirectus.subheading,
16134
16932
  operatingSince: lendingPoolDirectus === null || lendingPoolDirectus === void 0 ? void 0 : lendingPoolDirectus.operatingSince,
16135
16933
  totalValueLocked: {
16136
- total: (parseFloat(lendingPoolSubgraph.balance) +
16137
- parseFloat(offChainTvl)).toString(),
16934
+ // The one TVL definition, shared with getPoolTvlHistory.
16935
+ total: poolTvl(lendingPoolSubgraph.balance, offChainTvl).toString(),
16138
16936
  offchain: offChainTvl,
16139
16937
  },
16140
16938
  loansUnderManagement: (_q = lendingPoolDirectus === null || lendingPoolDirectus === void 0 ? void 0 : lendingPoolDirectus.loansUnderManagement) !== null && _q !== void 0 ? _q : '0',
@@ -17217,14 +18015,13 @@ const currentEpochFtdAmountQuery = gql `
17217
18015
  user: $userId
17218
18016
  lendingPool_in: $poolIds
17219
18017
  epochId: $currentEpoch
18018
+ type: DepositRequest
17220
18019
  }
17221
18020
  ) {
17222
18021
  fixedTermConfigId
17223
18022
  amountRequested
17224
18023
  tranche {
17225
18024
  id
17226
- balance
17227
- shares
17228
18025
  }
17229
18026
  }
17230
18027
  }
@@ -17239,11 +18036,7 @@ const currentFtdBalanceQuery = gql `
17239
18036
  configId
17240
18037
  lendingPoolTranche {
17241
18038
  id
17242
- }
17243
- }
17244
- lendingPool {
17245
- balance
17246
- tranches {
18039
+ balance
17247
18040
  shares
17248
18041
  }
17249
18042
  }
@@ -17251,6 +18044,13 @@ const currentFtdBalanceQuery = gql `
17251
18044
  }
17252
18045
  `;
17253
18046
 
18047
+ /** Thrown when the subgraph reports a request type with no known unit. */
18048
+ const UNKNOWN_REQUEST_TYPE_MESSAGE = 'UserLending: unknown request type, its amount unit is unknown';
18049
+ const KNOWN_REQUEST_TYPES = [
18050
+ 'DepositRequest',
18051
+ 'WithdrawalRequest',
18052
+ ];
18053
+ const isKnownRequestType = (type) => typeof type === 'string' && KNOWN_REQUEST_TYPES.includes(type);
17254
18054
  class UserLending {
17255
18055
  constructor(_kasuConfig, signerOrProvider) {
17256
18056
  this._kasuConfig = _kasuConfig;
@@ -17412,6 +18212,14 @@ class UserLending {
17412
18212
  ]);
17413
18213
  const retn = [];
17414
18214
  for (const userRequest of subgraphResult.userRequests) {
18215
+ // A request type this SDK does not know has no known amount unit
18216
+ // (the schema also declares types no live deployment emits yet).
18217
+ // Skip that ONE row before anything is priced or read on chain,
18218
+ // rather than failing — and blanking — the lender's whole list.
18219
+ if (!isKnownRequestType(userRequest.type)) {
18220
+ console.error(`${UNKNOWN_REQUEST_TYPE_MESSAGE}: ${String(userRequest.type)} (request ${userRequest.id}); row skipped`);
18221
+ continue;
18222
+ }
17415
18223
  const directusPool = directusResult.find(({ id }) => id === userRequest.lendingPool.id);
17416
18224
  const trancheName = trancheNames[userRequest.lendingPool.tranches.length - 1][parseInt(userRequest.tranche.orderId)];
17417
18225
  const events = [];
@@ -17507,9 +18315,7 @@ class UserLending {
17507
18315
  : 'Withdrawal',
17508
18316
  trancheName: trancheName,
17509
18317
  trancheId: userRequest.tranche.id,
17510
- requestedAmount: utils$2.parseUnits(userRequest.tranche.shares).isZero()
17511
- ? userRequest.amountRequested
17512
- : this.convertSharesToAssets(userRequest.amountRequested, userRequest.tranche.balance, userRequest.tranche.shares).toString(),
18318
+ requestedAmount: this.requestedAmountInAssets(userRequest),
17513
18319
  acceptedAmount: userRequest.amountAccepted,
17514
18320
  rejectedAmount: userRequest.amountRejected,
17515
18321
  status: userRequest.status,
@@ -17542,8 +18348,11 @@ class UserLending {
17542
18348
  for (const userFTD of currentFtdBalance.userLendingPoolTrancheFixedTermDepositLocks) {
17543
18349
  const ftdID = parseFloat(userFTD.lendingPoolTrancheFixedTermConfig.configId);
17544
18350
  const trancheID = userFTD.lendingPoolTrancheFixedTermConfig.lendingPoolTranche.id.toLowerCase();
17545
- const totalSupply = userFTD.lendingPool.tranches.reduce((total, cur) => total.add(utils$2.parseEther(cur.shares)), ethers.BigNumber.from(0));
17546
- const amount = this.convertSharesToAssets(userFTD.trancheShares, userFTD.lendingPool.balance, utils$2.formatEther(totalSupply));
18351
+ // The lock holds shares of ONE tranche, so they are worth that
18352
+ // tranche's share price — not the pool's average across tranches,
18353
+ // which differs whenever the tranches have earned differently.
18354
+ const lockTranche = userFTD.lendingPoolTrancheFixedTermConfig.lendingPoolTranche;
18355
+ const amount = this.convertSharesToAssets(userFTD.trancheShares, lockTranche.balance, lockTranche.shares);
17547
18356
  const item = (_a = trancheMap.get(trancheID)) !== null && _a !== void 0 ? _a : [];
17548
18357
  if (item[ftdID]) {
17549
18358
  item[ftdID] += parseFloat(amount);
@@ -17558,25 +18367,40 @@ class UserLending {
17558
18367
  }
17559
18368
  getCurrentEpochFtdAmount(poolIds, userId, currentEpoch) {
17560
18369
  return __awaiter(this, void 0, void 0, function* () {
18370
+ var _a, _b;
17561
18371
  const currentEpochFtdAmountRes = yield this._graph.request(currentEpochFtdAmountQuery, {
17562
18372
  userId: userId.toLowerCase(),
17563
18373
  poolIds: poolIds.map((id) => id.toLowerCase()),
17564
18374
  currentEpoch: parseFloat(currentEpoch),
17565
18375
  });
17566
- const currentEpochFtdAmounts = new Map();
18376
+ // Deposit requests only (the query filters on type): a deposit's
18377
+ // `amountRequested` is already an ASSET amount, so it is summed as it
18378
+ // is — never put through a share price. Summed, not assigned: a
18379
+ // cancelled request and its replacement share a tranche and a config
18380
+ // in one epoch, and the cancelled one's 0 must not overwrite the live
18381
+ // amount.
18382
+ const totals = new Map();
17567
18383
  for (const userRequest of currentEpochFtdAmountRes.userRequests) {
17568
18384
  const ftdID = parseFloat(userRequest.fixedTermConfigId);
17569
- const item = currentEpochFtdAmounts.get(userRequest.tranche.id);
17570
- const amountRequested = this.convertSharesToAssets(userRequest.amountRequested, userRequest.tranche.balance, userRequest.tranche.shares);
17571
- if (item) {
17572
- const clonedItem = [...item];
17573
- clonedItem[ftdID] = amountRequested;
17574
- currentEpochFtdAmounts.set(userRequest.tranche.id, clonedItem);
18385
+ if (!Number.isInteger(ftdID) || ftdID < 0)
17575
18386
  continue;
17576
- }
18387
+ const perConfig = (_a = totals.get(userRequest.tranche.id)) !== null && _a !== void 0 ? _a : new Map();
18388
+ perConfig.set(ftdID, ((_b = perConfig.get(ftdID)) !== null && _b !== void 0 ? _b : ethers.BigNumber.from(0)).add(utils$2.parseUnits(userRequest.amountRequested)));
18389
+ totals.set(userRequest.tranche.id, perConfig);
18390
+ }
18391
+ // A slot whose total is zero is left out — `'0.0'` is a truthy
18392
+ // string, and a caller testing the slot would read it as an amount.
18393
+ // A tranche with no non-zero slot is left out entirely.
18394
+ const currentEpochFtdAmounts = new Map();
18395
+ for (const [trancheId, perConfig] of totals) {
17577
18396
  const ftdAmounts = [];
17578
- ftdAmounts[ftdID] = amountRequested;
17579
- currentEpochFtdAmounts.set(userRequest.tranche.id, ftdAmounts);
18397
+ for (const [ftdID, total] of perConfig) {
18398
+ if (total.gt(0))
18399
+ ftdAmounts[ftdID] = utils$2.formatUnits(total);
18400
+ }
18401
+ if (ftdAmounts.length) {
18402
+ currentEpochFtdAmounts.set(trancheId, ftdAmounts);
18403
+ }
17580
18404
  }
17581
18405
  return currentEpochFtdAmounts;
17582
18406
  });
@@ -17597,6 +18421,31 @@ class UserLending {
17597
18421
  return amountMap;
17598
18422
  });
17599
18423
  }
18424
+ /**
18425
+ * A request's outstanding amount in ASSETS (the deployment's stable token).
18426
+ *
18427
+ * The subgraph's `UserRequest.amountRequested` is denominated by request
18428
+ * type. A DEPOSIT request holds assets: the deposit NFT's `assetAmount`,
18429
+ * top-ups added, accepted and rejected amounts taken off. A WITHDRAWAL
18430
+ * request holds tranche SHARES: the withdrawal NFT's `sharesAmount`,
18431
+ * accepted shares taken off. So only a withdrawal is converted, at the
18432
+ * tranche's current share price; a deposit passes through unchanged —
18433
+ * pricing it as shares overstated it by the share price.
18434
+ */
18435
+ requestedAmountInAssets(userRequest) {
18436
+ switch (userRequest.type) {
18437
+ case 'DepositRequest':
18438
+ return userRequest.amountRequested;
18439
+ case 'WithdrawalRequest':
18440
+ return this.convertSharesToAssets(userRequest.amountRequested, userRequest.tranche.balance, userRequest.tranche.shares);
18441
+ default: {
18442
+ // Fail closed: a type this SDK does not know has no known
18443
+ // unit, and pricing it either way would print a wrong figure.
18444
+ const unknownType = userRequest.type;
18445
+ throw new Error(`${UNKNOWN_REQUEST_TYPE_MESSAGE}: ${unknownType}`);
18446
+ }
18447
+ }
18448
+ }
17600
18449
  convertSharesToAssets(sharesAmount, totalAssets, totalSupply) {
17601
18450
  if (utils$2.parseUnits(totalSupply).isZero())
17602
18451
  return '0';
@@ -18295,35 +19144,6 @@ function buildYieldHistory(read, window, clock) {
18295
19144
  a.poolId.localeCompare(b.poolId) ||
18296
19145
  a.trancheId.localeCompare(b.trancheId));
18297
19146
  }
18298
- /**
18299
- * A GraphQL VALIDATION error naming an entity, field or argument this
18300
- * deployment's subgraph does not have. A network error, a timeout or an
18301
- * indexing error is not one.
18302
- */
18303
- function isMissingEntityError(error) {
18304
- if (typeof error !== 'object' || error === null)
18305
- return false;
18306
- const response = error.response;
18307
- const errors = response === null || response === void 0 ? void 0 : response.errors;
18308
- if (!Array.isArray(errors) || errors.length === 0)
18309
- return false;
18310
- return errors.every((e) => {
18311
- const message = e === null || e === void 0 ? void 0 : e.message;
18312
- return (typeof message === 'string' &&
18313
- /has no field|cannot query field|unknown (type|field|argument)|no such (type|field)/i.test(message));
18314
- });
18315
- }
18316
- /**
18317
- * The only failure `getYieldHistory` turns into `[]`: a missing-schema error
18318
- * on a LITE deployment, whose subgraph may predate the entities. On a full
18319
- * deployment the entities are part of the schema, so the same error means a
18320
- * broken query or subgraph and THROWS — an empty chart there would hide it.
18321
- * Everything else (network, timeout, indexing) throws on every deployment, so
18322
- * a consumer can tell "no history here" from "history unavailable".
18323
- */
18324
- function isEmptyHistoryError(error, liteDeployment) {
18325
- return liteDeployment && isMissingEntityError(error);
18326
- }
18327
19147
 
18328
19148
  class Portfolio {
18329
19149
  constructor(_kasuConfig, signerOrProvider) {
@@ -24767,11 +25587,11 @@ class Swapper {
24767
25587
  }
24768
25588
 
24769
25589
  class KasuSdk {
24770
- constructor(config, signerOrProvider) {
25590
+ constructor(config, signerOrProvider, options = {}) {
24771
25591
  this._graphClient = new GraphQLClient(config.subgraphUrl);
24772
25592
  this.Locking = new KSULocking(config, signerOrProvider);
24773
25593
  this.UserLending = new UserLending(config, signerOrProvider);
24774
- this.DataService = new DataService(config, signerOrProvider);
25594
+ this.DataService = new DataService(config, signerOrProvider, options.historyProvider);
24775
25595
  this.Portfolio = new Portfolio(config, signerOrProvider);
24776
25596
  this.Swapper = new Swapper();
24777
25597
  }
@@ -24779,7 +25599,7 @@ class KasuSdk {
24779
25599
 
24780
25600
  class SdkConfig {
24781
25601
  constructor(options) {
24782
- var _a, _b, _c, _d;
25602
+ var _a, _b, _c, _d, _e;
24783
25603
  this.subgraphUrl = options.subgraphUrl;
24784
25604
  this.contracts = options.contracts;
24785
25605
  this.directusUrl = (_a = options.directusUrl) !== null && _a !== void 0 ? _a : '';
@@ -24794,6 +25614,8 @@ class SdkConfig {
24794
25614
  this.isLiteDeployment = (_b = options.isLiteDeployment) !== null && _b !== void 0 ? _b : false;
24795
25615
  this.poolMetadataMapping = (_c = options.poolMetadataMapping) !== null && _c !== void 0 ? _c : {};
24796
25616
  this.stableAssetDecimals = (_d = options.stableAssetDecimals) !== null && _d !== void 0 ? _d : 6;
25617
+ this.historyRpcUrls = [...((_e = options.historyRpcUrls) !== null && _e !== void 0 ? _e : [])];
25618
+ this.chainId = options.chainId;
24797
25619
  }
24798
25620
  }
24799
25621
 
@@ -25100,6 +25922,27 @@ const XDC_RPC_URLS = [
25100
25922
  'https://rpc.xinfin.network',
25101
25923
  'https://rpc.primenumbers.xyz/',
25102
25924
  ];
25925
+ /**
25926
+ * XDC RPCs that serve PAST state, for `getPoolTvlHistory` (each verified
25927
+ * with `externalTVLOfPool` at old blocks, CORS `*`), in fallback order. Not
25928
+ * `rpc.xdcrpc.com`, which refuses older blocks, and never `rpc.xdc.org` /
25929
+ * `erpc.xdc.org`. Public and rate-limited.
25930
+ */
25931
+ const XDC_HISTORY_RPC_URLS = [
25932
+ 'https://rpc.primenumbers.xyz/',
25933
+ 'https://rpc.ankr.com/xdc',
25934
+ ];
25935
+ /**
25936
+ * Base RPCs that serve PAST state, in fallback order (each verified with
25937
+ * `externalTVLOfPool` at old blocks; all answer CORS). Not
25938
+ * `base-rpc.publicnode.com`, the default read RPC, which refuses old state.
25939
+ * Public and rate-limited.
25940
+ */
25941
+ const BASE_HISTORY_RPC_URLS = [
25942
+ 'https://mainnet.base.org',
25943
+ 'https://base.drpc.org',
25944
+ 'https://1rpc.io/base',
25945
+ ];
25103
25946
  /**
25104
25947
  * Built-in chain configurations for Kasu-supported networks.
25105
25948
  *
@@ -25143,6 +25986,7 @@ const CHAIN_CONFIGS = {
25143
25986
  'https://base-rpc.publicnode.com',
25144
25987
  'https://mainnet.base.org',
25145
25988
  ],
25989
+ historyRpcUrls: BASE_HISTORY_RPC_URLS,
25146
25990
  blockExplorerUrl: 'https://basescan.org',
25147
25991
  },
25148
25992
  xdc: {
@@ -25179,6 +26023,7 @@ const CHAIN_CONFIGS = {
25179
26023
  currencyCode: 'AUD',
25180
26024
  },
25181
26025
  rpcUrls: XDC_RPC_URLS,
26026
+ historyRpcUrls: XDC_HISTORY_RPC_URLS,
25182
26027
  blockExplorerUrl: 'https://xdcscan.com',
25183
26028
  },
25184
26029
  'xdc-usdc': {
@@ -25211,6 +26056,7 @@ const CHAIN_CONFIGS = {
25211
26056
  currencyCode: 'USD',
25212
26057
  },
25213
26058
  rpcUrls: XDC_RPC_URLS,
26059
+ historyRpcUrls: XDC_HISTORY_RPC_URLS,
25214
26060
  blockExplorerUrl: 'https://xdcscan.com',
25215
26061
  },
25216
26062
  plume: {
@@ -28322,6 +29168,28 @@ class StrategiesFacade {
28322
29168
  return yield this._dataService.getPerformanceFee();
28323
29169
  });
28324
29170
  }
29171
+ /**
29172
+ * The strategy's TVL (total lent by everyone) at every cycle boundary,
29173
+ * then as of now — the data behind a TVL chart. Ascending in time; the
29174
+ * last point (`kind: 'current'`) equals `strategy.tvl.total` when both
29175
+ * calls' reads succeed (see `PoolTvlHistoryEntry`).
29176
+ *
29177
+ * `tvl` is in display units of the deployment's OWN stable token (USDC on
29178
+ * Base and XDC USDC, AUDD on XDC AUDD); `timestamp` is unix SECONDS.
29179
+ * `{ count }` = the newest `count` epochs, `{ fromEpoch }` = from that
29180
+ * epoch on; `timeoutMs` (default 30 s) and `signal` bound the call. A
29181
+ * malformed option throws before any network read. See
29182
+ * `DataService.getPoolTvlHistory` for the reads, the cost and the errors.
29183
+ *
29184
+ * ```ts
29185
+ * const points = await kasu.strategies.getPoolTvlHistory(poolId, { count: 52 });
29186
+ * ```
29187
+ */
29188
+ getPoolTvlHistory(poolId_1) {
29189
+ return __awaiter(this, arguments, void 0, function* (poolId, options = {}) {
29190
+ return yield this._dataService.getPoolTvlHistory(poolId, options);
29191
+ });
29192
+ }
28325
29193
  /**
28326
29194
  * Fetch a single strategy by pool ID. Returns `null` if not found.
28327
29195
  */
@@ -28646,17 +29514,24 @@ function cancelDeadline(request, nextEpochStart, nowSeconds) {
28646
29514
  const isCancelled = (events) => events.some((e) => e.requestType === 'Cancelled');
28647
29515
  const isForced = (events) => events.some((e) => e.requestType === 'Forced');
28648
29516
  const initiatedEvent = (events) => events.find((e) => e.requestType === 'Initiated');
28649
- /**
28650
- * Subgraph behaviour: when a request is cancelled, the on-request
28651
- * `requestedAmount` field is reset to 0 (the lender's effective balance is
28652
- * restored). The original amount survives on the `Initiated` event's
28653
- * `assetAmount`. Recover from there so cancelled rows still carry the amount
28654
- * the lender originally asked for.
28655
- */
29517
+ /** `assetAmount` of the `Initiated` event, or 0 when there is none. */
28656
29518
  const initiatedAmountOf = (events) => {
28657
29519
  const initiated = initiatedEvent(events);
28658
29520
  return initiated ? Number(initiated.assetAmount || '0') : 0;
28659
29521
  };
29522
+ /**
29523
+ * Subgraph behaviour: when a request is cancelled, the on-request
29524
+ * `requestedAmount` field is reset to 0 (the lender's effective balance is
29525
+ * restored). The amount cancelled survives on the `Cancelled` event's
29526
+ * `assetAmount` — everything still outstanding at the cancel, top-ups
29527
+ * included. Only when that event carries no figure does this fall back to the
29528
+ * `Initiated` event, which holds the FIRST submission alone and so misses
29529
+ * every top-up.
29530
+ */
29531
+ const cancelledAmountOf = (events) => {
29532
+ const cancelledEvent = events.find((e) => e.requestType === 'Cancelled');
29533
+ return num(cancelledEvent === null || cancelledEvent === void 0 ? void 0 : cancelledEvent.assetAmount) || initiatedAmountOf(events);
29534
+ };
28660
29535
  /**
28661
29536
  * A deposit is REALLOCATED when the timeline carries a `Reallocated` event, or
28662
29537
  * an `Accepted` event into a tranche other than the one requested.
@@ -28681,17 +29556,15 @@ const numOrNull = (str) => {
28681
29556
  * Convert a `UserRequest` into a `RequestState`. Pure — no clock, no network,
28682
29557
  * no copy.
28683
29558
  *
28684
- * BRANCH ORDER (this IS the behaviour; it reproduces kasu-ui's
28685
- * `deriveTransactionView` check for check):
29559
+ * BRANCH ORDER (this IS the behaviour):
28686
29560
  *
28687
29561
  * 1. a `Cancelled` event → `cancelled`, neutral, 0
28688
29562
  * 2. a withdrawal with a `Forced` event → `forced`, outflow, −accepted
28689
29563
  * 3. a reallocated deposit → `reallocated`, inflow, +accepted
28690
- * 4. a withdrawal partly filled with a LIVE
28691
- * remainder (cycle still open) → `partial`, outflow, −accepted
29564
+ * 4. a withdrawal partly filled (paid > 0) with
29565
+ * a LIVE remainder (queued > 0, cycle open) → `partial`, outflow, −accepted
28692
29566
  * 5. resolved (`status === 'Processed'`):
28693
- * withdrawal, partly filled → `partial`, outflow, −accepted
28694
- * withdrawal, fully filled → `complete`, outflow, −accepted
29567
+ * withdrawal (nothing left queued) → `complete`, outflow, −accepted
28695
29568
  * deposit, nothing accepted → `rejected`, neutral, 0
28696
29569
  * deposit, part rejected → `partial`, inflow, +accepted
28697
29570
  * deposit, fully accepted → `complete`, inflow, +accepted
@@ -28719,10 +29592,10 @@ function deriveRequestState(request) {
28719
29592
  const isWithdrawal = request.requestType === 'Withdrawal';
28720
29593
  const cancelled = isCancelled(request.events);
28721
29594
  // Cancelled requests have `requestedAmount` zeroed on the request itself;
28722
- // pull the original value from the Initiated event so the row still
28723
- // carries "100 cancelled" instead of "0 cancelled".
29595
+ // take the amount from the Cancelled event so the row still carries
29596
+ // "1500 cancelled" (1000 + a 500 top-up) instead of "0 cancelled".
28724
29597
  const requested = cancelled
28725
- ? num(request.requestedAmount) || initiatedAmountOf(request.events)
29598
+ ? num(request.requestedAmount) || cancelledAmountOf(request.events)
28726
29599
  : num(request.requestedAmount);
28727
29600
  const accepted = num(request.acceptedAmount);
28728
29601
  const rejected = num(request.rejectedAmount);
@@ -28773,18 +29646,23 @@ function deriveRequestState(request) {
28773
29646
  }
28774
29647
  // 4. Withdrawal partly filled with a LIVE remainder: the request returns
28775
29648
  // to the queue with a live Cancel. Checked BEFORE the resolved branch and
28776
- // via `isCycleClosed`, never via the sticky raw status.
29649
+ // via `isCycleClosed`, never via the sticky raw status. `requested` is
29650
+ // what is still OUTSTANDING, so "partly filled" is "something paid AND
29651
+ // something still queued" — never a comparison of the two, which would
29652
+ // turn on whether more was paid than is left.
28777
29653
  if (isWithdrawal &&
28778
29654
  accepted > 0 &&
28779
- accepted < requested &&
29655
+ requested > 0 &&
28780
29656
  !isCycleClosed(request)) {
28781
29657
  return Object.assign(Object.assign({}, base), { kind: 'outflow', amount: -accepted, statusCode: 'partial' });
28782
29658
  }
28783
29659
  // 5. Resolved — a rejection IS a resolution.
28784
29660
  if (request.status === UserRequestStatus.PROCESSED) {
29661
+ // A withdrawal is resolved only once nothing is left queued — a
29662
+ // partly filled one returns to the queue (branch 4), and one cancelled
29663
+ // with a remainder is branch 1 — so a resolved withdrawal is complete.
28785
29664
  if (isWithdrawal) {
28786
- const partly = accepted < requested;
28787
- return Object.assign(Object.assign({}, base), { kind: 'outflow', amount: -accepted, statusCode: partly ? 'partial' : 'complete' });
29665
+ return Object.assign(Object.assign({}, base), { kind: 'outflow', amount: -accepted, statusCode: 'complete' });
28788
29666
  }
28789
29667
  // Deposit: full reject vs partial vs full accept.
28790
29668
  if (accepted === 0) {
@@ -28923,12 +29801,13 @@ class PortfolioFacade {
28923
29801
  * ```
28924
29802
  */
28925
29803
  class Kasu {
28926
- constructor(sdk, chainConfig, signerOrProvider, configOverrides) {
29804
+ constructor(sdk, chainConfig, signerOrProvider, configOverrides, historyProvider) {
28927
29805
  var _a, _b;
28928
29806
  this._sdk = sdk;
28929
29807
  this._chainConfig = chainConfig;
28930
29808
  this._signerOrProvider = signerOrProvider;
28931
29809
  this._configOverrides = configOverrides;
29810
+ this._historyProvider = historyProvider;
28932
29811
  this.strategies = new StrategiesFacade(sdk.DataService, sdk.UserLending, normaliseMapping((_a = configOverrides.poolMetadataMapping) !== null && _a !== void 0 ? _a : chainConfig.poolMetadataMapping));
28933
29812
  // Derived ONCE and handed to every facade that needs it. Each of them
28934
29813
  // re-deriving `Signer.isSigner` is how two facades come to disagree
@@ -28966,7 +29845,7 @@ class Kasu {
28966
29845
  * ```
28967
29846
  */
28968
29847
  static create(options) {
28969
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
29848
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
28970
29849
  const chainConfig = resolveChainConfig(options.chain);
28971
29850
  const overrides = (_a = options.configOverrides) !== null && _a !== void 0 ? _a : {};
28972
29851
  const sdkConfig = new SdkConfig({
@@ -28981,12 +29860,18 @@ class Kasu {
28981
29860
  // of 6. Every live deployment happens to be 6dp today, so the
28982
29861
  // default was right by luck; the next one need not be.
28983
29862
  stableAssetDecimals: (_h = overrides.stableAssetDecimals) !== null && _h !== void 0 ? _h : (_j = stableAssetOf(chainConfig)) === null || _j === void 0 ? void 0 : _j.decimals,
29863
+ historyRpcUrls: (_k = overrides.historyRpcUrls) !== null && _k !== void 0 ? _k : chainConfig.historyRpcUrls,
29864
+ chainId: (_l = overrides.chainId) !== null && _l !== void 0 ? _l : chainConfig.chainId,
28984
29865
  });
28985
- const signerOrProvider = (_k = options.signerOrProvider) !== null && _k !== void 0 ? _k : defaultProvider(chainConfig, typeof options.chain === 'string'
29866
+ const signerOrProvider = (_m = options.signerOrProvider) !== null && _m !== void 0 ? _m : defaultProvider(chainConfig, typeof options.chain === 'string'
28986
29867
  ? options.chain
28987
29868
  : chainConfig.name);
28988
- const sdk = new KasuSdk(sdkConfig, signerOrProvider);
28989
- return new Kasu(sdk, chainConfig, signerOrProvider, overrides);
29869
+ // An explicit history provider wins; otherwise DataService builds
29870
+ // them from `historyRpcUrls`, pinned to `chainId` (the one place).
29871
+ const sdk = new KasuSdk(sdkConfig, signerOrProvider, {
29872
+ historyProvider: options.historyProvider,
29873
+ });
29874
+ return new Kasu(sdk, chainConfig, signerOrProvider, overrides, options.historyProvider);
28990
29875
  }
28991
29876
  /**
28992
29877
  * A NEW instance bound to `signer`, with the same chain config and the same
@@ -29006,6 +29891,7 @@ class Kasu {
29006
29891
  chain: this._chainConfig,
29007
29892
  signerOrProvider: signer,
29008
29893
  configOverrides: this._configOverrides,
29894
+ historyProvider: this._historyProvider,
29009
29895
  });
29010
29896
  }
29011
29897
  /**
@@ -29817,9 +30703,13 @@ exports.MIN_LENDING_AMOUNT_FALLBACK = MIN_LENDING_AMOUNT_FALLBACK;
29817
30703
  exports.MIN_TRANCHE_CAPACITY = MIN_TRANCHE_CAPACITY;
29818
30704
  exports.NOT_A_WITHDRAWAL_CALL_MESSAGE = NOT_A_WITHDRAWAL_CALL_MESSAGE;
29819
30705
  exports.NO_DIRECTUS_URL_MESSAGE = NO_DIRECTUS_URL_MESSAGE;
30706
+ exports.NO_HISTORY_PROVIDER_MESSAGE = NO_HISTORY_PROVIDER_MESSAGE;
29820
30707
  exports.NO_KYC_PARAMS_MESSAGE = NO_KYC_PARAMS_MESSAGE;
29821
30708
  exports.NO_SPENDER_MESSAGE = NO_SPENDER_MESSAGE;
29822
30709
  exports.NO_WITHDRAWAL_ENCODER_MESSAGE = NO_WITHDRAWAL_ENCODER_MESSAGE;
30710
+ exports.PastStateUnavailableError = PastStateUnavailableError;
30711
+ exports.PoolTvlHistoryAbortedError = PoolTvlHistoryAbortedError;
30712
+ exports.PoolTvlHistoryTimeoutError = PoolTvlHistoryTimeoutError;
29823
30713
  exports.PortfolioFacade = PortfolioFacade;
29824
30714
  exports.READ_ONLY_MESSAGE = READ_ONLY_MESSAGE;
29825
30715
  exports.RIXON_CAPITAL = RIXON_CAPITAL;
@@ -29898,6 +30788,7 @@ exports.pickHighestYieldTranche = pickHighestYieldTranche;
29898
30788
  exports.pickShortDescription = pickShortDescription;
29899
30789
  exports.poolAllTranchesFull = poolAllTranchesFull;
29900
30790
  exports.poolMaxApy = poolMaxApy;
30791
+ exports.poolTvl = poolTvl;
29901
30792
  exports.projectedAnnualEarnings = projectedAnnualEarnings;
29902
30793
  exports.proveDepositInReceipt = proveDepositInReceipt;
29903
30794
  exports.proveUserOperationInReceipt = proveUserOperationInReceipt;