@kasufinance/kasu-sdk 2.13.1 → 2.15.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 +68 -5
- package/dist/bundle.cjs.js +1038 -29
- package/dist/bundle.esm.js +1026 -30
- package/dist/domain/index.d.ts +8 -3
- package/dist/domain/index.js +6 -2
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/loan-contract.d.ts +16 -5
- package/dist/domain/loan-contract.js +16 -5
- package/dist/domain/loan-contract.js.map +1 -1
- package/dist/domain/pool-tvl.d.ts +104 -0
- package/dist/domain/pool-tvl.js +179 -0
- package/dist/domain/pool-tvl.js.map +1 -1
- package/dist/domain/requests.js +29 -19
- package/dist/domain/requests.js.map +1 -1
- package/dist/domain/strategy-details.d.ts +262 -0
- package/dist/domain/strategy-details.js +375 -0
- package/dist/domain/strategy-details.js.map +1 -0
- package/dist/facade/index.d.ts +3 -1
- package/dist/facade/index.js +2 -0
- package/dist/facade/index.js.map +1 -1
- package/dist/facade/strategies.d.ts +102 -4
- package/dist/facade/strategies.js +182 -6
- package/dist/facade/strategies.js.map +1 -1
- package/dist/facade/strategy-deployments.d.ts +65 -0
- package/dist/facade/strategy-deployments.js +97 -0
- package/dist/facade/strategy-deployments.js.map +1 -0
- package/dist/facade/types.d.ts +82 -0
- package/dist/flows/deposit-flow.d.ts +19 -5
- package/dist/flows/deposit-flow.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/DataService/data-service.d.ts +29 -1
- package/dist/services/DataService/data-service.js +80 -9
- package/dist/services/DataService/data-service.js.map +1 -1
- package/dist/services/DataService/tvl-history.d.ts +49 -0
- package/dist/services/DataService/tvl-history.js +89 -0
- package/dist/services/DataService/tvl-history.js.map +1 -1
- package/dist/services/UserLending/queries.js +2 -7
- package/dist/services/UserLending/queries.js.map +1 -1
- package/dist/services/UserLending/subgraph-types.js.map +1 -1
- package/dist/services/UserLending/types.js.map +1 -1
- package/dist/services/UserLending/user-lending.js +72 -16
- package/dist/services/UserLending/user-lending.js.map +1 -1
- package/package.json +1 -1
- package/src/domain/index.ts +34 -3
- package/src/domain/loan-contract.ts +16 -5
- package/src/domain/pool-tvl.ts +281 -0
- package/src/domain/strategy-details.ts +528 -0
- package/src/facade/index.ts +13 -0
- package/src/facade/strategies.ts +231 -11
- package/src/facade/strategy-deployments.ts +161 -0
- package/src/facade/types.ts +85 -0
- package/src/flows/deposit-flow.ts +19 -5
- package/src/index.ts +4 -1
- package/src/services/DataService/data-service.ts +103 -7
- package/src/services/DataService/tvl-history.ts +131 -0
package/src/domain/pool-tvl.ts
CHANGED
|
@@ -64,3 +64,284 @@ export interface PoolTvlHistoryEntry {
|
|
|
64
64
|
/** `'clearing'` for a cycle boundary; `'current'` for the closing point. */
|
|
65
65
|
kind: 'clearing' | 'current';
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* One deployment's share of a strategy's history, for `combineTvlHistories`:
|
|
70
|
+
* that pool's `getPoolTvlHistoryWindow` result (`{ entries, opening }`) as it
|
|
71
|
+
* came back, and what one unit of the deployment's stable token is worth in
|
|
72
|
+
* USD (`1` for USDC; today's AUD→USD for AUDD). The SDK fetches no exchange
|
|
73
|
+
* rate: the caller passes it.
|
|
74
|
+
*/
|
|
75
|
+
export interface TvlHistoryLeg {
|
|
76
|
+
/** At least one point: a real pool always returns its `current` point. */
|
|
77
|
+
entries: readonly PoolTvlHistoryEntry[];
|
|
78
|
+
/** USD per unit of the leg's stable token. Finite and above 0. */
|
|
79
|
+
usdRate: number;
|
|
80
|
+
/**
|
|
81
|
+
* The leg's step BEFORE its first entry: the newest clearing before the
|
|
82
|
+
* window, as `getPoolTvlHistoryWindow` read it at its own block. It
|
|
83
|
+
* counts from its own `timestamp` on, and the leg is 0 before that — no
|
|
84
|
+
* value is carried back to a time before it existed. A `'clearing'`
|
|
85
|
+
* point no later than the first entry; it is never drawn. `null` or
|
|
86
|
+
* absent: the pool has no clearing before the window (it started lending
|
|
87
|
+
* later, say) and counts 0 before its first entry.
|
|
88
|
+
*/
|
|
89
|
+
opening?: PoolTvlHistoryEntry | null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* One point of a strategy's TVL summed across its deployments.
|
|
94
|
+
*
|
|
95
|
+
* There is no `epochId`: each chain clears at its own blocks and times, so a
|
|
96
|
+
* merged point belongs to no single epoch. Plot by `timestamp`.
|
|
97
|
+
*/
|
|
98
|
+
export interface StrategyTvlHistoryEntry {
|
|
99
|
+
/** Unix SECONDS. Strictly ascending across the series. */
|
|
100
|
+
timestamp: number;
|
|
101
|
+
/** The sum over legs of each leg's TVL then, times its `usdRate`. */
|
|
102
|
+
tvlUsd: number;
|
|
103
|
+
/** `'clearing'` for a cycle boundary on any leg; `'current'` for the closing point. */
|
|
104
|
+
kind: 'clearing' | 'current';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* What `combineTvlHistories` refuses, as a code:
|
|
109
|
+
*
|
|
110
|
+
* - `legs` — not a non-empty array (or one with holes);
|
|
111
|
+
* - `leg` — a leg that is not an object;
|
|
112
|
+
* - `usd-rate` — a rate that is not a finite number above 0;
|
|
113
|
+
* - `opening` — an opening that is not a well-formed `'clearing'` point at
|
|
114
|
+
* or before the leg's first entry (or `null`, or absent);
|
|
115
|
+
* - `entries` — a leg's `entries` is not an array;
|
|
116
|
+
* - `empty-leg` — a leg with no entries: pass only the legs you have;
|
|
117
|
+
* - `entry` — an entry that is not an object (or a hole);
|
|
118
|
+
* - `timestamp` / `tvl` — not a finite number at or above 0;
|
|
119
|
+
* - `kind` — neither `'clearing'` nor `'current'`;
|
|
120
|
+
* - `order` — timestamps going backwards within a leg;
|
|
121
|
+
* - `current` — a `current` point that is not the leg's last, or some legs
|
|
122
|
+
* ending in one and others not;
|
|
123
|
+
* - `overflow` — a sum too large to be a finite number.
|
|
124
|
+
*/
|
|
125
|
+
export type TvlHistoryInputErrorCode =
|
|
126
|
+
| 'legs'
|
|
127
|
+
| 'leg'
|
|
128
|
+
| 'usd-rate'
|
|
129
|
+
| 'opening'
|
|
130
|
+
| 'entries'
|
|
131
|
+
| 'empty-leg'
|
|
132
|
+
| 'entry'
|
|
133
|
+
| 'timestamp'
|
|
134
|
+
| 'tvl'
|
|
135
|
+
| 'kind'
|
|
136
|
+
| 'order'
|
|
137
|
+
| 'current'
|
|
138
|
+
| 'overflow';
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* What `combineTvlHistories` throws instead of returning a number it cannot
|
|
142
|
+
* stand behind. `leg` and `entry` are the zero-based position of the
|
|
143
|
+
* offending input, when there is one.
|
|
144
|
+
*/
|
|
145
|
+
export class TvlHistoryInputError extends Error {
|
|
146
|
+
readonly code: TvlHistoryInputErrorCode;
|
|
147
|
+
readonly leg: number | undefined;
|
|
148
|
+
readonly entry: number | undefined;
|
|
149
|
+
|
|
150
|
+
constructor(code: TvlHistoryInputErrorCode, leg?: number, entry?: number) {
|
|
151
|
+
const at = [
|
|
152
|
+
leg === undefined ? '' : ` at leg ${String(leg)}`,
|
|
153
|
+
entry === undefined ? '' : `, entry ${String(entry)}`,
|
|
154
|
+
].join('');
|
|
155
|
+
super(`combineTvlHistories: input refused (${code})${at}`);
|
|
156
|
+
this.name = 'TvlHistoryInputError';
|
|
157
|
+
this.code = code;
|
|
158
|
+
this.leg = leg;
|
|
159
|
+
this.entry = entry;
|
|
160
|
+
Object.setPrototypeOf(this, TvlHistoryInputError.prototype);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isNonNegativeFinite(value: unknown): value is number {
|
|
165
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Throws unless the leg is well formed. Index loops, not `forEach`: a hole in
|
|
170
|
+
* a sparse array is skipped by the array methods and would surface later as
|
|
171
|
+
* a raw `TypeError` instead of a refusal.
|
|
172
|
+
*/
|
|
173
|
+
function validateLeg(leg: unknown, legIndex: number): TvlHistoryLeg {
|
|
174
|
+
if (typeof leg !== 'object' || leg === null) {
|
|
175
|
+
throw new TvlHistoryInputError('leg', legIndex);
|
|
176
|
+
}
|
|
177
|
+
const { entries, usdRate, opening } = leg as Partial<TvlHistoryLeg>;
|
|
178
|
+
if (
|
|
179
|
+
typeof usdRate !== 'number' ||
|
|
180
|
+
!Number.isFinite(usdRate) ||
|
|
181
|
+
usdRate <= 0
|
|
182
|
+
) {
|
|
183
|
+
throw new TvlHistoryInputError('usd-rate', legIndex);
|
|
184
|
+
}
|
|
185
|
+
if (!Array.isArray(entries as unknown)) {
|
|
186
|
+
throw new TvlHistoryInputError('entries', legIndex);
|
|
187
|
+
}
|
|
188
|
+
const list = entries as readonly unknown[];
|
|
189
|
+
if (list.length === 0) {
|
|
190
|
+
throw new TvlHistoryInputError('empty-leg', legIndex);
|
|
191
|
+
}
|
|
192
|
+
let previous = -Infinity;
|
|
193
|
+
for (let index = 0; index < list.length; index += 1) {
|
|
194
|
+
const entry = list[index];
|
|
195
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
196
|
+
throw new TvlHistoryInputError('entry', legIndex, index);
|
|
197
|
+
}
|
|
198
|
+
const { timestamp, tvl, kind } = entry as Partial<PoolTvlHistoryEntry>;
|
|
199
|
+
if (!isNonNegativeFinite(timestamp)) {
|
|
200
|
+
throw new TvlHistoryInputError('timestamp', legIndex, index);
|
|
201
|
+
}
|
|
202
|
+
if (!isNonNegativeFinite(tvl)) {
|
|
203
|
+
throw new TvlHistoryInputError('tvl', legIndex, index);
|
|
204
|
+
}
|
|
205
|
+
if (kind !== 'clearing' && kind !== 'current') {
|
|
206
|
+
throw new TvlHistoryInputError('kind', legIndex, index);
|
|
207
|
+
}
|
|
208
|
+
if (kind === 'current' && index !== list.length - 1) {
|
|
209
|
+
throw new TvlHistoryInputError('current', legIndex, index);
|
|
210
|
+
}
|
|
211
|
+
if (timestamp < previous) {
|
|
212
|
+
throw new TvlHistoryInputError('order', legIndex, index);
|
|
213
|
+
}
|
|
214
|
+
previous = timestamp;
|
|
215
|
+
}
|
|
216
|
+
if (opening !== undefined && opening !== null) {
|
|
217
|
+
const first = list[0] as PoolTvlHistoryEntry;
|
|
218
|
+
const { timestamp, tvl, kind } = (
|
|
219
|
+
typeof opening === 'object' ? opening : {}
|
|
220
|
+
) as Partial<PoolTvlHistoryEntry>;
|
|
221
|
+
if (
|
|
222
|
+
!isNonNegativeFinite(timestamp) ||
|
|
223
|
+
!isNonNegativeFinite(tvl) ||
|
|
224
|
+
kind !== 'clearing' ||
|
|
225
|
+
timestamp > first.timestamp
|
|
226
|
+
) {
|
|
227
|
+
throw new TvlHistoryInputError('opening', legIndex);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return leg as TvlHistoryLeg;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* A strategy's TVL in USD over time, from its deployments' histories — Tax
|
|
235
|
+
* Pay runs on Base (USDC) and on XDC (AUDD), and its total lent is both.
|
|
236
|
+
* Pure: the caller fetches each leg (`strategyDeployments` names them) and
|
|
237
|
+
* passes the exchange rates.
|
|
238
|
+
*
|
|
239
|
+
* A step merge by time. At every distinct timestamp where any leg has a
|
|
240
|
+
* clearing, the value is the sum over legs of that leg's latest point at or
|
|
241
|
+
* before then, times its `usdRate`. A leg's `opening` — what it held just
|
|
242
|
+
* before the window — is one more step: it counts from its own timestamp
|
|
243
|
+
* until the leg's first entry, and the leg is 0 before it (or throughout,
|
|
244
|
+
* before its first entry, without one).
|
|
245
|
+
* Points that share a timestamp — clearings run in one block, or two chains
|
|
246
|
+
* clearing in the same second — are one output point. The series ends with
|
|
247
|
+
* one `current` point at the latest of the legs' `current` timestamps, equal
|
|
248
|
+
* to the sum of their `current` values. Every leg must end in a `current`
|
|
249
|
+
* point, or none may.
|
|
250
|
+
*
|
|
251
|
+
* The window is the union of the legs' windows: the series starts at the
|
|
252
|
+
* earliest first entry of any leg. An `opening` is a value, never a point.
|
|
253
|
+
* Without it, two windows cut at offset clearing times would count the
|
|
254
|
+
* later-starting leg 0 at the left edge and draw a false step up; with it,
|
|
255
|
+
* only a genuine late start (`opening: null`) counts 0.
|
|
256
|
+
*
|
|
257
|
+
* Every leg and entry is validated first: a bad input — an empty leg
|
|
258
|
+
* included — throws `TvlHistoryInputError` (its `code` says why), so the
|
|
259
|
+
* result never holds `NaN` or `Infinity`. The inputs are not modified.
|
|
260
|
+
*
|
|
261
|
+
* ```ts
|
|
262
|
+
* const window = { count: 52 };
|
|
263
|
+
* const points = combineTvlHistories([
|
|
264
|
+
* { ...(await base.strategies.getPoolTvlHistoryWindow(basePool, window)), usdRate: 1 },
|
|
265
|
+
* { ...(await xdc.strategies.getPoolTvlHistoryWindow(xdcPool, window)), usdRate: audToUsd },
|
|
266
|
+
* ]);
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
export function combineTvlHistories(
|
|
270
|
+
legs: readonly TvlHistoryLeg[],
|
|
271
|
+
): StrategyTvlHistoryEntry[] {
|
|
272
|
+
if (!Array.isArray(legs as unknown) || legs.length === 0) {
|
|
273
|
+
throw new TvlHistoryInputError('legs');
|
|
274
|
+
}
|
|
275
|
+
const valid: TvlHistoryLeg[] = [];
|
|
276
|
+
for (let index = 0; index < legs.length; index += 1) {
|
|
277
|
+
valid.push(validateLeg(legs[index], index));
|
|
278
|
+
}
|
|
279
|
+
const endsInCurrent = valid.map(
|
|
280
|
+
(leg) => leg.entries[leg.entries.length - 1].kind === 'current',
|
|
281
|
+
);
|
|
282
|
+
const odd = endsInCurrent.findIndex((ends) => ends !== endsInCurrent[0]);
|
|
283
|
+
if (odd !== -1) throw new TvlHistoryInputError('current', odd);
|
|
284
|
+
|
|
285
|
+
const finalTimestamp = endsInCurrent[0]
|
|
286
|
+
? Math.max(
|
|
287
|
+
...valid.map(
|
|
288
|
+
(leg) => leg.entries[leg.entries.length - 1].timestamp,
|
|
289
|
+
),
|
|
290
|
+
)
|
|
291
|
+
: undefined;
|
|
292
|
+
const clearingTimes = [
|
|
293
|
+
...new Set(
|
|
294
|
+
valid.flatMap((leg) =>
|
|
295
|
+
leg.entries
|
|
296
|
+
.filter((entry) => entry.kind === 'clearing')
|
|
297
|
+
.map((entry) => entry.timestamp),
|
|
298
|
+
),
|
|
299
|
+
),
|
|
300
|
+
]
|
|
301
|
+
.filter((t) => finalTimestamp === undefined || t < finalTimestamp)
|
|
302
|
+
.sort((a, b) => a - b);
|
|
303
|
+
|
|
304
|
+
// One cursor per leg: the times ascend, so each leg is walked once. A
|
|
305
|
+
// leg's `current` point is a step like any other, which keeps a chain
|
|
306
|
+
// whose subgraph lags behind the other's last clearing at its own latest.
|
|
307
|
+
const cursors = valid.map(() => -1);
|
|
308
|
+
const sumAt = (t: number): number => {
|
|
309
|
+
let sum = 0;
|
|
310
|
+
valid.forEach((leg, index) => {
|
|
311
|
+
let cursor = cursors[index];
|
|
312
|
+
while (
|
|
313
|
+
cursor + 1 < leg.entries.length &&
|
|
314
|
+
leg.entries[cursor + 1].timestamp <= t
|
|
315
|
+
) {
|
|
316
|
+
cursor += 1;
|
|
317
|
+
}
|
|
318
|
+
cursors[index] = cursor;
|
|
319
|
+
// Before the first entry, the opening is the step — from its
|
|
320
|
+
// own time on, never earlier.
|
|
321
|
+
const opening = leg.opening;
|
|
322
|
+
const value =
|
|
323
|
+
cursor >= 0
|
|
324
|
+
? leg.entries[cursor].tvl
|
|
325
|
+
: opening && opening.timestamp <= t
|
|
326
|
+
? opening.tvl
|
|
327
|
+
: 0;
|
|
328
|
+
sum += value * leg.usdRate;
|
|
329
|
+
});
|
|
330
|
+
if (!Number.isFinite(sum)) throw new TvlHistoryInputError('overflow');
|
|
331
|
+
return sum;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const combined: StrategyTvlHistoryEntry[] = clearingTimes.map((t) => ({
|
|
335
|
+
timestamp: t,
|
|
336
|
+
tvlUsd: sumAt(t),
|
|
337
|
+
kind: 'clearing' as const,
|
|
338
|
+
}));
|
|
339
|
+
if (finalTimestamp !== undefined) {
|
|
340
|
+
combined.push({
|
|
341
|
+
timestamp: finalTimestamp,
|
|
342
|
+
tvlUsd: sumAt(finalTimestamp),
|
|
343
|
+
kind: 'current',
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
return combined;
|
|
347
|
+
}
|