@kasufinance/kasu-sdk 2.11.0 → 2.12.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 +11 -1
- package/dist/bundle.cjs.js +674 -59
- package/dist/bundle.esm.js +673 -60
- package/dist/domain/index.d.ts +4 -2
- package/dist/domain/index.js +1 -0
- package/dist/domain/index.js.map +1 -1
- 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/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 +1 -0
- package/dist/index.js.map +1 -1
- 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 +177 -0
- package/dist/services/Portfolio/yield-history.js +405 -0
- package/dist/services/Portfolio/yield-history.js.map +1 -0
- package/package.json +1 -1
- package/src/domain/index.ts +12 -10
- package/src/domain/yield-history.ts +194 -0
- package/src/facade/user-portfolio.ts +40 -3
- package/src/index.ts +2 -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 +574 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { gql } from 'graphql-request';
|
|
11
|
+
import { epochTrancheYield, } from '../../domain/yield-history';
|
|
12
|
+
/** The subgraph returns at most this many rows per collection per request. */
|
|
13
|
+
export const YIELD_HISTORY_PAGE_SIZE = 1000;
|
|
14
|
+
/**
|
|
15
|
+
* A runaway guard, not a working limit: a collection this many pages long
|
|
16
|
+
* (`MAX × PAGE_SIZE` rows for one lender) is a broken response, not a history.
|
|
17
|
+
*/
|
|
18
|
+
export const YIELD_HISTORY_MAX_PAGES = 100;
|
|
19
|
+
/**
|
|
20
|
+
* Throws `RangeError` on a malformed `fromEpoch` / `count`. `getYieldHistory`
|
|
21
|
+
* calls it BEFORE any network read.
|
|
22
|
+
*/
|
|
23
|
+
export function validateYieldHistoryOptions(options) {
|
|
24
|
+
const { fromEpoch, count } = options;
|
|
25
|
+
if (fromEpoch !== undefined &&
|
|
26
|
+
(!Number.isSafeInteger(fromEpoch) || fromEpoch < 0)) {
|
|
27
|
+
throw new RangeError(`getYieldHistory: fromEpoch must be a non-negative integer, got ${String(fromEpoch)}`);
|
|
28
|
+
}
|
|
29
|
+
if (count !== undefined && (!Number.isSafeInteger(count) || count < 1)) {
|
|
30
|
+
throw new RangeError(`getYieldHistory: count must be a positive integer, got ${String(count)}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function resolveYieldHistoryWindow(options, currentEpoch) {
|
|
34
|
+
validateYieldHistoryOptions(options);
|
|
35
|
+
const { fromEpoch, count } = options;
|
|
36
|
+
const newest = currentEpoch - 1;
|
|
37
|
+
if (newest < 0)
|
|
38
|
+
return null;
|
|
39
|
+
let from;
|
|
40
|
+
let to = newest;
|
|
41
|
+
if (fromEpoch !== undefined) {
|
|
42
|
+
from = fromEpoch;
|
|
43
|
+
if (count !== undefined)
|
|
44
|
+
to = Math.min(newest, fromEpoch + count - 1);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
from = count !== undefined ? Math.max(0, newest - count + 1) : 0;
|
|
48
|
+
}
|
|
49
|
+
return from > to ? null : { fromEpoch: from, toEpoch: to };
|
|
50
|
+
}
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Subgraph reads
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
/**
|
|
55
|
+
* Round trip 1: the lender's tranche positions and fixed-term locks. Same
|
|
56
|
+
* pool exclusion and the same nested-list size (`$first`) as
|
|
57
|
+
* `lendingPortfolioQuery`, so both read the same lock set.
|
|
58
|
+
*/
|
|
59
|
+
export const yieldHistoryPositionsQuery = gql `
|
|
60
|
+
query YieldHistoryPositions(
|
|
61
|
+
$userAddress: ID!
|
|
62
|
+
$unusedPools: [String!]!
|
|
63
|
+
$first: Int!
|
|
64
|
+
) {
|
|
65
|
+
user(id: $userAddress) {
|
|
66
|
+
lendingPoolUserDetails(
|
|
67
|
+
first: $first
|
|
68
|
+
where: { lendingPool_not_in: $unusedPools }
|
|
69
|
+
) {
|
|
70
|
+
lendingPool {
|
|
71
|
+
id
|
|
72
|
+
}
|
|
73
|
+
lendingPoolTrancheUserDetails(first: $first) {
|
|
74
|
+
id
|
|
75
|
+
tranche {
|
|
76
|
+
id
|
|
77
|
+
balance
|
|
78
|
+
shares
|
|
79
|
+
}
|
|
80
|
+
userLendingPoolTrancheFixedTermDepositLocks(first: $first) {
|
|
81
|
+
id
|
|
82
|
+
epochLockStart
|
|
83
|
+
epochLockEnd
|
|
84
|
+
initialTrancheShares
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
/**
|
|
92
|
+
* Round trip 2 onwards: the four per-epoch series, one page of each per
|
|
93
|
+
* request, cursor-paginated by `id`. A series that is exhausted is skipped with
|
|
94
|
+
* `@include`, so a history costs as many requests as its LONGEST series has
|
|
95
|
+
* pages — normally one — never one per epoch.
|
|
96
|
+
*
|
|
97
|
+
* Share snapshots are read up to the window's end with no lower bound: the
|
|
98
|
+
* value AS OF the first epoch may come from an update long before it.
|
|
99
|
+
*/
|
|
100
|
+
export const yieldHistoryPageQuery = gql `
|
|
101
|
+
query YieldHistoryPage(
|
|
102
|
+
$tranches: [String!]!
|
|
103
|
+
$userTranches: [String!]!
|
|
104
|
+
$locks: [String!]!
|
|
105
|
+
$fromEpoch: BigInt!
|
|
106
|
+
$toEpoch: BigInt!
|
|
107
|
+
$interestCursor: ID!
|
|
108
|
+
$trancheSharesCursor: ID!
|
|
109
|
+
$userSharesCursor: ID!
|
|
110
|
+
$lockSharesCursor: ID!
|
|
111
|
+
$withInterest: Boolean!
|
|
112
|
+
$withTrancheShares: Boolean!
|
|
113
|
+
$withUserShares: Boolean!
|
|
114
|
+
$withLockShares: Boolean!
|
|
115
|
+
$first: Int!
|
|
116
|
+
) {
|
|
117
|
+
interest: lendingPoolTrancheEpochInterests(
|
|
118
|
+
first: $first
|
|
119
|
+
orderBy: id
|
|
120
|
+
orderDirection: asc
|
|
121
|
+
where: {
|
|
122
|
+
id_gt: $interestCursor
|
|
123
|
+
lendingPoolTranche_in: $tranches
|
|
124
|
+
epochId_gte: $fromEpoch
|
|
125
|
+
epochId_lte: $toEpoch
|
|
126
|
+
}
|
|
127
|
+
) @include(if: $withInterest) {
|
|
128
|
+
id
|
|
129
|
+
epochId
|
|
130
|
+
epochInterestAmount
|
|
131
|
+
lendingPoolTranche {
|
|
132
|
+
id
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
trancheShares: lendingPoolTrancheShareUpdates(
|
|
136
|
+
first: $first
|
|
137
|
+
orderBy: id
|
|
138
|
+
orderDirection: asc
|
|
139
|
+
where: {
|
|
140
|
+
id_gt: $trancheSharesCursor
|
|
141
|
+
lendingPoolTranche_in: $tranches
|
|
142
|
+
epochId_lte: $toEpoch
|
|
143
|
+
}
|
|
144
|
+
) @include(if: $withTrancheShares) {
|
|
145
|
+
id
|
|
146
|
+
epochId
|
|
147
|
+
shareUpdatesIndex
|
|
148
|
+
shares
|
|
149
|
+
lendingPoolTranche {
|
|
150
|
+
id
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
userShares: lendingPoolTrancheUserEpochSharesUpdates(
|
|
154
|
+
first: $first
|
|
155
|
+
orderBy: id
|
|
156
|
+
orderDirection: asc
|
|
157
|
+
where: {
|
|
158
|
+
id_gt: $userSharesCursor
|
|
159
|
+
lendingPoolTrancheUserDetails_in: $userTranches
|
|
160
|
+
epochId_lte: $toEpoch
|
|
161
|
+
}
|
|
162
|
+
) @include(if: $withUserShares) {
|
|
163
|
+
id
|
|
164
|
+
epochId
|
|
165
|
+
shareUpdatesIndex
|
|
166
|
+
shares
|
|
167
|
+
lendingPoolTrancheUserDetails {
|
|
168
|
+
id
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
lockShares: userLendingPoolTrancheFtdLockShareUpdates(
|
|
172
|
+
first: $first
|
|
173
|
+
orderBy: id
|
|
174
|
+
orderDirection: asc
|
|
175
|
+
where: {
|
|
176
|
+
id_gt: $lockSharesCursor
|
|
177
|
+
userLendingPoolTrancheFixedTermDepositLock_in: $locks
|
|
178
|
+
epochId_lte: $toEpoch
|
|
179
|
+
}
|
|
180
|
+
) @include(if: $withLockShares) {
|
|
181
|
+
id
|
|
182
|
+
epochId
|
|
183
|
+
shareUpdatesIndex
|
|
184
|
+
shares
|
|
185
|
+
userLendingPoolTrancheFixedTermDepositLock {
|
|
186
|
+
id
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
`;
|
|
191
|
+
/**
|
|
192
|
+
* Fetch the four series for the window, paginating each by `id` until a short
|
|
193
|
+
* page. Returns the rows and how many requests it took.
|
|
194
|
+
*/
|
|
195
|
+
export function fetchYieldHistorySeries(request, ids, window) {
|
|
196
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
197
|
+
var _a;
|
|
198
|
+
const rows = {
|
|
199
|
+
interest: [],
|
|
200
|
+
trancheShares: [],
|
|
201
|
+
userShares: [],
|
|
202
|
+
lockShares: [],
|
|
203
|
+
};
|
|
204
|
+
const series = [
|
|
205
|
+
{ key: 'interest', flag: 'withInterest', cursor: 'interestCursor' },
|
|
206
|
+
{
|
|
207
|
+
key: 'trancheShares',
|
|
208
|
+
flag: 'withTrancheShares',
|
|
209
|
+
cursor: 'trancheSharesCursor',
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
key: 'userShares',
|
|
213
|
+
flag: 'withUserShares',
|
|
214
|
+
cursor: 'userSharesCursor',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
key: 'lockShares',
|
|
218
|
+
flag: 'withLockShares',
|
|
219
|
+
cursor: 'lockSharesCursor',
|
|
220
|
+
},
|
|
221
|
+
];
|
|
222
|
+
const cursors = {
|
|
223
|
+
interest: '',
|
|
224
|
+
trancheShares: '',
|
|
225
|
+
userShares: '',
|
|
226
|
+
lockShares: '',
|
|
227
|
+
};
|
|
228
|
+
const open = {
|
|
229
|
+
interest: ids.tranches.length > 0,
|
|
230
|
+
trancheShares: ids.tranches.length > 0,
|
|
231
|
+
userShares: ids.userTranches.length > 0,
|
|
232
|
+
lockShares: ids.locks.length > 0,
|
|
233
|
+
};
|
|
234
|
+
let requests = 0;
|
|
235
|
+
while (series.some(({ key }) => open[key])) {
|
|
236
|
+
if (requests >= YIELD_HISTORY_MAX_PAGES) {
|
|
237
|
+
throw new Error(`getYieldHistory: more than ${String(YIELD_HISTORY_MAX_PAGES)} pages for one lender`);
|
|
238
|
+
}
|
|
239
|
+
const variables = {
|
|
240
|
+
tranches: ids.tranches,
|
|
241
|
+
userTranches: ids.userTranches,
|
|
242
|
+
locks: ids.locks,
|
|
243
|
+
fromEpoch: String(window.fromEpoch),
|
|
244
|
+
toEpoch: String(window.toEpoch),
|
|
245
|
+
first: YIELD_HISTORY_PAGE_SIZE,
|
|
246
|
+
};
|
|
247
|
+
for (const { key, flag, cursor } of series) {
|
|
248
|
+
variables[flag] = open[key];
|
|
249
|
+
variables[cursor] = cursors[key];
|
|
250
|
+
}
|
|
251
|
+
const page = yield request(variables);
|
|
252
|
+
requests += 1;
|
|
253
|
+
for (const { key } of series) {
|
|
254
|
+
if (!open[key])
|
|
255
|
+
continue;
|
|
256
|
+
const got = (_a = page[key]) !== null && _a !== void 0 ? _a : [];
|
|
257
|
+
rows[key].push(...got);
|
|
258
|
+
const last = got.at(-1);
|
|
259
|
+
if (got.length < YIELD_HISTORY_PAGE_SIZE || last === undefined) {
|
|
260
|
+
open[key] = false;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (last.id <= cursors[key]) {
|
|
264
|
+
throw new Error(`getYieldHistory: the ${key} cursor did not advance`);
|
|
265
|
+
}
|
|
266
|
+
cursors[key] = last.id;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return { rows, requests };
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The snapshot in force at `epoch`: among updates with `epochId ≤ epoch`, the
|
|
274
|
+
* one with the highest `shareUpdatesIndex` — exactly what
|
|
275
|
+
* `lendingPortfolioQuery` selects with `orderBy: shareUpdatesIndex, desc,
|
|
276
|
+
* first: 1, where: { epochId_lt: epoch + 1 }`. `undefined` when none.
|
|
277
|
+
*/
|
|
278
|
+
export function sharesAsOf(updates, epoch) {
|
|
279
|
+
let found;
|
|
280
|
+
for (const update of updates) {
|
|
281
|
+
if (Number(update.epochId) > epoch)
|
|
282
|
+
continue;
|
|
283
|
+
if (!found || update.shareUpdatesIndex > found.shareUpdatesIndex) {
|
|
284
|
+
found = update;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return found;
|
|
288
|
+
}
|
|
289
|
+
function groupBy(rows, key) {
|
|
290
|
+
const map = new Map();
|
|
291
|
+
for (const row of rows) {
|
|
292
|
+
const k = key(row);
|
|
293
|
+
const list = map.get(k);
|
|
294
|
+
if (list)
|
|
295
|
+
list.push(row);
|
|
296
|
+
else
|
|
297
|
+
map.set(k, [row]);
|
|
298
|
+
}
|
|
299
|
+
return map;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Pure: the fetched series → one entry per pool, tranche and settled epoch in
|
|
303
|
+
* which the lender held shares (or a lock earned a top-up). Each entry runs the
|
|
304
|
+
* same `epochTrancheYield` as the portfolio's last-epoch figure, with the same
|
|
305
|
+
* share selection, so the newest entry equals that figure.
|
|
306
|
+
*
|
|
307
|
+
* Sorted by `epochId`, then `poolId`, then `trancheId`.
|
|
308
|
+
*/
|
|
309
|
+
export function buildYieldHistory(read, window, clock) {
|
|
310
|
+
var _a, _b, _c, _d, _e, _f;
|
|
311
|
+
const user = read.positions.user;
|
|
312
|
+
if (!user)
|
|
313
|
+
return [];
|
|
314
|
+
const interestByTranche = groupBy(read.interest, (row) => row.lendingPoolTranche.id);
|
|
315
|
+
const trancheSharesByTranche = groupBy(read.trancheShares, (row) => row.lendingPoolTranche.id);
|
|
316
|
+
const userSharesByPosition = groupBy(read.userShares, (row) => row.lendingPoolTrancheUserDetails.id);
|
|
317
|
+
const lockSharesByLock = groupBy(read.lockShares, (row) => row.userLendingPoolTrancheFixedTermDepositLock.id);
|
|
318
|
+
const entries = [];
|
|
319
|
+
for (const pool of user.lendingPoolUserDetails) {
|
|
320
|
+
for (const position of pool.lendingPoolTrancheUserDetails) {
|
|
321
|
+
const trancheId = position.tranche.id;
|
|
322
|
+
const trancheUpdates = (_a = trancheSharesByTranche.get(trancheId)) !== null && _a !== void 0 ? _a : [];
|
|
323
|
+
const userUpdates = (_b = userSharesByPosition.get(position.id)) !== null && _b !== void 0 ? _b : [];
|
|
324
|
+
const trancheBalanceNow = parseFloat(position.tranche.balance);
|
|
325
|
+
const trancheSharesNow = parseFloat(position.tranche.shares);
|
|
326
|
+
for (const record of (_c = interestByTranche.get(trancheId)) !== null && _c !== void 0 ? _c : []) {
|
|
327
|
+
const epoch = Number(record.epochId);
|
|
328
|
+
if (epoch < window.fromEpoch || epoch > window.toEpoch) {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const userSnapshot = sharesAsOf(userUpdates, epoch);
|
|
332
|
+
const userShares = parseFloat((_d = userSnapshot === null || userSnapshot === void 0 ? void 0 : userSnapshot.shares) !== null && _d !== void 0 ? _d : '0');
|
|
333
|
+
const locks = position.userLendingPoolTrancheFixedTermDepositLocks.map((lock) => {
|
|
334
|
+
var _a, _b, _c, _d, _e;
|
|
335
|
+
const lockUpdates = (_a = lockSharesByLock.get(lock.id)) !== null && _a !== void 0 ? _a : [];
|
|
336
|
+
return {
|
|
337
|
+
active: parseFloat(lock.epochLockStart) < epoch &&
|
|
338
|
+
parseFloat(lock.epochLockEnd) >= epoch &&
|
|
339
|
+
userSnapshot !== undefined,
|
|
340
|
+
// No snapshot yet = the lock's opening
|
|
341
|
+
// shares: a lock made after clearing E
|
|
342
|
+
// starts at E but its first update is
|
|
343
|
+
// tagged E + 1, and reading that gap as 0
|
|
344
|
+
// would book its principal as a top-up.
|
|
345
|
+
sharesAtEpoch: parseFloat((_c = (_b = sharesAsOf(lockUpdates, epoch)) === null || _b === void 0 ? void 0 : _b.shares) !== null && _c !== void 0 ? _c : lock.initialTrancheShares),
|
|
346
|
+
sharesBeforeEpoch: parseFloat((_e = (_d = sharesAsOf(lockUpdates, epoch - 1)) === null || _d === void 0 ? void 0 : _d.shares) !== null && _e !== void 0 ? _e : lock.initialTrancheShares),
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
const { total } = epochTrancheYield({
|
|
350
|
+
epochInterest: parseFloat(record.epochInterestAmount),
|
|
351
|
+
userShares,
|
|
352
|
+
trancheShares: parseFloat((_f = (_e = sharesAsOf(trancheUpdates, epoch)) === null || _e === void 0 ? void 0 : _e.shares) !== null && _f !== void 0 ? _f : '0'),
|
|
353
|
+
trancheBalanceNow,
|
|
354
|
+
trancheSharesNow,
|
|
355
|
+
locks,
|
|
356
|
+
});
|
|
357
|
+
// Nothing held and nothing topped up: not part of this
|
|
358
|
+
// lender's history (a new lender's history is empty).
|
|
359
|
+
if (userShares <= 0 && total === 0)
|
|
360
|
+
continue;
|
|
361
|
+
entries.push({
|
|
362
|
+
epochId: epoch,
|
|
363
|
+
epochEnd: clock.epochZeroStart +
|
|
364
|
+
(epoch + 1) * clock.epochDuration,
|
|
365
|
+
poolId: pool.lendingPool.id,
|
|
366
|
+
trancheId,
|
|
367
|
+
netYield: total,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return entries.sort((a, b) => a.epochId - b.epochId ||
|
|
373
|
+
a.poolId.localeCompare(b.poolId) ||
|
|
374
|
+
a.trancheId.localeCompare(b.trancheId));
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* A GraphQL VALIDATION error naming an entity, field or argument this
|
|
378
|
+
* deployment's subgraph does not have. A network error, a timeout or an
|
|
379
|
+
* indexing error is not one.
|
|
380
|
+
*/
|
|
381
|
+
export function isMissingEntityError(error) {
|
|
382
|
+
if (typeof error !== 'object' || error === null)
|
|
383
|
+
return false;
|
|
384
|
+
const response = error.response;
|
|
385
|
+
const errors = response === null || response === void 0 ? void 0 : response.errors;
|
|
386
|
+
if (!Array.isArray(errors) || errors.length === 0)
|
|
387
|
+
return false;
|
|
388
|
+
return errors.every((e) => {
|
|
389
|
+
const message = e === null || e === void 0 ? void 0 : e.message;
|
|
390
|
+
return (typeof message === 'string' &&
|
|
391
|
+
/has no field|cannot query field|unknown (type|field|argument)|no such (type|field)/i.test(message));
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* The only failure `getYieldHistory` turns into `[]`: a missing-schema error
|
|
396
|
+
* on a LITE deployment, whose subgraph may predate the entities. On a full
|
|
397
|
+
* deployment the entities are part of the schema, so the same error means a
|
|
398
|
+
* broken query or subgraph and THROWS — an empty chart there would hide it.
|
|
399
|
+
* Everything else (network, timeout, indexing) throws on every deployment, so
|
|
400
|
+
* a consumer can tell "no history here" from "history unavailable".
|
|
401
|
+
*/
|
|
402
|
+
export function isEmptyHistoryError(error, liteDeployment) {
|
|
403
|
+
return liteDeployment && isMissingEntityError(error);
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=yield-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yield-history.js","sourceRoot":"","sources":["../../../src/services/Portfolio/yield-history.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/domain/index.ts
CHANGED
|
@@ -134,11 +134,7 @@ export {
|
|
|
134
134
|
lastEventTimestamp,
|
|
135
135
|
submissionEvents,
|
|
136
136
|
} from './requests';
|
|
137
|
-
export type {
|
|
138
|
-
RequestKind,
|
|
139
|
-
RequestState,
|
|
140
|
-
RequestStatusCode,
|
|
141
|
-
} from './requests';
|
|
137
|
+
export type { RequestKind, RequestState, RequestStatusCode } from './requests';
|
|
142
138
|
|
|
143
139
|
export { decodeRevert, extractRevertData } from './revert-errors';
|
|
144
140
|
export type { DecodedRevert, RevertFamily } from './revert-errors';
|
|
@@ -183,11 +179,7 @@ export {
|
|
|
183
179
|
trancheHasCapacity,
|
|
184
180
|
trancheRiskRank,
|
|
185
181
|
} from './tranches';
|
|
186
|
-
export type {
|
|
187
|
-
ApyBounds,
|
|
188
|
-
PoolStatus,
|
|
189
|
-
TrancheCapacitySignal,
|
|
190
|
-
} from './tranches';
|
|
182
|
+
export type { ApyBounds, PoolStatus, TrancheCapacitySignal } from './tranches';
|
|
191
183
|
|
|
192
184
|
export {
|
|
193
185
|
classifyWalletFailure,
|
|
@@ -196,6 +188,16 @@ export {
|
|
|
196
188
|
} from './wallet-errors';
|
|
197
189
|
export type { WalletFailure } from './wallet-errors';
|
|
198
190
|
|
|
191
|
+
export { aggregateYieldHistory, epochTrancheYield } from './yield-history';
|
|
192
|
+
export type {
|
|
193
|
+
EpochLockShares,
|
|
194
|
+
EpochTrancheYield,
|
|
195
|
+
EpochTrancheYieldInput,
|
|
196
|
+
YieldHistoryEntry,
|
|
197
|
+
YieldHistoryFilter,
|
|
198
|
+
YieldHistoryPoint,
|
|
199
|
+
} from './yield-history';
|
|
200
|
+
|
|
199
201
|
export {
|
|
200
202
|
accruedInterest,
|
|
201
203
|
dailyInterest,
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settled interest per cycle (epoch): what a lender was credited at each close,
|
|
3
|
+
* per pool and tranche, and the one formula that turns a tranche's credited
|
|
4
|
+
* interest into one lender's share of it.
|
|
5
|
+
*
|
|
6
|
+
* **The figures are NET, and only net.** `LendingPool._applyTrancheInterest`
|
|
7
|
+
* takes the performance fee off first and emits `InterestApplied` with the
|
|
8
|
+
* lender side only (`interest × (1 − performanceFee)`); a fixed-term top-up
|
|
9
|
+
* (`_applyFixedRateInterests`) is net of the fee the same way. So the figure
|
|
10
|
+
* this module computes from those records IS what the lender was credited —
|
|
11
|
+
* never take the fee off it again. No gross is offered: the subgraph keeps
|
|
12
|
+
* only TODAY's fee, so a historical gross would be a guess.
|
|
13
|
+
*
|
|
14
|
+
* Units: amounts are display units of the DEPLOYMENT'S OWN stable token (USDC
|
|
15
|
+
* on Base and XDC USDC, AUDD on XDC AUDD) — never converted to USD here;
|
|
16
|
+
* shares are display units of tranche shares, as the subgraph's BigDecimals.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** One fixed-term lock inside a tranche position, at one epoch. */
|
|
20
|
+
export interface EpochLockShares {
|
|
21
|
+
/**
|
|
22
|
+
* The lock earns in this epoch: it started BEFORE the epoch, ends at or
|
|
23
|
+
* after it, and the lender has a share snapshot for the tranche at it.
|
|
24
|
+
*/
|
|
25
|
+
active: boolean;
|
|
26
|
+
/** The lock's tranche shares as of the epoch (latest update at or before it). */
|
|
27
|
+
sharesAtEpoch: number;
|
|
28
|
+
/** The lock's tranche shares as of the epoch BEFORE it. */
|
|
29
|
+
sharesBeforeEpoch: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Everything one lender's share of one tranche's credited interest needs. */
|
|
33
|
+
export interface EpochTrancheYieldInput {
|
|
34
|
+
/** The tranche's interest credited at this epoch's close (net of the fee). */
|
|
35
|
+
epochInterest: number;
|
|
36
|
+
/** The lender's tranche shares as of the epoch (fixed-term locks included). */
|
|
37
|
+
userShares: number;
|
|
38
|
+
/** The tranche's total shares as of the epoch. */
|
|
39
|
+
trancheShares: number;
|
|
40
|
+
/**
|
|
41
|
+
* The tranche's CURRENT balance and shares: the price a fixed-term top-up
|
|
42
|
+
* (extra shares) is valued at. The subgraph keeps no per-epoch tranche
|
|
43
|
+
* balance, so an older top-up is valued at today's price — a second-order
|
|
44
|
+
* difference, because the top-up is only the fixed rate's excess over the
|
|
45
|
+
* variable one.
|
|
46
|
+
*/
|
|
47
|
+
trancheBalanceNow: number;
|
|
48
|
+
trancheSharesNow: number;
|
|
49
|
+
locks: readonly EpochLockShares[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface EpochTrancheYield {
|
|
53
|
+
/** `variable + Σ locks` — the lender's whole yield in the tranche. */
|
|
54
|
+
total: number;
|
|
55
|
+
/** The variable residual: the share-weighted base yield minus the locks' part of it. */
|
|
56
|
+
variable: number;
|
|
57
|
+
/** Each lock's yield, in the order of `input.locks`: its base part plus its top-up. */
|
|
58
|
+
locks: number[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The ONE per-epoch formula. `Portfolio.getPortfolioLendingData` runs it for
|
|
63
|
+
* the last settled epoch and `getYieldHistory` for every epoch of a window, so
|
|
64
|
+
* the two figures cannot drift apart.
|
|
65
|
+
*
|
|
66
|
+
* ```
|
|
67
|
+
* base = epochInterest × userShares / trancheShares
|
|
68
|
+
* lockBase = base × lock.sharesBeforeEpoch / userShares (active locks)
|
|
69
|
+
* topUp = (lock.sharesAtEpoch − lock.sharesBeforeEpoch) × trancheBalanceNow / trancheSharesNow
|
|
70
|
+
* variable = base − Σ lockBase
|
|
71
|
+
* total = variable + Σ (lockBase + topUp)
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* A zero denominator contributes 0 (a tranche or a lender with no shares
|
|
75
|
+
* earned nothing at that close), never `NaN`.
|
|
76
|
+
*/
|
|
77
|
+
export function epochTrancheYield(
|
|
78
|
+
input: EpochTrancheYieldInput,
|
|
79
|
+
): EpochTrancheYield {
|
|
80
|
+
const base =
|
|
81
|
+
input.trancheShares === 0
|
|
82
|
+
? 0
|
|
83
|
+
: (input.epochInterest * input.userShares) / input.trancheShares;
|
|
84
|
+
|
|
85
|
+
let variable = base;
|
|
86
|
+
const locks = input.locks.map((lock) => {
|
|
87
|
+
if (!lock.active) return 0;
|
|
88
|
+
|
|
89
|
+
const topUp =
|
|
90
|
+
input.trancheSharesNow === 0
|
|
91
|
+
? 0
|
|
92
|
+
: ((lock.sharesAtEpoch - lock.sharesBeforeEpoch) *
|
|
93
|
+
input.trancheBalanceNow) /
|
|
94
|
+
input.trancheSharesNow;
|
|
95
|
+
|
|
96
|
+
const lockBase =
|
|
97
|
+
input.userShares === 0
|
|
98
|
+
? 0
|
|
99
|
+
: (base * lock.sharesBeforeEpoch) / input.userShares;
|
|
100
|
+
|
|
101
|
+
variable -= lockBase;
|
|
102
|
+
|
|
103
|
+
return lockBase + topUp;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const total = locks.reduce((sum, lockYield) => sum + lockYield, variable);
|
|
107
|
+
|
|
108
|
+
return { total, variable, locks };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** One lender's settled yield in one tranche at one epoch. */
|
|
112
|
+
export interface YieldHistoryEntry {
|
|
113
|
+
/** The epoch (cycle) number, in the chain's numbering. */
|
|
114
|
+
epochId: number;
|
|
115
|
+
/**
|
|
116
|
+
* When the epoch ended — the next epoch's start — in unix SECONDS (not
|
|
117
|
+
* milliseconds).
|
|
118
|
+
*/
|
|
119
|
+
epochEnd: number;
|
|
120
|
+
/** Lowercased lending pool address. */
|
|
121
|
+
poolId: string;
|
|
122
|
+
/** Lowercased tranche address. */
|
|
123
|
+
trancheId: string;
|
|
124
|
+
/**
|
|
125
|
+
* What the close credited the lender in this tranche, AFTER the
|
|
126
|
+
* performance fee, in display units of the deployment's own stable token
|
|
127
|
+
* (AUDD on XDC AUDD — convert before formatting it as USD).
|
|
128
|
+
*/
|
|
129
|
+
netYield: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** One epoch's yield summed over the entries an aggregation kept. */
|
|
133
|
+
export interface YieldHistoryPoint {
|
|
134
|
+
/** The epoch (cycle) number. */
|
|
135
|
+
epochId: number;
|
|
136
|
+
/** The epoch's end, in unix SECONDS. */
|
|
137
|
+
epochEnd: number;
|
|
138
|
+
/** Net yield summed, in the deployment's own stable token. */
|
|
139
|
+
netYield: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface YieldHistoryFilter {
|
|
143
|
+
/** Keep only this pool (case-insensitive). */
|
|
144
|
+
poolId?: string;
|
|
145
|
+
/** Keep only this tranche (case-insensitive). */
|
|
146
|
+
trancheId?: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Pool/tranche entries → one point per epoch, ascending by `epochId`. With no
|
|
151
|
+
* filter it sums every strategy (the Lend tab's bars); with `poolId` it is one
|
|
152
|
+
* strategy (a strategy page's bars).
|
|
153
|
+
*
|
|
154
|
+
* An epoch appears only if a kept entry has it — there is no zero padding, so
|
|
155
|
+
* an empty input (a new lender) is an empty output.
|
|
156
|
+
*
|
|
157
|
+
* For a fixed-term lock holder the bars can sum to slightly MORE than the
|
|
158
|
+
* lender's lifetime interest (up to about 0.2 %): each top-up is valued at the
|
|
159
|
+
* tranche's CURRENT share price, which has grown since. Show "earned to date"
|
|
160
|
+
* from the lifetime figure, never from this sum.
|
|
161
|
+
*/
|
|
162
|
+
export function aggregateYieldHistory(
|
|
163
|
+
entries: readonly YieldHistoryEntry[],
|
|
164
|
+
filter: YieldHistoryFilter = {},
|
|
165
|
+
): YieldHistoryPoint[] {
|
|
166
|
+
const poolId = filter.poolId?.toLowerCase();
|
|
167
|
+
const trancheId = filter.trancheId?.toLowerCase();
|
|
168
|
+
const byEpoch = new Map<number, YieldHistoryPoint>();
|
|
169
|
+
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
if (poolId !== undefined && entry.poolId.toLowerCase() !== poolId) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (
|
|
175
|
+
trancheId !== undefined &&
|
|
176
|
+
entry.trancheId.toLowerCase() !== trancheId
|
|
177
|
+
) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const point = byEpoch.get(entry.epochId);
|
|
182
|
+
if (point) {
|
|
183
|
+
point.netYield += entry.netYield;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
byEpoch.set(entry.epochId, {
|
|
187
|
+
epochId: entry.epochId,
|
|
188
|
+
epochEnd: entry.epochEnd,
|
|
189
|
+
netYield: entry.netYield,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return [...byEpoch.values()].sort((a, b) => a.epochId - b.epochId);
|
|
194
|
+
}
|