@kasufinance/kasu-sdk 2.14.0 → 2.16.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 +45 -5
- package/dist/bundle.cjs.js +1484 -228
- package/dist/bundle.esm.js +1476 -229
- package/dist/domain/index.d.ts +6 -1
- 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.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/domain/yield-history.d.ts +25 -1
- package/dist/domain/yield-history.js +41 -11
- package/dist/domain/yield-history.js.map +1 -1
- package/dist/facade/index.d.ts +1 -1
- package/dist/facade/index.js +2 -0
- package/dist/facade/index.js.map +1 -1
- package/dist/facade/strategies.d.ts +85 -3
- package/dist/facade/strategies.js +182 -6
- package/dist/facade/strategies.js.map +1 -1
- 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.js.map +1 -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.js +89 -0
- package/dist/services/DataService/tvl-history.js.map +1 -1
- package/dist/services/Portfolio/portfolio.d.ts +54 -1
- package/dist/services/Portfolio/portfolio.js +357 -80
- package/dist/services/Portfolio/portfolio.js.map +1 -1
- package/dist/services/Portfolio/queries.d.ts +16 -0
- package/dist/services/Portfolio/queries.js +119 -2
- package/dist/services/Portfolio/queries.js.map +1 -1
- package/dist/services/Portfolio/types.d.ts +94 -6
- package/dist/services/Portfolio/yield-history.d.ts +148 -10
- package/dist/services/Portfolio/yield-history.js +241 -31
- package/dist/services/Portfolio/yield-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.d.ts +16 -0
- package/dist/services/UserLending/user-lending.js +96 -16
- package/dist/services/UserLending/user-lending.js.map +1 -1
- package/dist/services/shared.d.ts +7 -0
- package/dist/services/shared.js +48 -0
- package/dist/services/shared.js.map +1 -1
- package/package.json +1 -1
- package/src/domain/index.ts +27 -1
- package/src/domain/loan-contract.ts +16 -5
- package/src/domain/strategy-details.ts +528 -0
- package/src/domain/yield-history.ts +47 -11
- package/src/facade/index.ts +3 -0
- package/src/facade/strategies.ts +205 -10
- package/src/facade/types.ts +85 -0
- package/src/flows/deposit-flow.ts +19 -5
- package/src/services/Portfolio/portfolio.ts +559 -126
- package/src/services/Portfolio/queries.ts +119 -2
- package/src/services/Portfolio/types.ts +91 -6
- package/src/services/Portfolio/yield-history.ts +405 -56
- package/src/services/UserLending/user-lending.ts +46 -0
- package/src/services/shared.ts +48 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { YieldHistoryEntry } from '../../domain/yield-history';
|
|
1
|
+
import { EpochTrancheYield, YieldHistoryEntry } from '../../domain/yield-history';
|
|
2
2
|
/**
|
|
3
3
|
* Which settled epochs a history covers. Epochs are counted in the chain's
|
|
4
4
|
* numbering (`SystemVariables.currentEpochNumber`). Only SETTLED epochs exist
|
|
@@ -40,13 +40,20 @@ export declare function resolveYieldHistoryWindow(options: YieldHistoryOptions,
|
|
|
40
40
|
*/
|
|
41
41
|
export declare const yieldHistoryPositionsQuery: string;
|
|
42
42
|
/**
|
|
43
|
-
* Round trip 2 onwards: the
|
|
43
|
+
* Round trip 2 onwards: the six per-epoch series, one page of each per
|
|
44
44
|
* request, cursor-paginated by `id`. A series that is exhausted is skipped with
|
|
45
45
|
* `@include`, so a history costs as many requests as its LONGEST series has
|
|
46
46
|
* pages — normally one — never one per epoch.
|
|
47
47
|
*
|
|
48
48
|
* Share snapshots are read up to the window's end with no lower bound: the
|
|
49
|
-
* value AS OF the first epoch may come from an update long before it.
|
|
49
|
+
* value AS OF the first epoch may come from an update long before it. Each
|
|
50
|
+
* carries its `timestamp`, and the window's clearings their start, because a
|
|
51
|
+
* cycle's interest is paired with the shares in force when the clearing
|
|
52
|
+
* APPLIED it (`sharesAtInterest`), which the epoch tag alone gets wrong for a
|
|
53
|
+
* withdrawal accepted between clearings — so the lender's updates are read one
|
|
54
|
+
* tag past the window (`$userSharesToEpoch`), where the subgraph puts it.
|
|
55
|
+
* Every share exit between clearings is read too (`trancheSharesAtInterest`
|
|
56
|
+
* rebuilds the tranche total from the previous clearing's snapshot).
|
|
50
57
|
*/
|
|
51
58
|
export declare const yieldHistoryPageQuery: string;
|
|
52
59
|
export interface YieldHistoryPositionsResult {
|
|
@@ -57,6 +64,8 @@ export interface YieldHistoryPositionsResult {
|
|
|
57
64
|
};
|
|
58
65
|
lendingPoolTrancheUserDetails: {
|
|
59
66
|
id: string;
|
|
67
|
+
/** The ledger's share count (accepted deposits − withdrawals ± top-ups). */
|
|
68
|
+
shares?: string;
|
|
60
69
|
tranche: {
|
|
61
70
|
id: string;
|
|
62
71
|
balance: string;
|
|
@@ -71,6 +80,12 @@ export interface YieldHistoryPositionsResult {
|
|
|
71
80
|
}[];
|
|
72
81
|
}[];
|
|
73
82
|
} | null;
|
|
83
|
+
/** The block the subgraph answered at. */
|
|
84
|
+
_meta?: {
|
|
85
|
+
block: {
|
|
86
|
+
number: number;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
74
89
|
}
|
|
75
90
|
/** One share snapshot: the value from `epochId` on, until the next one. */
|
|
76
91
|
export interface ShareUpdateRow {
|
|
@@ -79,6 +94,13 @@ export interface ShareUpdateRow {
|
|
|
79
94
|
shareUpdatesIndex: number;
|
|
80
95
|
shares: string;
|
|
81
96
|
}
|
|
97
|
+
/** A tranche or lender share snapshot, with the block time it was written at. */
|
|
98
|
+
export interface TimedShareUpdateRow extends ShareUpdateRow {
|
|
99
|
+
/** Unix seconds. */
|
|
100
|
+
timestamp: string;
|
|
101
|
+
}
|
|
102
|
+
/** What the share selection reads from a snapshot — no entity id needed. */
|
|
103
|
+
export type TimedShareSnapshot = Pick<TimedShareUpdateRow, 'epochId' | 'shareUpdatesIndex' | 'shares' | 'timestamp'>;
|
|
82
104
|
export interface InterestRow {
|
|
83
105
|
id: string;
|
|
84
106
|
epochId: string;
|
|
@@ -87,14 +109,40 @@ export interface InterestRow {
|
|
|
87
109
|
id: string;
|
|
88
110
|
};
|
|
89
111
|
}
|
|
112
|
+
/** A pool's clearing for one epoch: its first transaction applied the interest. */
|
|
113
|
+
export interface ClearingRow {
|
|
114
|
+
id: string;
|
|
115
|
+
epochId: string;
|
|
116
|
+
/** Unix seconds of the clearing's first transaction. */
|
|
117
|
+
startTimestamp: string;
|
|
118
|
+
lendingPool: {
|
|
119
|
+
id: string;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Shares leaving a tranche (any lender): an accepted withdrawal or an
|
|
124
|
+
* immediate one. Each carries the pool's clearing epoch at the time
|
|
125
|
+
* (`epochClearingId`) — so one between clearings E − 1 and E is tagged E.
|
|
126
|
+
*/
|
|
127
|
+
export interface ShareExitRow {
|
|
128
|
+
id: string;
|
|
129
|
+
type: 'WithdrawalAccepted' | 'ImmediateWithdrawalExecuted';
|
|
130
|
+
epochId: string;
|
|
131
|
+
/** Unix seconds. */
|
|
132
|
+
createdOn: string;
|
|
133
|
+
sharesAmount: string | null;
|
|
134
|
+
tranche: {
|
|
135
|
+
id: string;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
90
138
|
export interface YieldHistoryPageResult {
|
|
91
139
|
interest?: InterestRow[];
|
|
92
|
-
trancheShares?: (
|
|
140
|
+
trancheShares?: (TimedShareUpdateRow & {
|
|
93
141
|
lendingPoolTranche: {
|
|
94
142
|
id: string;
|
|
95
143
|
};
|
|
96
144
|
})[];
|
|
97
|
-
userShares?: (
|
|
145
|
+
userShares?: (TimedShareUpdateRow & {
|
|
98
146
|
lendingPoolTrancheUserDetails: {
|
|
99
147
|
id: string;
|
|
100
148
|
};
|
|
@@ -104,17 +152,19 @@ export interface YieldHistoryPageResult {
|
|
|
104
152
|
id: string;
|
|
105
153
|
};
|
|
106
154
|
})[];
|
|
155
|
+
clearings?: ClearingRow[];
|
|
156
|
+
withdrawals?: ShareExitRow[];
|
|
107
157
|
}
|
|
108
158
|
/** Everything `buildYieldHistory` reads, keyed by the subgraph's entity ids. */
|
|
109
159
|
export interface YieldHistoryRead {
|
|
110
160
|
positions: YieldHistoryPositionsResult;
|
|
111
161
|
interest: InterestRow[];
|
|
112
|
-
trancheShares: (
|
|
162
|
+
trancheShares: (TimedShareUpdateRow & {
|
|
113
163
|
lendingPoolTranche: {
|
|
114
164
|
id: string;
|
|
115
165
|
};
|
|
116
166
|
})[];
|
|
117
|
-
userShares: (
|
|
167
|
+
userShares: (TimedShareUpdateRow & {
|
|
118
168
|
lendingPoolTrancheUserDetails: {
|
|
119
169
|
id: string;
|
|
120
170
|
};
|
|
@@ -124,6 +174,8 @@ export interface YieldHistoryRead {
|
|
|
124
174
|
id: string;
|
|
125
175
|
};
|
|
126
176
|
})[];
|
|
177
|
+
clearings: ClearingRow[];
|
|
178
|
+
withdrawals: ShareExitRow[];
|
|
127
179
|
}
|
|
128
180
|
/** The chain's epoch clock: epoch E ends at `epochZeroStart + (E + 1) × duration`. */
|
|
129
181
|
export interface EpochClock {
|
|
@@ -132,10 +184,11 @@ export interface EpochClock {
|
|
|
132
184
|
}
|
|
133
185
|
type PageRequest = (variables: Record<string, unknown>) => Promise<YieldHistoryPageResult>;
|
|
134
186
|
/**
|
|
135
|
-
* Fetch the
|
|
187
|
+
* Fetch the six series for the window, paginating each by `id` until a short
|
|
136
188
|
* page. Returns the rows and how many requests it took.
|
|
137
189
|
*/
|
|
138
190
|
export declare function fetchYieldHistorySeries(request: PageRequest, ids: {
|
|
191
|
+
pools: string[];
|
|
139
192
|
tranches: string[];
|
|
140
193
|
userTranches: string[];
|
|
141
194
|
locks: string[];
|
|
@@ -149,12 +202,97 @@ export declare function fetchYieldHistorySeries(request: PageRequest, ids: {
|
|
|
149
202
|
* `lendingPortfolioQuery` selects with `orderBy: shareUpdatesIndex, desc,
|
|
150
203
|
* first: 1, where: { epochId_lt: epoch + 1 }`. `undefined` when none.
|
|
151
204
|
*/
|
|
152
|
-
export declare function sharesAsOf(updates: readonly
|
|
205
|
+
export declare function sharesAsOf<T extends Pick<ShareUpdateRow, 'epochId' | 'shareUpdatesIndex' | 'shares'>>(updates: readonly T[], epoch: number): T | undefined;
|
|
206
|
+
/**
|
|
207
|
+
* The snapshot in force when the clearing of `epoch` APPLIED that epoch's
|
|
208
|
+
* interest — the shares the interest was actually spread over.
|
|
209
|
+
*
|
|
210
|
+
* A clearing applies interest FIRST (`ClearingCoordinator.doClearing`: the
|
|
211
|
+
* `applyInterests` call before steps 1–5), then accepts deposits and
|
|
212
|
+
* withdrawals. So an update is in force for epoch E's interest when it is
|
|
213
|
+
* tagged `≤ E` (the subgraph tags every update written during clearing E, or
|
|
214
|
+
* after it, `E + 1` or later), OR when it was written before clearing E began
|
|
215
|
+
* (`timestamp < clearingStart`). The second test is the one that matters: a
|
|
216
|
+
* withdrawal the pool manager accepts BETWEEN clearings
|
|
217
|
+
* (`forceAcceptWithdrawalRequest`) is tagged `E + 1` by the subgraph, as if a
|
|
218
|
+
* clearing had accepted it, yet the shares were gone before clearing E.
|
|
219
|
+
*
|
|
220
|
+
* Without a clearing start (none recorded) it is `sharesAsOf` — the tag alone.
|
|
221
|
+
*/
|
|
222
|
+
export declare function sharesAtInterest<T extends TimedShareSnapshot>(updates: readonly T[], epoch: number, clearingStart: number | undefined): T | undefined;
|
|
223
|
+
/**
|
|
224
|
+
* The tranche's total shares when the clearing of `epoch` applied its
|
|
225
|
+
* interest.
|
|
226
|
+
*
|
|
227
|
+
* The subgraph writes a tranche snapshot when a clearing ENDS (tagged with
|
|
228
|
+
* the next epoch) and at each immediate withdrawal, but none when the manager
|
|
229
|
+
* accepts a withdrawal between clearings — so the newest snapshot can predate
|
|
230
|
+
* shares that had already left. Instead of guessing the order of events in a
|
|
231
|
+
* block, the total is rebuilt from the one snapshot whose place is certain:
|
|
232
|
+
* the FIRST one tagged `epoch`, written when clearing `epoch − 1` ended (the
|
|
233
|
+
* tag itself starts there). Every share exit between that and clearing
|
|
234
|
+
* `epoch` is tagged `epoch` (the pool's clearing epoch at the time), and none
|
|
235
|
+
* can happen during a clearing (`notPendingClearing`,
|
|
236
|
+
* `verifyClearingNotPending`), while deposits and top-ups only happen inside
|
|
237
|
+
* one. So: that snapshot less every exit tagged `epoch` before the clearing's
|
|
238
|
+
* start (the clearing's own acceptances come at or after it).
|
|
239
|
+
*
|
|
240
|
+
* Without a clearing start, or without a snapshot tagged `epoch` (a tranche's
|
|
241
|
+
* first clearing), it is the snapshot in force by tag alone.
|
|
242
|
+
*/
|
|
243
|
+
export declare function trancheSharesAtInterest(snapshots: readonly TimedShareSnapshot[], exits: readonly Pick<ShareExitRow, 'epochId' | 'createdOn' | 'sharesAmount'>[], epoch: number, clearingStart: number | undefined): number;
|
|
244
|
+
/** A fixed-term lock inside one lender's tranche position. */
|
|
245
|
+
export interface PositionLock {
|
|
246
|
+
epochLockStart: string;
|
|
247
|
+
epochLockEnd: string;
|
|
248
|
+
initialTrancheShares: string;
|
|
249
|
+
/** The lock's share snapshots (paired by tag: its tags are right). */
|
|
250
|
+
updates: readonly Pick<ShareUpdateRow, 'epochId' | 'shareUpdatesIndex' | 'shares'>[];
|
|
251
|
+
}
|
|
252
|
+
/** Everything one lender's tranche position needs to price one epoch. */
|
|
253
|
+
export interface PositionSeries {
|
|
254
|
+
trancheBalanceNow: number;
|
|
255
|
+
trancheSharesNow: number;
|
|
256
|
+
userUpdates: readonly TimedShareSnapshot[];
|
|
257
|
+
trancheUpdates: readonly TimedShareSnapshot[];
|
|
258
|
+
exits: readonly Pick<ShareExitRow, 'epochId' | 'createdOn' | 'sharesAmount'>[];
|
|
259
|
+
locks: readonly PositionLock[];
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* THE per-position, per-epoch selection both `getYieldHistory` and
|
|
263
|
+
* `getPortfolioLendingData` run: the shares in force when clearing `epoch`
|
|
264
|
+
* applied its interest (`sharesAtInterest`, `trancheSharesAtInterest`), each
|
|
265
|
+
* lock's snapshots as of `epoch` and `epoch − 1`, then `epochTrancheYield`.
|
|
266
|
+
*/
|
|
267
|
+
export declare function positionEpochYield(series: PositionSeries, epoch: number, epochInterest: number, clearingStart: number | undefined): {
|
|
268
|
+
yield: EpochTrancheYield;
|
|
269
|
+
userShares: number;
|
|
270
|
+
};
|
|
271
|
+
/** One lender's tranche position, as the positions and portfolio queries read it. */
|
|
272
|
+
export interface PositionRef {
|
|
273
|
+
id: string;
|
|
274
|
+
tranche: {
|
|
275
|
+
id: string;
|
|
276
|
+
balance: string;
|
|
277
|
+
shares: string;
|
|
278
|
+
};
|
|
279
|
+
userLendingPoolTrancheFixedTermDepositLocks: {
|
|
280
|
+
id?: string;
|
|
281
|
+
epochLockStart: string;
|
|
282
|
+
epochLockEnd: string;
|
|
283
|
+
initialTrancheShares: string;
|
|
284
|
+
}[];
|
|
285
|
+
}
|
|
286
|
+
/** A position's `PositionSeries` out of the fetched history series. */
|
|
287
|
+
export declare function positionSeriesFrom(rows: Omit<YieldHistoryRead, 'positions'>, position: PositionRef): PositionSeries;
|
|
288
|
+
/** `pool → epoch → clearing start`, from the clearing rows. */
|
|
289
|
+
export declare function clearingStarts(clearings: readonly ClearingRow[]): Map<string, Map<number, number>>;
|
|
153
290
|
/**
|
|
154
291
|
* Pure: the fetched series → one entry per pool, tranche and settled epoch in
|
|
155
292
|
* which the lender held shares (or a lock earned a top-up). Each entry runs the
|
|
156
293
|
* same `epochTrancheYield` as the portfolio's last-epoch figure, with the same
|
|
157
|
-
* share selection, so the
|
|
294
|
+
* share selection (`sharesAtInterest`, `trancheSharesAtInterest`), so the
|
|
295
|
+
* newest entry equals that figure.
|
|
158
296
|
*
|
|
159
297
|
* Sorted by `epochId`, then `poolId`, then `trancheId`.
|
|
160
298
|
*/
|
|
@@ -72,6 +72,7 @@ export const yieldHistoryPositionsQuery = gql `
|
|
|
72
72
|
}
|
|
73
73
|
lendingPoolTrancheUserDetails(first: $first) {
|
|
74
74
|
id
|
|
75
|
+
shares
|
|
75
76
|
tranche {
|
|
76
77
|
id
|
|
77
78
|
balance
|
|
@@ -86,32 +87,50 @@ export const yieldHistoryPositionsQuery = gql `
|
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
}
|
|
90
|
+
_meta {
|
|
91
|
+
block {
|
|
92
|
+
number
|
|
93
|
+
}
|
|
94
|
+
}
|
|
89
95
|
}
|
|
90
96
|
`;
|
|
91
97
|
/**
|
|
92
|
-
* Round trip 2 onwards: the
|
|
98
|
+
* Round trip 2 onwards: the six per-epoch series, one page of each per
|
|
93
99
|
* request, cursor-paginated by `id`. A series that is exhausted is skipped with
|
|
94
100
|
* `@include`, so a history costs as many requests as its LONGEST series has
|
|
95
101
|
* pages — normally one — never one per epoch.
|
|
96
102
|
*
|
|
97
103
|
* 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.
|
|
104
|
+
* value AS OF the first epoch may come from an update long before it. Each
|
|
105
|
+
* carries its `timestamp`, and the window's clearings their start, because a
|
|
106
|
+
* cycle's interest is paired with the shares in force when the clearing
|
|
107
|
+
* APPLIED it (`sharesAtInterest`), which the epoch tag alone gets wrong for a
|
|
108
|
+
* withdrawal accepted between clearings — so the lender's updates are read one
|
|
109
|
+
* tag past the window (`$userSharesToEpoch`), where the subgraph puts it.
|
|
110
|
+
* Every share exit between clearings is read too (`trancheSharesAtInterest`
|
|
111
|
+
* rebuilds the tranche total from the previous clearing's snapshot).
|
|
99
112
|
*/
|
|
100
113
|
export const yieldHistoryPageQuery = gql `
|
|
101
114
|
query YieldHistoryPage(
|
|
115
|
+
$pools: [String!]!
|
|
102
116
|
$tranches: [String!]!
|
|
103
117
|
$userTranches: [String!]!
|
|
104
118
|
$locks: [String!]!
|
|
105
119
|
$fromEpoch: BigInt!
|
|
106
120
|
$toEpoch: BigInt!
|
|
121
|
+
$userSharesToEpoch: BigInt!
|
|
107
122
|
$interestCursor: ID!
|
|
108
123
|
$trancheSharesCursor: ID!
|
|
109
124
|
$userSharesCursor: ID!
|
|
110
125
|
$lockSharesCursor: ID!
|
|
126
|
+
$clearingsCursor: ID!
|
|
127
|
+
$withdrawalsCursor: ID!
|
|
111
128
|
$withInterest: Boolean!
|
|
112
129
|
$withTrancheShares: Boolean!
|
|
113
130
|
$withUserShares: Boolean!
|
|
114
131
|
$withLockShares: Boolean!
|
|
132
|
+
$withClearings: Boolean!
|
|
133
|
+
$withWithdrawals: Boolean!
|
|
115
134
|
$first: Int!
|
|
116
135
|
) {
|
|
117
136
|
interest: lendingPoolTrancheEpochInterests(
|
|
@@ -146,6 +165,7 @@ export const yieldHistoryPageQuery = gql `
|
|
|
146
165
|
epochId
|
|
147
166
|
shareUpdatesIndex
|
|
148
167
|
shares
|
|
168
|
+
timestamp
|
|
149
169
|
lendingPoolTranche {
|
|
150
170
|
id
|
|
151
171
|
}
|
|
@@ -157,13 +177,14 @@ export const yieldHistoryPageQuery = gql `
|
|
|
157
177
|
where: {
|
|
158
178
|
id_gt: $userSharesCursor
|
|
159
179
|
lendingPoolTrancheUserDetails_in: $userTranches
|
|
160
|
-
epochId_lte: $
|
|
180
|
+
epochId_lte: $userSharesToEpoch
|
|
161
181
|
}
|
|
162
182
|
) @include(if: $withUserShares) {
|
|
163
183
|
id
|
|
164
184
|
epochId
|
|
165
185
|
shareUpdatesIndex
|
|
166
186
|
shares
|
|
187
|
+
timestamp
|
|
167
188
|
lendingPoolTrancheUserDetails {
|
|
168
189
|
id
|
|
169
190
|
}
|
|
@@ -186,10 +207,49 @@ export const yieldHistoryPageQuery = gql `
|
|
|
186
207
|
id
|
|
187
208
|
}
|
|
188
209
|
}
|
|
210
|
+
clearings: lendingPoolClearings(
|
|
211
|
+
first: $first
|
|
212
|
+
orderBy: id
|
|
213
|
+
orderDirection: asc
|
|
214
|
+
where: {
|
|
215
|
+
id_gt: $clearingsCursor
|
|
216
|
+
lendingPool_in: $pools
|
|
217
|
+
epochId_gte: $fromEpoch
|
|
218
|
+
epochId_lte: $toEpoch
|
|
219
|
+
}
|
|
220
|
+
) @include(if: $withClearings) {
|
|
221
|
+
id
|
|
222
|
+
epochId
|
|
223
|
+
startTimestamp
|
|
224
|
+
lendingPool {
|
|
225
|
+
id
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
withdrawals: userRequestEvents(
|
|
229
|
+
first: $first
|
|
230
|
+
orderBy: id
|
|
231
|
+
orderDirection: asc
|
|
232
|
+
where: {
|
|
233
|
+
id_gt: $withdrawalsCursor
|
|
234
|
+
type_in: [WithdrawalAccepted, ImmediateWithdrawalExecuted]
|
|
235
|
+
tranche_in: $tranches
|
|
236
|
+
epochId_gte: $fromEpoch
|
|
237
|
+
epochId_lte: $toEpoch
|
|
238
|
+
}
|
|
239
|
+
) @include(if: $withWithdrawals) {
|
|
240
|
+
id
|
|
241
|
+
type
|
|
242
|
+
epochId
|
|
243
|
+
createdOn
|
|
244
|
+
sharesAmount
|
|
245
|
+
tranche {
|
|
246
|
+
id
|
|
247
|
+
}
|
|
248
|
+
}
|
|
189
249
|
}
|
|
190
250
|
`;
|
|
191
251
|
/**
|
|
192
|
-
* Fetch the
|
|
252
|
+
* Fetch the six series for the window, paginating each by `id` until a short
|
|
193
253
|
* page. Returns the rows and how many requests it took.
|
|
194
254
|
*/
|
|
195
255
|
export function fetchYieldHistorySeries(request, ids, window) {
|
|
@@ -200,6 +260,8 @@ export function fetchYieldHistorySeries(request, ids, window) {
|
|
|
200
260
|
trancheShares: [],
|
|
201
261
|
userShares: [],
|
|
202
262
|
lockShares: [],
|
|
263
|
+
clearings: [],
|
|
264
|
+
withdrawals: [],
|
|
203
265
|
};
|
|
204
266
|
const series = [
|
|
205
267
|
{ key: 'interest', flag: 'withInterest', cursor: 'interestCursor' },
|
|
@@ -218,18 +280,32 @@ export function fetchYieldHistorySeries(request, ids, window) {
|
|
|
218
280
|
flag: 'withLockShares',
|
|
219
281
|
cursor: 'lockSharesCursor',
|
|
220
282
|
},
|
|
283
|
+
{
|
|
284
|
+
key: 'clearings',
|
|
285
|
+
flag: 'withClearings',
|
|
286
|
+
cursor: 'clearingsCursor',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
key: 'withdrawals',
|
|
290
|
+
flag: 'withWithdrawals',
|
|
291
|
+
cursor: 'withdrawalsCursor',
|
|
292
|
+
},
|
|
221
293
|
];
|
|
222
294
|
const cursors = {
|
|
223
295
|
interest: '',
|
|
224
296
|
trancheShares: '',
|
|
225
297
|
userShares: '',
|
|
226
298
|
lockShares: '',
|
|
299
|
+
clearings: '',
|
|
300
|
+
withdrawals: '',
|
|
227
301
|
};
|
|
228
302
|
const open = {
|
|
229
303
|
interest: ids.tranches.length > 0,
|
|
230
304
|
trancheShares: ids.tranches.length > 0,
|
|
231
305
|
userShares: ids.userTranches.length > 0,
|
|
232
306
|
lockShares: ids.locks.length > 0,
|
|
307
|
+
clearings: ids.pools.length > 0,
|
|
308
|
+
withdrawals: ids.tranches.length > 0,
|
|
233
309
|
};
|
|
234
310
|
let requests = 0;
|
|
235
311
|
while (series.some(({ key }) => open[key])) {
|
|
@@ -237,11 +313,15 @@ export function fetchYieldHistorySeries(request, ids, window) {
|
|
|
237
313
|
throw new Error(`getYieldHistory: more than ${String(YIELD_HISTORY_MAX_PAGES)} pages for one lender`);
|
|
238
314
|
}
|
|
239
315
|
const variables = {
|
|
316
|
+
pools: ids.pools,
|
|
240
317
|
tranches: ids.tranches,
|
|
241
318
|
userTranches: ids.userTranches,
|
|
242
319
|
locks: ids.locks,
|
|
243
320
|
fromEpoch: String(window.fromEpoch),
|
|
244
321
|
toEpoch: String(window.toEpoch),
|
|
322
|
+
// One tag further for the lender: a withdrawal accepted between
|
|
323
|
+
// clearings is tagged with the epoch AFTER the one it counts in.
|
|
324
|
+
userSharesToEpoch: String(window.toEpoch + 1),
|
|
245
325
|
first: YIELD_HISTORY_PAGE_SIZE,
|
|
246
326
|
};
|
|
247
327
|
for (const { key, flag, cursor } of series) {
|
|
@@ -286,6 +366,144 @@ export function sharesAsOf(updates, epoch) {
|
|
|
286
366
|
}
|
|
287
367
|
return found;
|
|
288
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* The snapshot in force when the clearing of `epoch` APPLIED that epoch's
|
|
371
|
+
* interest — the shares the interest was actually spread over.
|
|
372
|
+
*
|
|
373
|
+
* A clearing applies interest FIRST (`ClearingCoordinator.doClearing`: the
|
|
374
|
+
* `applyInterests` call before steps 1–5), then accepts deposits and
|
|
375
|
+
* withdrawals. So an update is in force for epoch E's interest when it is
|
|
376
|
+
* tagged `≤ E` (the subgraph tags every update written during clearing E, or
|
|
377
|
+
* after it, `E + 1` or later), OR when it was written before clearing E began
|
|
378
|
+
* (`timestamp < clearingStart`). The second test is the one that matters: a
|
|
379
|
+
* withdrawal the pool manager accepts BETWEEN clearings
|
|
380
|
+
* (`forceAcceptWithdrawalRequest`) is tagged `E + 1` by the subgraph, as if a
|
|
381
|
+
* clearing had accepted it, yet the shares were gone before clearing E.
|
|
382
|
+
*
|
|
383
|
+
* Without a clearing start (none recorded) it is `sharesAsOf` — the tag alone.
|
|
384
|
+
*/
|
|
385
|
+
export function sharesAtInterest(updates, epoch, clearingStart) {
|
|
386
|
+
let found;
|
|
387
|
+
for (const update of updates) {
|
|
388
|
+
const inForce = Number(update.epochId) <= epoch ||
|
|
389
|
+
(clearingStart !== undefined &&
|
|
390
|
+
Number(update.timestamp) < clearingStart);
|
|
391
|
+
if (!inForce)
|
|
392
|
+
continue;
|
|
393
|
+
if (!found || update.shareUpdatesIndex > found.shareUpdatesIndex) {
|
|
394
|
+
found = update;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return found;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* The tranche's total shares when the clearing of `epoch` applied its
|
|
401
|
+
* interest.
|
|
402
|
+
*
|
|
403
|
+
* The subgraph writes a tranche snapshot when a clearing ENDS (tagged with
|
|
404
|
+
* the next epoch) and at each immediate withdrawal, but none when the manager
|
|
405
|
+
* accepts a withdrawal between clearings — so the newest snapshot can predate
|
|
406
|
+
* shares that had already left. Instead of guessing the order of events in a
|
|
407
|
+
* block, the total is rebuilt from the one snapshot whose place is certain:
|
|
408
|
+
* the FIRST one tagged `epoch`, written when clearing `epoch − 1` ended (the
|
|
409
|
+
* tag itself starts there). Every share exit between that and clearing
|
|
410
|
+
* `epoch` is tagged `epoch` (the pool's clearing epoch at the time), and none
|
|
411
|
+
* can happen during a clearing (`notPendingClearing`,
|
|
412
|
+
* `verifyClearingNotPending`), while deposits and top-ups only happen inside
|
|
413
|
+
* one. So: that snapshot less every exit tagged `epoch` before the clearing's
|
|
414
|
+
* start (the clearing's own acceptances come at or after it).
|
|
415
|
+
*
|
|
416
|
+
* Without a clearing start, or without a snapshot tagged `epoch` (a tranche's
|
|
417
|
+
* first clearing), it is the snapshot in force by tag alone.
|
|
418
|
+
*/
|
|
419
|
+
export function trancheSharesAtInterest(snapshots, exits, epoch, clearingStart) {
|
|
420
|
+
var _a, _b, _c;
|
|
421
|
+
let base;
|
|
422
|
+
if (clearingStart !== undefined) {
|
|
423
|
+
for (const snapshot of snapshots) {
|
|
424
|
+
if (Number(snapshot.epochId) !== epoch)
|
|
425
|
+
continue;
|
|
426
|
+
if (!base || snapshot.shareUpdatesIndex < base.shareUpdatesIndex) {
|
|
427
|
+
base = snapshot;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (clearingStart === undefined || !base) {
|
|
432
|
+
return parseFloat((_b = (_a = sharesAsOf(snapshots, epoch)) === null || _a === void 0 ? void 0 : _a.shares) !== null && _b !== void 0 ? _b : '0');
|
|
433
|
+
}
|
|
434
|
+
let shares = parseFloat(base.shares);
|
|
435
|
+
for (const exit of exits) {
|
|
436
|
+
if (Number(exit.epochId) === epoch &&
|
|
437
|
+
Number(exit.createdOn) < clearingStart) {
|
|
438
|
+
shares -= parseFloat((_c = exit.sharesAmount) !== null && _c !== void 0 ? _c : '0');
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return shares;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* THE per-position, per-epoch selection both `getYieldHistory` and
|
|
445
|
+
* `getPortfolioLendingData` run: the shares in force when clearing `epoch`
|
|
446
|
+
* applied its interest (`sharesAtInterest`, `trancheSharesAtInterest`), each
|
|
447
|
+
* lock's snapshots as of `epoch` and `epoch − 1`, then `epochTrancheYield`.
|
|
448
|
+
*/
|
|
449
|
+
export function positionEpochYield(series, epoch, epochInterest, clearingStart) {
|
|
450
|
+
var _a;
|
|
451
|
+
const userSnapshot = sharesAtInterest(series.userUpdates, epoch, clearingStart);
|
|
452
|
+
const userShares = parseFloat((_a = userSnapshot === null || userSnapshot === void 0 ? void 0 : userSnapshot.shares) !== null && _a !== void 0 ? _a : '0');
|
|
453
|
+
const locks = series.locks.map((lock) => {
|
|
454
|
+
var _a, _b, _c, _d;
|
|
455
|
+
return ({
|
|
456
|
+
active: parseFloat(lock.epochLockStart) < epoch &&
|
|
457
|
+
parseFloat(lock.epochLockEnd) >= epoch &&
|
|
458
|
+
userSnapshot !== undefined,
|
|
459
|
+
// No snapshot yet = the lock's opening shares: a lock made after
|
|
460
|
+
// clearing E starts at E but its first update is tagged E + 1, and
|
|
461
|
+
// reading that gap as 0 would book its principal as a top-up.
|
|
462
|
+
sharesAtEpoch: parseFloat((_b = (_a = sharesAsOf(lock.updates, epoch)) === null || _a === void 0 ? void 0 : _a.shares) !== null && _b !== void 0 ? _b : lock.initialTrancheShares),
|
|
463
|
+
sharesBeforeEpoch: parseFloat((_d = (_c = sharesAsOf(lock.updates, epoch - 1)) === null || _c === void 0 ? void 0 : _c.shares) !== null && _d !== void 0 ? _d : lock.initialTrancheShares),
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
return {
|
|
467
|
+
yield: epochTrancheYield({
|
|
468
|
+
epochInterest,
|
|
469
|
+
userShares,
|
|
470
|
+
trancheShares: trancheSharesAtInterest(series.trancheUpdates, series.exits, epoch, clearingStart),
|
|
471
|
+
trancheBalanceNow: series.trancheBalanceNow,
|
|
472
|
+
trancheSharesNow: series.trancheSharesNow,
|
|
473
|
+
locks,
|
|
474
|
+
}),
|
|
475
|
+
userShares,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
/** A position's `PositionSeries` out of the fetched history series. */
|
|
479
|
+
export function positionSeriesFrom(rows, position) {
|
|
480
|
+
const trancheId = position.tranche.id;
|
|
481
|
+
return {
|
|
482
|
+
trancheBalanceNow: parseFloat(position.tranche.balance),
|
|
483
|
+
trancheSharesNow: parseFloat(position.tranche.shares),
|
|
484
|
+
userUpdates: rows.userShares.filter((row) => row.lendingPoolTrancheUserDetails.id === position.id),
|
|
485
|
+
trancheUpdates: rows.trancheShares.filter((row) => row.lendingPoolTranche.id === trancheId),
|
|
486
|
+
exits: rows.withdrawals.filter((row) => row.tranche.id === trancheId),
|
|
487
|
+
locks: position.userLendingPoolTrancheFixedTermDepositLocks.map((lock) => (Object.assign(Object.assign({}, lock), { updates: rows.lockShares.filter((row) => row.userLendingPoolTrancheFixedTermDepositLock.id ===
|
|
488
|
+
lock.id) }))),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
/** `pool → epoch → clearing start`, from the clearing rows. */
|
|
492
|
+
export function clearingStarts(clearings) {
|
|
493
|
+
var _a;
|
|
494
|
+
const starts = new Map();
|
|
495
|
+
for (const clearing of clearings) {
|
|
496
|
+
const start = Number(clearing.startTimestamp);
|
|
497
|
+
// A clearing not yet started carries 0: no start to pair by.
|
|
498
|
+
if (!(start > 0))
|
|
499
|
+
continue;
|
|
500
|
+
const pool = clearing.lendingPool.id.toLowerCase();
|
|
501
|
+
const byEpoch = (_a = starts.get(pool)) !== null && _a !== void 0 ? _a : new Map();
|
|
502
|
+
byEpoch.set(Number(clearing.epochId), start);
|
|
503
|
+
starts.set(pool, byEpoch);
|
|
504
|
+
}
|
|
505
|
+
return starts;
|
|
506
|
+
}
|
|
289
507
|
function groupBy(rows, key) {
|
|
290
508
|
const map = new Map();
|
|
291
509
|
for (const row of rows) {
|
|
@@ -302,12 +520,13 @@ function groupBy(rows, key) {
|
|
|
302
520
|
* Pure: the fetched series → one entry per pool, tranche and settled epoch in
|
|
303
521
|
* which the lender held shares (or a lock earned a top-up). Each entry runs the
|
|
304
522
|
* same `epochTrancheYield` as the portfolio's last-epoch figure, with the same
|
|
305
|
-
* share selection, so the
|
|
523
|
+
* share selection (`sharesAtInterest`, `trancheSharesAtInterest`), so the
|
|
524
|
+
* newest entry equals that figure.
|
|
306
525
|
*
|
|
307
526
|
* Sorted by `epochId`, then `poolId`, then `trancheId`.
|
|
308
527
|
*/
|
|
309
528
|
export function buildYieldHistory(read, window, clock) {
|
|
310
|
-
var _a, _b, _c, _d
|
|
529
|
+
var _a, _b, _c, _d;
|
|
311
530
|
const user = read.positions.user;
|
|
312
531
|
if (!user)
|
|
313
532
|
return [];
|
|
@@ -315,45 +534,36 @@ export function buildYieldHistory(read, window, clock) {
|
|
|
315
534
|
const trancheSharesByTranche = groupBy(read.trancheShares, (row) => row.lendingPoolTranche.id);
|
|
316
535
|
const userSharesByPosition = groupBy(read.userShares, (row) => row.lendingPoolTrancheUserDetails.id);
|
|
317
536
|
const lockSharesByLock = groupBy(read.lockShares, (row) => row.userLendingPoolTrancheFixedTermDepositLock.id);
|
|
537
|
+
const withdrawalsByTranche = groupBy(read.withdrawals, (row) => row.tranche.id);
|
|
538
|
+
const starts = clearingStarts(read.clearings);
|
|
318
539
|
const entries = [];
|
|
319
540
|
for (const pool of user.lendingPoolUserDetails) {
|
|
541
|
+
const poolStarts = starts.get(pool.lendingPool.id.toLowerCase());
|
|
320
542
|
for (const position of pool.lendingPoolTrancheUserDetails) {
|
|
321
543
|
const trancheId = position.tranche.id;
|
|
322
544
|
const trancheUpdates = (_a = trancheSharesByTranche.get(trancheId)) !== null && _a !== void 0 ? _a : [];
|
|
323
545
|
const userUpdates = (_b = userSharesByPosition.get(position.id)) !== null && _b !== void 0 ? _b : [];
|
|
546
|
+
const trancheWithdrawals = (_c = withdrawalsByTranche.get(trancheId)) !== null && _c !== void 0 ? _c : [];
|
|
324
547
|
const trancheBalanceNow = parseFloat(position.tranche.balance);
|
|
325
548
|
const trancheSharesNow = parseFloat(position.tranche.shares);
|
|
326
|
-
for (const record of (
|
|
549
|
+
for (const record of (_d = interestByTranche.get(trancheId)) !== null && _d !== void 0 ? _d : []) {
|
|
327
550
|
const epoch = Number(record.epochId);
|
|
328
551
|
if (epoch < window.fromEpoch || epoch > window.toEpoch) {
|
|
329
552
|
continue;
|
|
330
553
|
}
|
|
331
|
-
const
|
|
332
|
-
const
|
|
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'),
|
|
554
|
+
const clearingStart = poolStarts === null || poolStarts === void 0 ? void 0 : poolStarts.get(epoch);
|
|
555
|
+
const { yield: epochYield, userShares } = positionEpochYield({
|
|
353
556
|
trancheBalanceNow,
|
|
354
557
|
trancheSharesNow,
|
|
355
|
-
|
|
356
|
-
|
|
558
|
+
userUpdates,
|
|
559
|
+
trancheUpdates,
|
|
560
|
+
exits: trancheWithdrawals,
|
|
561
|
+
locks: position.userLendingPoolTrancheFixedTermDepositLocks.map((lock) => {
|
|
562
|
+
var _a;
|
|
563
|
+
return (Object.assign(Object.assign({}, lock), { updates: (_a = lockSharesByLock.get(lock.id)) !== null && _a !== void 0 ? _a : [] }));
|
|
564
|
+
}),
|
|
565
|
+
}, epoch, parseFloat(record.epochInterestAmount), clearingStart);
|
|
566
|
+
const total = epochYield.total;
|
|
357
567
|
// Nothing held and nothing topped up: not part of this
|
|
358
568
|
// lender's history (a new lender's history is empty).
|
|
359
569
|
if (userShares <= 0 && total === 0)
|