@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
package/dist/bundle.cjs.js
CHANGED
|
@@ -16943,6 +16943,101 @@ class KSULocking {
|
|
|
16943
16943
|
}
|
|
16944
16944
|
}
|
|
16945
16945
|
|
|
16946
|
+
/**
|
|
16947
|
+
* Settled interest per cycle (epoch): what a lender was credited at each close,
|
|
16948
|
+
* per pool and tranche, and the one formula that turns a tranche's credited
|
|
16949
|
+
* interest into one lender's share of it.
|
|
16950
|
+
*
|
|
16951
|
+
* **The figures are NET, and only net.** `LendingPool._applyTrancheInterest`
|
|
16952
|
+
* takes the performance fee off first and emits `InterestApplied` with the
|
|
16953
|
+
* lender side only (`interest × (1 − performanceFee)`); a fixed-term top-up
|
|
16954
|
+
* (`_applyFixedRateInterests`) is net of the fee the same way. So the figure
|
|
16955
|
+
* this module computes from those records IS what the lender was credited —
|
|
16956
|
+
* never take the fee off it again. No gross is offered: the subgraph keeps
|
|
16957
|
+
* only TODAY's fee, so a historical gross would be a guess.
|
|
16958
|
+
*
|
|
16959
|
+
* Units: amounts are display units of the DEPLOYMENT'S OWN stable token (USDC
|
|
16960
|
+
* on Base and XDC USDC, AUDD on XDC AUDD) — never converted to USD here;
|
|
16961
|
+
* shares are display units of tranche shares, as the subgraph's BigDecimals.
|
|
16962
|
+
*/
|
|
16963
|
+
/**
|
|
16964
|
+
* The ONE per-epoch formula. `Portfolio.getPortfolioLendingData` runs it for
|
|
16965
|
+
* the last settled epoch and `getYieldHistory` for every epoch of a window, so
|
|
16966
|
+
* the two figures cannot drift apart.
|
|
16967
|
+
*
|
|
16968
|
+
* ```
|
|
16969
|
+
* base = epochInterest × userShares / trancheShares
|
|
16970
|
+
* lockBase = base × lock.sharesBeforeEpoch / userShares (active locks)
|
|
16971
|
+
* topUp = (lock.sharesAtEpoch − lock.sharesBeforeEpoch) × trancheBalanceNow / trancheSharesNow
|
|
16972
|
+
* variable = base − Σ lockBase
|
|
16973
|
+
* total = variable + Σ (lockBase + topUp)
|
|
16974
|
+
* ```
|
|
16975
|
+
*
|
|
16976
|
+
* A zero denominator contributes 0 (a tranche or a lender with no shares
|
|
16977
|
+
* earned nothing at that close), never `NaN`.
|
|
16978
|
+
*/
|
|
16979
|
+
function epochTrancheYield(input) {
|
|
16980
|
+
const base = input.trancheShares === 0
|
|
16981
|
+
? 0
|
|
16982
|
+
: (input.epochInterest * input.userShares) / input.trancheShares;
|
|
16983
|
+
let variable = base;
|
|
16984
|
+
const locks = input.locks.map((lock) => {
|
|
16985
|
+
if (!lock.active)
|
|
16986
|
+
return 0;
|
|
16987
|
+
const topUp = input.trancheSharesNow === 0
|
|
16988
|
+
? 0
|
|
16989
|
+
: ((lock.sharesAtEpoch - lock.sharesBeforeEpoch) *
|
|
16990
|
+
input.trancheBalanceNow) /
|
|
16991
|
+
input.trancheSharesNow;
|
|
16992
|
+
const lockBase = input.userShares === 0
|
|
16993
|
+
? 0
|
|
16994
|
+
: (base * lock.sharesBeforeEpoch) / input.userShares;
|
|
16995
|
+
variable -= lockBase;
|
|
16996
|
+
return lockBase + topUp;
|
|
16997
|
+
});
|
|
16998
|
+
const total = locks.reduce((sum, lockYield) => sum + lockYield, variable);
|
|
16999
|
+
return { total, variable, locks };
|
|
17000
|
+
}
|
|
17001
|
+
/**
|
|
17002
|
+
* Pool/tranche entries → one point per epoch, ascending by `epochId`. With no
|
|
17003
|
+
* filter it sums every strategy (the Lend tab's bars); with `poolId` it is one
|
|
17004
|
+
* strategy (a strategy page's bars).
|
|
17005
|
+
*
|
|
17006
|
+
* An epoch appears only if a kept entry has it — there is no zero padding, so
|
|
17007
|
+
* an empty input (a new lender) is an empty output.
|
|
17008
|
+
*
|
|
17009
|
+
* For a fixed-term lock holder the bars can sum to slightly MORE than the
|
|
17010
|
+
* lender's lifetime interest (up to about 0.2 %): each top-up is valued at the
|
|
17011
|
+
* tranche's CURRENT share price, which has grown since. Show "earned to date"
|
|
17012
|
+
* from the lifetime figure, never from this sum.
|
|
17013
|
+
*/
|
|
17014
|
+
function aggregateYieldHistory(entries, filter = {}) {
|
|
17015
|
+
var _a, _b;
|
|
17016
|
+
const poolId = (_a = filter.poolId) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
17017
|
+
const trancheId = (_b = filter.trancheId) === null || _b === void 0 ? void 0 : _b.toLowerCase();
|
|
17018
|
+
const byEpoch = new Map();
|
|
17019
|
+
for (const entry of entries) {
|
|
17020
|
+
if (poolId !== undefined && entry.poolId.toLowerCase() !== poolId) {
|
|
17021
|
+
continue;
|
|
17022
|
+
}
|
|
17023
|
+
if (trancheId !== undefined &&
|
|
17024
|
+
entry.trancheId.toLowerCase() !== trancheId) {
|
|
17025
|
+
continue;
|
|
17026
|
+
}
|
|
17027
|
+
const point = byEpoch.get(entry.epochId);
|
|
17028
|
+
if (point) {
|
|
17029
|
+
point.netYield += entry.netYield;
|
|
17030
|
+
continue;
|
|
17031
|
+
}
|
|
17032
|
+
byEpoch.set(entry.epochId, {
|
|
17033
|
+
epochId: entry.epochId,
|
|
17034
|
+
epochEnd: entry.epochEnd,
|
|
17035
|
+
netYield: entry.netYield,
|
|
17036
|
+
});
|
|
17037
|
+
}
|
|
17038
|
+
return [...byEpoch.values()].sort((a, b) => a.epochId - b.epochId);
|
|
17039
|
+
}
|
|
17040
|
+
|
|
16946
17041
|
/**
|
|
16947
17042
|
* Check if the SDK is configured for a Lite deployment.
|
|
16948
17043
|
* Lite deployments don't have KSU token, locking, or loyalty features.
|
|
@@ -17726,9 +17821,11 @@ const lendingPortfolioQuery = gql `
|
|
|
17726
17821
|
$epochId: BigInt!
|
|
17727
17822
|
$lastEpochId: BigInt!
|
|
17728
17823
|
$unusedPools: [String!]!
|
|
17824
|
+
$first: Int!
|
|
17729
17825
|
) {
|
|
17730
17826
|
user(id: $userAddress) {
|
|
17731
17827
|
lendingPoolUserDetails(
|
|
17828
|
+
first: $first
|
|
17732
17829
|
where: { lendingPool_not_in: $unusedPools }
|
|
17733
17830
|
) {
|
|
17734
17831
|
id
|
|
@@ -17736,11 +17833,11 @@ const lendingPortfolioQuery = gql `
|
|
|
17736
17833
|
id
|
|
17737
17834
|
name
|
|
17738
17835
|
balance
|
|
17739
|
-
tranches {
|
|
17836
|
+
tranches(first: $first) {
|
|
17740
17837
|
shares
|
|
17741
17838
|
}
|
|
17742
17839
|
}
|
|
17743
|
-
lendingPoolTrancheUserDetails {
|
|
17840
|
+
lendingPoolTrancheUserDetails(first: $first) {
|
|
17744
17841
|
shares
|
|
17745
17842
|
lendingPoolTrancheUserEpochSharesUpdates(
|
|
17746
17843
|
orderDirection: desc
|
|
@@ -17750,7 +17847,7 @@ const lendingPortfolioQuery = gql `
|
|
|
17750
17847
|
) {
|
|
17751
17848
|
shares
|
|
17752
17849
|
}
|
|
17753
|
-
userLendingPoolTrancheFixedTermDepositLocks {
|
|
17850
|
+
userLendingPoolTrancheFixedTermDepositLocks(first: $first) {
|
|
17754
17851
|
isLocked
|
|
17755
17852
|
unlockAmount
|
|
17756
17853
|
initialAmount
|
|
@@ -17774,6 +17871,14 @@ const lendingPortfolioQuery = gql `
|
|
|
17774
17871
|
) {
|
|
17775
17872
|
shares
|
|
17776
17873
|
}
|
|
17874
|
+
lockSharesAtLastEpoch: userLendingPoolTrancheFixedTermDepositLockShareUpdate(
|
|
17875
|
+
orderBy: shareUpdatesIndex
|
|
17876
|
+
orderDirection: desc
|
|
17877
|
+
first: 1
|
|
17878
|
+
where: { epochId_lt: $epochId }
|
|
17879
|
+
) {
|
|
17880
|
+
shares
|
|
17881
|
+
}
|
|
17777
17882
|
}
|
|
17778
17883
|
tranche {
|
|
17779
17884
|
id
|
|
@@ -17800,6 +17905,7 @@ const lendingPortfolioQuery = gql `
|
|
|
17800
17905
|
}
|
|
17801
17906
|
}
|
|
17802
17907
|
userRequests(
|
|
17908
|
+
first: $first
|
|
17803
17909
|
where: {
|
|
17804
17910
|
user: $userAddress
|
|
17805
17911
|
userRequestEvents_: {
|
|
@@ -17815,6 +17921,7 @@ const lendingPortfolioQuery = gql `
|
|
|
17815
17921
|
}
|
|
17816
17922
|
fixedTermConfigId
|
|
17817
17923
|
userRequestEvents(
|
|
17924
|
+
first: $first
|
|
17818
17925
|
where: { type_in: [DepositInitiated, DepositIncreased] }
|
|
17819
17926
|
) {
|
|
17820
17927
|
id
|
|
@@ -17824,6 +17931,400 @@ const lendingPortfolioQuery = gql `
|
|
|
17824
17931
|
}
|
|
17825
17932
|
`;
|
|
17826
17933
|
|
|
17934
|
+
/** The subgraph returns at most this many rows per collection per request. */
|
|
17935
|
+
const YIELD_HISTORY_PAGE_SIZE = 1000;
|
|
17936
|
+
/**
|
|
17937
|
+
* A runaway guard, not a working limit: a collection this many pages long
|
|
17938
|
+
* (`MAX × PAGE_SIZE` rows for one lender) is a broken response, not a history.
|
|
17939
|
+
*/
|
|
17940
|
+
const YIELD_HISTORY_MAX_PAGES = 100;
|
|
17941
|
+
/**
|
|
17942
|
+
* Throws `RangeError` on a malformed `fromEpoch` / `count`. `getYieldHistory`
|
|
17943
|
+
* calls it BEFORE any network read.
|
|
17944
|
+
*/
|
|
17945
|
+
function validateYieldHistoryOptions(options) {
|
|
17946
|
+
const { fromEpoch, count } = options;
|
|
17947
|
+
if (fromEpoch !== undefined &&
|
|
17948
|
+
(!Number.isSafeInteger(fromEpoch) || fromEpoch < 0)) {
|
|
17949
|
+
throw new RangeError(`getYieldHistory: fromEpoch must be a non-negative integer, got ${String(fromEpoch)}`);
|
|
17950
|
+
}
|
|
17951
|
+
if (count !== undefined && (!Number.isSafeInteger(count) || count < 1)) {
|
|
17952
|
+
throw new RangeError(`getYieldHistory: count must be a positive integer, got ${String(count)}`);
|
|
17953
|
+
}
|
|
17954
|
+
}
|
|
17955
|
+
function resolveYieldHistoryWindow(options, currentEpoch) {
|
|
17956
|
+
validateYieldHistoryOptions(options);
|
|
17957
|
+
const { fromEpoch, count } = options;
|
|
17958
|
+
const newest = currentEpoch - 1;
|
|
17959
|
+
if (newest < 0)
|
|
17960
|
+
return null;
|
|
17961
|
+
let from;
|
|
17962
|
+
let to = newest;
|
|
17963
|
+
if (fromEpoch !== undefined) {
|
|
17964
|
+
from = fromEpoch;
|
|
17965
|
+
if (count !== undefined)
|
|
17966
|
+
to = Math.min(newest, fromEpoch + count - 1);
|
|
17967
|
+
}
|
|
17968
|
+
else {
|
|
17969
|
+
from = count !== undefined ? Math.max(0, newest - count + 1) : 0;
|
|
17970
|
+
}
|
|
17971
|
+
return from > to ? null : { fromEpoch: from, toEpoch: to };
|
|
17972
|
+
}
|
|
17973
|
+
// ---------------------------------------------------------------------------
|
|
17974
|
+
// Subgraph reads
|
|
17975
|
+
// ---------------------------------------------------------------------------
|
|
17976
|
+
/**
|
|
17977
|
+
* Round trip 1: the lender's tranche positions and fixed-term locks. Same
|
|
17978
|
+
* pool exclusion and the same nested-list size (`$first`) as
|
|
17979
|
+
* `lendingPortfolioQuery`, so both read the same lock set.
|
|
17980
|
+
*/
|
|
17981
|
+
const yieldHistoryPositionsQuery = gql `
|
|
17982
|
+
query YieldHistoryPositions(
|
|
17983
|
+
$userAddress: ID!
|
|
17984
|
+
$unusedPools: [String!]!
|
|
17985
|
+
$first: Int!
|
|
17986
|
+
) {
|
|
17987
|
+
user(id: $userAddress) {
|
|
17988
|
+
lendingPoolUserDetails(
|
|
17989
|
+
first: $first
|
|
17990
|
+
where: { lendingPool_not_in: $unusedPools }
|
|
17991
|
+
) {
|
|
17992
|
+
lendingPool {
|
|
17993
|
+
id
|
|
17994
|
+
}
|
|
17995
|
+
lendingPoolTrancheUserDetails(first: $first) {
|
|
17996
|
+
id
|
|
17997
|
+
tranche {
|
|
17998
|
+
id
|
|
17999
|
+
balance
|
|
18000
|
+
shares
|
|
18001
|
+
}
|
|
18002
|
+
userLendingPoolTrancheFixedTermDepositLocks(first: $first) {
|
|
18003
|
+
id
|
|
18004
|
+
epochLockStart
|
|
18005
|
+
epochLockEnd
|
|
18006
|
+
initialTrancheShares
|
|
18007
|
+
}
|
|
18008
|
+
}
|
|
18009
|
+
}
|
|
18010
|
+
}
|
|
18011
|
+
}
|
|
18012
|
+
`;
|
|
18013
|
+
/**
|
|
18014
|
+
* Round trip 2 onwards: the four per-epoch series, one page of each per
|
|
18015
|
+
* request, cursor-paginated by `id`. A series that is exhausted is skipped with
|
|
18016
|
+
* `@include`, so a history costs as many requests as its LONGEST series has
|
|
18017
|
+
* pages — normally one — never one per epoch.
|
|
18018
|
+
*
|
|
18019
|
+
* Share snapshots are read up to the window's end with no lower bound: the
|
|
18020
|
+
* value AS OF the first epoch may come from an update long before it.
|
|
18021
|
+
*/
|
|
18022
|
+
const yieldHistoryPageQuery = gql `
|
|
18023
|
+
query YieldHistoryPage(
|
|
18024
|
+
$tranches: [String!]!
|
|
18025
|
+
$userTranches: [String!]!
|
|
18026
|
+
$locks: [String!]!
|
|
18027
|
+
$fromEpoch: BigInt!
|
|
18028
|
+
$toEpoch: BigInt!
|
|
18029
|
+
$interestCursor: ID!
|
|
18030
|
+
$trancheSharesCursor: ID!
|
|
18031
|
+
$userSharesCursor: ID!
|
|
18032
|
+
$lockSharesCursor: ID!
|
|
18033
|
+
$withInterest: Boolean!
|
|
18034
|
+
$withTrancheShares: Boolean!
|
|
18035
|
+
$withUserShares: Boolean!
|
|
18036
|
+
$withLockShares: Boolean!
|
|
18037
|
+
$first: Int!
|
|
18038
|
+
) {
|
|
18039
|
+
interest: lendingPoolTrancheEpochInterests(
|
|
18040
|
+
first: $first
|
|
18041
|
+
orderBy: id
|
|
18042
|
+
orderDirection: asc
|
|
18043
|
+
where: {
|
|
18044
|
+
id_gt: $interestCursor
|
|
18045
|
+
lendingPoolTranche_in: $tranches
|
|
18046
|
+
epochId_gte: $fromEpoch
|
|
18047
|
+
epochId_lte: $toEpoch
|
|
18048
|
+
}
|
|
18049
|
+
) @include(if: $withInterest) {
|
|
18050
|
+
id
|
|
18051
|
+
epochId
|
|
18052
|
+
epochInterestAmount
|
|
18053
|
+
lendingPoolTranche {
|
|
18054
|
+
id
|
|
18055
|
+
}
|
|
18056
|
+
}
|
|
18057
|
+
trancheShares: lendingPoolTrancheShareUpdates(
|
|
18058
|
+
first: $first
|
|
18059
|
+
orderBy: id
|
|
18060
|
+
orderDirection: asc
|
|
18061
|
+
where: {
|
|
18062
|
+
id_gt: $trancheSharesCursor
|
|
18063
|
+
lendingPoolTranche_in: $tranches
|
|
18064
|
+
epochId_lte: $toEpoch
|
|
18065
|
+
}
|
|
18066
|
+
) @include(if: $withTrancheShares) {
|
|
18067
|
+
id
|
|
18068
|
+
epochId
|
|
18069
|
+
shareUpdatesIndex
|
|
18070
|
+
shares
|
|
18071
|
+
lendingPoolTranche {
|
|
18072
|
+
id
|
|
18073
|
+
}
|
|
18074
|
+
}
|
|
18075
|
+
userShares: lendingPoolTrancheUserEpochSharesUpdates(
|
|
18076
|
+
first: $first
|
|
18077
|
+
orderBy: id
|
|
18078
|
+
orderDirection: asc
|
|
18079
|
+
where: {
|
|
18080
|
+
id_gt: $userSharesCursor
|
|
18081
|
+
lendingPoolTrancheUserDetails_in: $userTranches
|
|
18082
|
+
epochId_lte: $toEpoch
|
|
18083
|
+
}
|
|
18084
|
+
) @include(if: $withUserShares) {
|
|
18085
|
+
id
|
|
18086
|
+
epochId
|
|
18087
|
+
shareUpdatesIndex
|
|
18088
|
+
shares
|
|
18089
|
+
lendingPoolTrancheUserDetails {
|
|
18090
|
+
id
|
|
18091
|
+
}
|
|
18092
|
+
}
|
|
18093
|
+
lockShares: userLendingPoolTrancheFtdLockShareUpdates(
|
|
18094
|
+
first: $first
|
|
18095
|
+
orderBy: id
|
|
18096
|
+
orderDirection: asc
|
|
18097
|
+
where: {
|
|
18098
|
+
id_gt: $lockSharesCursor
|
|
18099
|
+
userLendingPoolTrancheFixedTermDepositLock_in: $locks
|
|
18100
|
+
epochId_lte: $toEpoch
|
|
18101
|
+
}
|
|
18102
|
+
) @include(if: $withLockShares) {
|
|
18103
|
+
id
|
|
18104
|
+
epochId
|
|
18105
|
+
shareUpdatesIndex
|
|
18106
|
+
shares
|
|
18107
|
+
userLendingPoolTrancheFixedTermDepositLock {
|
|
18108
|
+
id
|
|
18109
|
+
}
|
|
18110
|
+
}
|
|
18111
|
+
}
|
|
18112
|
+
`;
|
|
18113
|
+
/**
|
|
18114
|
+
* Fetch the four series for the window, paginating each by `id` until a short
|
|
18115
|
+
* page. Returns the rows and how many requests it took.
|
|
18116
|
+
*/
|
|
18117
|
+
function fetchYieldHistorySeries(request, ids, window) {
|
|
18118
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18119
|
+
var _a;
|
|
18120
|
+
const rows = {
|
|
18121
|
+
interest: [],
|
|
18122
|
+
trancheShares: [],
|
|
18123
|
+
userShares: [],
|
|
18124
|
+
lockShares: [],
|
|
18125
|
+
};
|
|
18126
|
+
const series = [
|
|
18127
|
+
{ key: 'interest', flag: 'withInterest', cursor: 'interestCursor' },
|
|
18128
|
+
{
|
|
18129
|
+
key: 'trancheShares',
|
|
18130
|
+
flag: 'withTrancheShares',
|
|
18131
|
+
cursor: 'trancheSharesCursor',
|
|
18132
|
+
},
|
|
18133
|
+
{
|
|
18134
|
+
key: 'userShares',
|
|
18135
|
+
flag: 'withUserShares',
|
|
18136
|
+
cursor: 'userSharesCursor',
|
|
18137
|
+
},
|
|
18138
|
+
{
|
|
18139
|
+
key: 'lockShares',
|
|
18140
|
+
flag: 'withLockShares',
|
|
18141
|
+
cursor: 'lockSharesCursor',
|
|
18142
|
+
},
|
|
18143
|
+
];
|
|
18144
|
+
const cursors = {
|
|
18145
|
+
interest: '',
|
|
18146
|
+
trancheShares: '',
|
|
18147
|
+
userShares: '',
|
|
18148
|
+
lockShares: '',
|
|
18149
|
+
};
|
|
18150
|
+
const open = {
|
|
18151
|
+
interest: ids.tranches.length > 0,
|
|
18152
|
+
trancheShares: ids.tranches.length > 0,
|
|
18153
|
+
userShares: ids.userTranches.length > 0,
|
|
18154
|
+
lockShares: ids.locks.length > 0,
|
|
18155
|
+
};
|
|
18156
|
+
let requests = 0;
|
|
18157
|
+
while (series.some(({ key }) => open[key])) {
|
|
18158
|
+
if (requests >= YIELD_HISTORY_MAX_PAGES) {
|
|
18159
|
+
throw new Error(`getYieldHistory: more than ${String(YIELD_HISTORY_MAX_PAGES)} pages for one lender`);
|
|
18160
|
+
}
|
|
18161
|
+
const variables = {
|
|
18162
|
+
tranches: ids.tranches,
|
|
18163
|
+
userTranches: ids.userTranches,
|
|
18164
|
+
locks: ids.locks,
|
|
18165
|
+
fromEpoch: String(window.fromEpoch),
|
|
18166
|
+
toEpoch: String(window.toEpoch),
|
|
18167
|
+
first: YIELD_HISTORY_PAGE_SIZE,
|
|
18168
|
+
};
|
|
18169
|
+
for (const { key, flag, cursor } of series) {
|
|
18170
|
+
variables[flag] = open[key];
|
|
18171
|
+
variables[cursor] = cursors[key];
|
|
18172
|
+
}
|
|
18173
|
+
const page = yield request(variables);
|
|
18174
|
+
requests += 1;
|
|
18175
|
+
for (const { key } of series) {
|
|
18176
|
+
if (!open[key])
|
|
18177
|
+
continue;
|
|
18178
|
+
const got = (_a = page[key]) !== null && _a !== void 0 ? _a : [];
|
|
18179
|
+
rows[key].push(...got);
|
|
18180
|
+
const last = got.at(-1);
|
|
18181
|
+
if (got.length < YIELD_HISTORY_PAGE_SIZE || last === undefined) {
|
|
18182
|
+
open[key] = false;
|
|
18183
|
+
continue;
|
|
18184
|
+
}
|
|
18185
|
+
if (last.id <= cursors[key]) {
|
|
18186
|
+
throw new Error(`getYieldHistory: the ${key} cursor did not advance`);
|
|
18187
|
+
}
|
|
18188
|
+
cursors[key] = last.id;
|
|
18189
|
+
}
|
|
18190
|
+
}
|
|
18191
|
+
return { rows, requests };
|
|
18192
|
+
});
|
|
18193
|
+
}
|
|
18194
|
+
/**
|
|
18195
|
+
* The snapshot in force at `epoch`: among updates with `epochId ≤ epoch`, the
|
|
18196
|
+
* one with the highest `shareUpdatesIndex` — exactly what
|
|
18197
|
+
* `lendingPortfolioQuery` selects with `orderBy: shareUpdatesIndex, desc,
|
|
18198
|
+
* first: 1, where: { epochId_lt: epoch + 1 }`. `undefined` when none.
|
|
18199
|
+
*/
|
|
18200
|
+
function sharesAsOf(updates, epoch) {
|
|
18201
|
+
let found;
|
|
18202
|
+
for (const update of updates) {
|
|
18203
|
+
if (Number(update.epochId) > epoch)
|
|
18204
|
+
continue;
|
|
18205
|
+
if (!found || update.shareUpdatesIndex > found.shareUpdatesIndex) {
|
|
18206
|
+
found = update;
|
|
18207
|
+
}
|
|
18208
|
+
}
|
|
18209
|
+
return found;
|
|
18210
|
+
}
|
|
18211
|
+
function groupBy(rows, key) {
|
|
18212
|
+
const map = new Map();
|
|
18213
|
+
for (const row of rows) {
|
|
18214
|
+
const k = key(row);
|
|
18215
|
+
const list = map.get(k);
|
|
18216
|
+
if (list)
|
|
18217
|
+
list.push(row);
|
|
18218
|
+
else
|
|
18219
|
+
map.set(k, [row]);
|
|
18220
|
+
}
|
|
18221
|
+
return map;
|
|
18222
|
+
}
|
|
18223
|
+
/**
|
|
18224
|
+
* Pure: the fetched series → one entry per pool, tranche and settled epoch in
|
|
18225
|
+
* which the lender held shares (or a lock earned a top-up). Each entry runs the
|
|
18226
|
+
* same `epochTrancheYield` as the portfolio's last-epoch figure, with the same
|
|
18227
|
+
* share selection, so the newest entry equals that figure.
|
|
18228
|
+
*
|
|
18229
|
+
* Sorted by `epochId`, then `poolId`, then `trancheId`.
|
|
18230
|
+
*/
|
|
18231
|
+
function buildYieldHistory(read, window, clock) {
|
|
18232
|
+
var _a, _b, _c, _d, _e, _f;
|
|
18233
|
+
const user = read.positions.user;
|
|
18234
|
+
if (!user)
|
|
18235
|
+
return [];
|
|
18236
|
+
const interestByTranche = groupBy(read.interest, (row) => row.lendingPoolTranche.id);
|
|
18237
|
+
const trancheSharesByTranche = groupBy(read.trancheShares, (row) => row.lendingPoolTranche.id);
|
|
18238
|
+
const userSharesByPosition = groupBy(read.userShares, (row) => row.lendingPoolTrancheUserDetails.id);
|
|
18239
|
+
const lockSharesByLock = groupBy(read.lockShares, (row) => row.userLendingPoolTrancheFixedTermDepositLock.id);
|
|
18240
|
+
const entries = [];
|
|
18241
|
+
for (const pool of user.lendingPoolUserDetails) {
|
|
18242
|
+
for (const position of pool.lendingPoolTrancheUserDetails) {
|
|
18243
|
+
const trancheId = position.tranche.id;
|
|
18244
|
+
const trancheUpdates = (_a = trancheSharesByTranche.get(trancheId)) !== null && _a !== void 0 ? _a : [];
|
|
18245
|
+
const userUpdates = (_b = userSharesByPosition.get(position.id)) !== null && _b !== void 0 ? _b : [];
|
|
18246
|
+
const trancheBalanceNow = parseFloat(position.tranche.balance);
|
|
18247
|
+
const trancheSharesNow = parseFloat(position.tranche.shares);
|
|
18248
|
+
for (const record of (_c = interestByTranche.get(trancheId)) !== null && _c !== void 0 ? _c : []) {
|
|
18249
|
+
const epoch = Number(record.epochId);
|
|
18250
|
+
if (epoch < window.fromEpoch || epoch > window.toEpoch) {
|
|
18251
|
+
continue;
|
|
18252
|
+
}
|
|
18253
|
+
const userSnapshot = sharesAsOf(userUpdates, epoch);
|
|
18254
|
+
const userShares = parseFloat((_d = userSnapshot === null || userSnapshot === void 0 ? void 0 : userSnapshot.shares) !== null && _d !== void 0 ? _d : '0');
|
|
18255
|
+
const locks = position.userLendingPoolTrancheFixedTermDepositLocks.map((lock) => {
|
|
18256
|
+
var _a, _b, _c, _d, _e;
|
|
18257
|
+
const lockUpdates = (_a = lockSharesByLock.get(lock.id)) !== null && _a !== void 0 ? _a : [];
|
|
18258
|
+
return {
|
|
18259
|
+
active: parseFloat(lock.epochLockStart) < epoch &&
|
|
18260
|
+
parseFloat(lock.epochLockEnd) >= epoch &&
|
|
18261
|
+
userSnapshot !== undefined,
|
|
18262
|
+
// No snapshot yet = the lock's opening
|
|
18263
|
+
// shares: a lock made after clearing E
|
|
18264
|
+
// starts at E but its first update is
|
|
18265
|
+
// tagged E + 1, and reading that gap as 0
|
|
18266
|
+
// would book its principal as a top-up.
|
|
18267
|
+
sharesAtEpoch: parseFloat((_c = (_b = sharesAsOf(lockUpdates, epoch)) === null || _b === void 0 ? void 0 : _b.shares) !== null && _c !== void 0 ? _c : lock.initialTrancheShares),
|
|
18268
|
+
sharesBeforeEpoch: parseFloat((_e = (_d = sharesAsOf(lockUpdates, epoch - 1)) === null || _d === void 0 ? void 0 : _d.shares) !== null && _e !== void 0 ? _e : lock.initialTrancheShares),
|
|
18269
|
+
};
|
|
18270
|
+
});
|
|
18271
|
+
const { total } = epochTrancheYield({
|
|
18272
|
+
epochInterest: parseFloat(record.epochInterestAmount),
|
|
18273
|
+
userShares,
|
|
18274
|
+
trancheShares: parseFloat((_f = (_e = sharesAsOf(trancheUpdates, epoch)) === null || _e === void 0 ? void 0 : _e.shares) !== null && _f !== void 0 ? _f : '0'),
|
|
18275
|
+
trancheBalanceNow,
|
|
18276
|
+
trancheSharesNow,
|
|
18277
|
+
locks,
|
|
18278
|
+
});
|
|
18279
|
+
// Nothing held and nothing topped up: not part of this
|
|
18280
|
+
// lender's history (a new lender's history is empty).
|
|
18281
|
+
if (userShares <= 0 && total === 0)
|
|
18282
|
+
continue;
|
|
18283
|
+
entries.push({
|
|
18284
|
+
epochId: epoch,
|
|
18285
|
+
epochEnd: clock.epochZeroStart +
|
|
18286
|
+
(epoch + 1) * clock.epochDuration,
|
|
18287
|
+
poolId: pool.lendingPool.id,
|
|
18288
|
+
trancheId,
|
|
18289
|
+
netYield: total,
|
|
18290
|
+
});
|
|
18291
|
+
}
|
|
18292
|
+
}
|
|
18293
|
+
}
|
|
18294
|
+
return entries.sort((a, b) => a.epochId - b.epochId ||
|
|
18295
|
+
a.poolId.localeCompare(b.poolId) ||
|
|
18296
|
+
a.trancheId.localeCompare(b.trancheId));
|
|
18297
|
+
}
|
|
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
|
+
|
|
17827
18328
|
class Portfolio {
|
|
17828
18329
|
constructor(_kasuConfig, signerOrProvider) {
|
|
17829
18330
|
this._kasuConfig = _kasuConfig;
|
|
@@ -17924,7 +18425,7 @@ class Portfolio {
|
|
|
17924
18425
|
performanceFee *
|
|
17925
18426
|
ecosystemFeeRate;
|
|
17926
18427
|
}
|
|
17927
|
-
return (
|
|
18428
|
+
return (totalWeeklyEcosystemFees * userRKsuAmount) / totalRKsuAmount;
|
|
17928
18429
|
}
|
|
17929
18430
|
/**
|
|
17930
18431
|
* Calculate the user's weekly KASU bonus rewards (KASU) based on current deposits,
|
|
@@ -17932,7 +18433,7 @@ class Portfolio {
|
|
|
17932
18433
|
*/
|
|
17933
18434
|
calculateWeeklyKsuBonusRewards(params) {
|
|
17934
18435
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17935
|
-
const { userAddress, userRksuAmount, userDepositAmounts, ksuEpochPrice } = params;
|
|
18436
|
+
const { userAddress, userRksuAmount, userDepositAmounts, ksuEpochPrice, } = params;
|
|
17936
18437
|
const totalUserDeposits = userDepositAmounts[0].add(userDepositAmounts[1]);
|
|
17937
18438
|
if (totalUserDeposits.isZero())
|
|
17938
18439
|
return 0;
|
|
@@ -18041,7 +18542,7 @@ class Portfolio {
|
|
|
18041
18542
|
}
|
|
18042
18543
|
getPortfolioLendingData(userAddress, poolOverviews, currentEpoch, provider) {
|
|
18043
18544
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18044
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
18545
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
18045
18546
|
const SECONDS_PER_EPOCH = 604800; // seconds in a week
|
|
18046
18547
|
const portfolioLendingPoolsPromise = [];
|
|
18047
18548
|
const previousEpoch = ethers.BigNumber.from(currentEpoch).sub(ethers.BigNumber.from(1));
|
|
@@ -18051,6 +18552,7 @@ class Portfolio {
|
|
|
18051
18552
|
epochId: parseFloat(currentEpoch),
|
|
18052
18553
|
lastEpochId: previousEpoch.toNumber(),
|
|
18053
18554
|
unusedPools: this._kasuConfig.UNUSED_LENDING_POOL_IDS,
|
|
18555
|
+
first: YIELD_HISTORY_PAGE_SIZE,
|
|
18054
18556
|
}),
|
|
18055
18557
|
this._userLendingService.getPortfolioUserTrancheBalances(userAddress),
|
|
18056
18558
|
this._userLendingService.getUserPoolBalance(userAddress, poolOverviews.map(({ id }) => id), provider),
|
|
@@ -18088,7 +18590,6 @@ class Portfolio {
|
|
|
18088
18590
|
}
|
|
18089
18591
|
}
|
|
18090
18592
|
let totalInvestments = ethers.BigNumber.from(0);
|
|
18091
|
-
let totalYieldEarnedLastEpoch = 0;
|
|
18092
18593
|
for (const poolOverview of poolOverviews) {
|
|
18093
18594
|
const userPoolBalance = userPoolBalances.find(({ poolId }) => poolId === poolOverview.id);
|
|
18094
18595
|
if (!userPoolBalance) {
|
|
@@ -18096,6 +18597,7 @@ class Portfolio {
|
|
|
18096
18597
|
}
|
|
18097
18598
|
totalInvestments = userPoolBalance.balance.add(totalInvestments);
|
|
18098
18599
|
const tranches = [];
|
|
18600
|
+
let poolYieldEarnedLastEpoch = 0;
|
|
18099
18601
|
const lendingPoolUserDetails = lastEpochData.user.lendingPoolUserDetails.find(({ lendingPool }) => lendingPool.id === poolOverview.id);
|
|
18100
18602
|
if (!lendingPoolUserDetails)
|
|
18101
18603
|
continue;
|
|
@@ -18114,18 +18616,42 @@ class Portfolio {
|
|
|
18114
18616
|
}, depositDetails, fixedLoans: [] }));
|
|
18115
18617
|
continue;
|
|
18116
18618
|
}
|
|
18117
|
-
const
|
|
18118
|
-
|
|
18119
|
-
|
|
18120
|
-
|
|
18121
|
-
|
|
18122
|
-
|
|
18123
|
-
|
|
18124
|
-
const
|
|
18125
|
-
|
|
18126
|
-
|
|
18127
|
-
: (
|
|
18128
|
-
|
|
18619
|
+
const lastEpoch = previousEpoch.toNumber();
|
|
18620
|
+
const userHasLastEpochShares = Boolean((_c = lastEpochUserTrancheDetails
|
|
18621
|
+
.lendingPoolTrancheUserEpochSharesUpdates) === null || _c === void 0 ? void 0 : _c.length);
|
|
18622
|
+
const fixedTermDeposits = lastEpochUserTrancheDetails.userLendingPoolTrancheFixedTermDepositLocks;
|
|
18623
|
+
// The one per-epoch formula `getYieldHistory` runs too, with
|
|
18624
|
+
// the same share selection — so the last bar of a history
|
|
18625
|
+
// equals these figures.
|
|
18626
|
+
const lastEpochYield = epochTrancheYield({
|
|
18627
|
+
epochInterest: parseFloat((_f = (_e = (_d = lastEpochUserTrancheDetails.tranche
|
|
18628
|
+
.lendingPoolTrancheEpochInterest) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.epochInterestAmount) !== null && _f !== void 0 ? _f : '0'),
|
|
18629
|
+
userShares: parseFloat((_j = (_h = (_g = lastEpochUserTrancheDetails
|
|
18630
|
+
.lendingPoolTrancheUserEpochSharesUpdates) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.shares) !== null && _j !== void 0 ? _j : '0'),
|
|
18631
|
+
trancheShares: parseFloat((_m = (_l = (_k = lastEpochUserTrancheDetails.tranche
|
|
18632
|
+
.lendingPoolTrancheShareUpdates) === null || _k === void 0 ? void 0 : _k[0]) === null || _l === void 0 ? void 0 : _l.shares) !== null && _m !== void 0 ? _m : '0'),
|
|
18633
|
+
trancheBalanceNow: parseFloat(lastEpochUserTrancheDetails.tranche.balance),
|
|
18634
|
+
trancheSharesNow: parseFloat(lastEpochUserTrancheDetails.tranche.shares),
|
|
18635
|
+
locks: fixedTermDeposits.map((fixedTermDeposit) => {
|
|
18636
|
+
var _a, _b, _c, _d, _e, _f;
|
|
18637
|
+
return ({
|
|
18638
|
+
active: parseFloat(fixedTermDeposit.epochLockStart) <
|
|
18639
|
+
lastEpoch &&
|
|
18640
|
+
parseFloat(fixedTermDeposit.epochLockEnd) >=
|
|
18641
|
+
lastEpoch &&
|
|
18642
|
+
userHasLastEpochShares,
|
|
18643
|
+
// No snapshot = the lock's opening shares, exactly
|
|
18644
|
+
// as `getYieldHistory` reads it: a lock made after
|
|
18645
|
+
// clearing E starts at E with its first update
|
|
18646
|
+
// tagged E + 1. Never the CURRENT shares, which
|
|
18647
|
+
// would carry a top-up from a clearing that ran
|
|
18648
|
+
// after the last settled epoch.
|
|
18649
|
+
sharesAtEpoch: parseFloat((_c = (_b = (_a = fixedTermDeposit.lockSharesAtLastEpoch) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.shares) !== null && _c !== void 0 ? _c : fixedTermDeposit.initialTrancheShares),
|
|
18650
|
+
sharesBeforeEpoch: parseFloat((_f = (_e = (_d = fixedTermDeposit
|
|
18651
|
+
.userLendingPoolTrancheFixedTermDepositLockShareUpdate) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.shares) !== null && _f !== void 0 ? _f : fixedTermDeposit.initialTrancheShares),
|
|
18652
|
+
});
|
|
18653
|
+
}),
|
|
18654
|
+
});
|
|
18129
18655
|
const userTrancheBalance = {
|
|
18130
18656
|
balance: '0',
|
|
18131
18657
|
yieldEarned: 0,
|
|
@@ -18140,47 +18666,20 @@ class Portfolio {
|
|
|
18140
18666
|
}
|
|
18141
18667
|
}
|
|
18142
18668
|
let totalLifetimeFtdYield = 0;
|
|
18143
|
-
|
|
18144
|
-
|
|
18145
|
-
var _a, _b, _c, _d, _e;
|
|
18669
|
+
const fixedLoans = fixedTermDeposits.map((fixedTermDeposit, lockIndex) => {
|
|
18670
|
+
var _a, _b;
|
|
18146
18671
|
const fixedLoanDepositDetails = (_a = userRequest === null || userRequest === void 0 ? void 0 : userRequest[fixedTermDeposit
|
|
18147
|
-
.lendingPoolTrancheFixedTermConfig
|
|
18148
|
-
|
|
18149
|
-
|
|
18150
|
-
let lifetimeYieldEarned = 0;
|
|
18151
|
-
if (parseFloat(fixedTermDeposit.epochLockStart) <
|
|
18152
|
-
previousEpoch.toNumber() &&
|
|
18153
|
-
parseFloat(fixedTermDeposit.epochLockEnd) >=
|
|
18154
|
-
previousEpoch.toNumber() &&
|
|
18155
|
-
Boolean((_b = lastEpochUserTrancheDetails
|
|
18156
|
-
.lendingPoolTrancheUserEpochSharesUpdates) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
18157
|
-
const currentEpochFtdShares = parseFloat(fixedTermDeposit.trancheShares);
|
|
18158
|
-
const lastEpochFtdShares = parseFloat((_e = (_d = (_c = fixedTermDeposit
|
|
18159
|
-
.userLendingPoolTrancheFixedTermDepositLockShareUpdate) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.shares) !== null && _e !== void 0 ? _e : '0');
|
|
18160
|
-
const additionalFtdYield = ((currentEpochFtdShares -
|
|
18161
|
-
lastEpochFtdShares) *
|
|
18162
|
-
currentTotalAmount) /
|
|
18163
|
-
currentTotalShares;
|
|
18164
|
-
const lastEpochFtdBaseYield = (lastEpochTotalBaseYield *
|
|
18165
|
-
lastEpochFtdShares) /
|
|
18166
|
-
lastEpochUserShares;
|
|
18167
|
-
lastEpochVariableBaseYield -=
|
|
18168
|
-
lastEpochFtdBaseYield;
|
|
18169
|
-
lastEpochYieldEarned =
|
|
18170
|
-
lastEpochFtdBaseYield + additionalFtdYield;
|
|
18171
|
-
}
|
|
18172
|
-
lifetimeYieldEarned =
|
|
18173
|
-
this.calculateFixedTermDepositEarnings(fixedTermDeposit, lastEpochUserTrancheDetails.tranche.balance, lastEpochUserTrancheDetails.tranche.shares);
|
|
18672
|
+
.lendingPoolTrancheFixedTermConfig.configId]) !== null && _a !== void 0 ? _a : [];
|
|
18673
|
+
const lastEpochYieldEarned = (_b = lastEpochYield.locks[lockIndex]) !== null && _b !== void 0 ? _b : 0;
|
|
18674
|
+
const lifetimeYieldEarned = this.calculateFixedTermDepositEarnings(fixedTermDeposit, lastEpochUserTrancheDetails.tranche.balance, lastEpochUserTrancheDetails.tranche.shares);
|
|
18174
18675
|
totalLifetimeFtdYield += lifetimeYieldEarned;
|
|
18175
|
-
totalYieldEarnedLastEpoch += lastEpochYieldEarned;
|
|
18176
18676
|
const lockDuration = parseFloat(fixedTermDeposit
|
|
18177
18677
|
.lendingPoolTrancheFixedTermConfig
|
|
18178
18678
|
.epochLockDuration) * SECONDS_PER_EPOCH;
|
|
18179
18679
|
return {
|
|
18180
18680
|
lockId: fixedTermDeposit.lockId,
|
|
18181
18681
|
configId: fixedTermDeposit
|
|
18182
|
-
.lendingPoolTrancheFixedTermConfig
|
|
18183
|
-
.configId,
|
|
18682
|
+
.lendingPoolTrancheFixedTermConfig.configId,
|
|
18184
18683
|
epochLockDuration: fixedTermDeposit
|
|
18185
18684
|
.lendingPoolTrancheFixedTermConfig
|
|
18186
18685
|
.epochLockDuration,
|
|
@@ -18190,9 +18689,7 @@ class Portfolio {
|
|
|
18190
18689
|
.epochInterestRate,
|
|
18191
18690
|
epochLockStart: fixedTermDeposit.epochLockStart,
|
|
18192
18691
|
amount: this._userLendingService
|
|
18193
|
-
.convertSharesToAssets(fixedTermDeposit.trancheShares, lastEpochUserTrancheDetails.tranche
|
|
18194
|
-
.balance, lastEpochUserTrancheDetails.tranche
|
|
18195
|
-
.shares)
|
|
18692
|
+
.convertSharesToAssets(fixedTermDeposit.trancheShares, lastEpochUserTrancheDetails.tranche.balance, lastEpochUserTrancheDetails.tranche.shares)
|
|
18196
18693
|
.toString(),
|
|
18197
18694
|
endTime: parseInt(fixedTermDeposit.createdOn) +
|
|
18198
18695
|
lockDuration,
|
|
@@ -18200,20 +18697,22 @@ class Portfolio {
|
|
|
18200
18697
|
isWithdrawalRequested: fixedTermDeposit.isWithdrawalRequested,
|
|
18201
18698
|
startTime: parseInt(fixedTermDeposit.createdOn),
|
|
18202
18699
|
yieldEarnings: {
|
|
18203
|
-
lastEpoch:
|
|
18700
|
+
lastEpoch: this.precisionToString(lastEpochYieldEarned),
|
|
18204
18701
|
lifetime: this.precisionToString(lifetimeYieldEarned),
|
|
18205
18702
|
},
|
|
18206
18703
|
depositDetails: fixedLoanDepositDetails,
|
|
18207
18704
|
};
|
|
18208
18705
|
});
|
|
18209
|
-
|
|
18706
|
+
// This POOL's last-epoch total: the tranche totals summed, the
|
|
18707
|
+
// locks counted once (inside `total`).
|
|
18708
|
+
poolYieldEarnedLastEpoch += lastEpochYield.total;
|
|
18210
18709
|
tranches.push(Object.assign(Object.assign({}, tranche), { investedAmount: userTrancheBalance, yieldEarnings: {
|
|
18211
|
-
lastEpoch:
|
|
18710
|
+
lastEpoch: this.precisionToString(lastEpochYield.variable),
|
|
18212
18711
|
lifetime: this.precisionToString(totalLifetimeFtdYield), // passing in total FTD yield to be subtracted by total trache lifetime later down
|
|
18213
18712
|
}, fixedLoans,
|
|
18214
18713
|
depositDetails }));
|
|
18215
18714
|
}
|
|
18216
|
-
portfolioLendingPoolsPromise.push(Object.assign(Object.assign({}, poolOverview), { totalYieldEarningsLastEpoch:
|
|
18715
|
+
portfolioLendingPoolsPromise.push(Object.assign(Object.assign({}, poolOverview), { totalYieldEarningsLastEpoch: this.precisionToString(poolYieldEarnedLastEpoch), totalInvestedAmount: userPoolBalance.balance.toString(), totalYieldEarningsLifetime: userPoolBalance.yieldEarned.toString(), tranches }));
|
|
18217
18716
|
}
|
|
18218
18717
|
const portfolioLendingPools = portfolioLendingPoolsPromise.map((pool) => (Object.assign(Object.assign({}, pool), { tranches: pool.tranches.map((tranche) => {
|
|
18219
18718
|
const trancheBalance = tranche.investedAmount;
|
|
@@ -18231,7 +18730,87 @@ class Portfolio {
|
|
|
18231
18730
|
!utils$2.parseUnits(pool.totalYieldEarningsLifetime).isZero());
|
|
18232
18731
|
});
|
|
18233
18732
|
}
|
|
18733
|
+
/**
|
|
18734
|
+
* The lender's settled yield per cycle (epoch), per pool and tranche —
|
|
18735
|
+
* what the chain credited at each close, NET of the performance fee
|
|
18736
|
+
* (`domain/yield-history.ts`), in the deployment's own stable token. Each
|
|
18737
|
+
* entry runs the same `epochTrancheYield` as `getPortfolioLendingData`'s
|
|
18738
|
+
* last-epoch figures: a tranche's `yieldEarnings.lastEpoch` plus its
|
|
18739
|
+
* locks' equals its entry for the epoch of the tranche's most recently
|
|
18740
|
+
* APPLIED interest (older than the last cycle for a pool that has not
|
|
18741
|
+
* cleared since; no entry, and a figure of 0, when the lender held
|
|
18742
|
+
* nothing at that epoch).
|
|
18743
|
+
*
|
|
18744
|
+
* Sum entries with `aggregateYieldHistory` (all strategies, or one with
|
|
18745
|
+
* `{ poolId }`). Only epochs in which the lender held shares appear — a
|
|
18746
|
+
* new lender gets `[]`. Pools in `UNUSED_LENDING_POOL_IDS` are left out,
|
|
18747
|
+
* as in `lendingPortfolioQuery`; Directus enablement is NOT applied.
|
|
18748
|
+
*
|
|
18749
|
+
* Cost: three parallel `SystemVariables` reads and the positions query in
|
|
18750
|
+
* one round, then one subgraph request per page of the longest series
|
|
18751
|
+
* (1000 rows a page) — normally two subgraph round trips in all, never one
|
|
18752
|
+
* per epoch.
|
|
18753
|
+
*
|
|
18754
|
+
* On a LITE deployment, a subgraph that lacks the entities (a validation
|
|
18755
|
+
* error naming them) yields `[]`; on a full deployment that error throws,
|
|
18756
|
+
* like every other failure (`isEmptyHistoryError`).
|
|
18757
|
+
*
|
|
18758
|
+
* @throws RangeError on a malformed `fromEpoch` / `count`, before any
|
|
18759
|
+
* network read.
|
|
18760
|
+
*/
|
|
18761
|
+
getYieldHistory(userAddress_1) {
|
|
18762
|
+
return __awaiter(this, arguments, void 0, function* (userAddress, options = {}) {
|
|
18763
|
+
validateYieldHistoryOptions(options);
|
|
18764
|
+
try {
|
|
18765
|
+
const [currentEpoch, epochZeroStart, epochDuration, positions] = yield Promise.all([
|
|
18766
|
+
this._systemVariablesAbi.currentEpochNumber(),
|
|
18767
|
+
this._systemVariablesAbi.epochStartTimestamp(0),
|
|
18768
|
+
this._systemVariablesAbi.epochDuration(),
|
|
18769
|
+
this._graph.request(yieldHistoryPositionsQuery, {
|
|
18770
|
+
userAddress: userAddress.toLowerCase(),
|
|
18771
|
+
unusedPools: this._kasuConfig.UNUSED_LENDING_POOL_IDS,
|
|
18772
|
+
first: YIELD_HISTORY_PAGE_SIZE,
|
|
18773
|
+
}),
|
|
18774
|
+
]);
|
|
18775
|
+
const window = resolveYieldHistoryWindow(options, currentEpoch.toNumber());
|
|
18776
|
+
if (!window || !positions.user)
|
|
18777
|
+
return [];
|
|
18778
|
+
const userTranches = [];
|
|
18779
|
+
const tranches = new Set();
|
|
18780
|
+
const locks = [];
|
|
18781
|
+
for (const pool of positions.user.lendingPoolUserDetails) {
|
|
18782
|
+
for (const position of pool.lendingPoolTrancheUserDetails) {
|
|
18783
|
+
userTranches.push(position.id);
|
|
18784
|
+
tranches.add(position.tranche.id);
|
|
18785
|
+
for (const lock of position.userLendingPoolTrancheFixedTermDepositLocks) {
|
|
18786
|
+
locks.push(lock.id);
|
|
18787
|
+
}
|
|
18788
|
+
}
|
|
18789
|
+
}
|
|
18790
|
+
if (userTranches.length === 0)
|
|
18791
|
+
return [];
|
|
18792
|
+
const { rows } = yield fetchYieldHistorySeries((variables) => this._graph.request(yieldHistoryPageQuery, variables), { tranches: [...tranches], userTranches, locks }, window);
|
|
18793
|
+
return buildYieldHistory(Object.assign({ positions }, rows), window, {
|
|
18794
|
+
epochZeroStart: epochZeroStart.toNumber(),
|
|
18795
|
+
epochDuration: epochDuration.toNumber(),
|
|
18796
|
+
});
|
|
18797
|
+
}
|
|
18798
|
+
catch (error) {
|
|
18799
|
+
if (isEmptyHistoryError(error, this._isLiteDeployment))
|
|
18800
|
+
return [];
|
|
18801
|
+
throw error;
|
|
18802
|
+
}
|
|
18803
|
+
});
|
|
18804
|
+
}
|
|
18805
|
+
/**
|
|
18806
|
+
* A money figure as a plain decimal string. Below a millionth of a token
|
|
18807
|
+
* (the stable asset's own precision) is float noise, never money, so it
|
|
18808
|
+
* is `'0'` — not `'1e-7'`, which `parseUnits` rejects, nor `'-3e-12'`. A
|
|
18809
|
+
* small negative remainder (above −0.0001) is `'0'` as well.
|
|
18810
|
+
*/
|
|
18234
18811
|
precisionToString(val) {
|
|
18812
|
+
if (Math.abs(val) < 1e-6)
|
|
18813
|
+
return '0';
|
|
18235
18814
|
if (val < 0 && val > -1e-4) {
|
|
18236
18815
|
return '0';
|
|
18237
18816
|
}
|
|
@@ -28254,6 +28833,40 @@ class PortfolioFacade {
|
|
|
28254
28833
|
return { pools, summary };
|
|
28255
28834
|
});
|
|
28256
28835
|
}
|
|
28836
|
+
/**
|
|
28837
|
+
* The lender's settled yield per cycle, per pool and tranche: what each
|
|
28838
|
+
* close credited them, NET of the performance fee (the chain credits it
|
|
28839
|
+
* net), in display units of the deployment's own stable token (AUDD on
|
|
28840
|
+
* XDC AUDD). `epochEnd` is unix seconds. Net only — no gross is offered.
|
|
28841
|
+
*
|
|
28842
|
+
* Validates `options` before any read (`RangeError` on a malformed one).
|
|
28843
|
+
*
|
|
28844
|
+
* ```ts
|
|
28845
|
+
* const entries = await kasu.portfolio.getYieldHistory('0xUser...', { count: 52 });
|
|
28846
|
+
* const allStrategies = aggregateYieldHistory(entries);
|
|
28847
|
+
* const oneStrategy = aggregateYieldHistory(entries, { poolId });
|
|
28848
|
+
* ```
|
|
28849
|
+
*
|
|
28850
|
+
* `getPositions`' `totalYieldEarningsLastEpoch` for a pool equals that
|
|
28851
|
+
* pool's bar for the epoch of its most recently APPLIED interest — which
|
|
28852
|
+
* is older than the last cycle for a pool that has not cleared since (the
|
|
28853
|
+
* bar carries that older `epochId`). A lender who held nothing at that
|
|
28854
|
+
* epoch has no bar there and a figure of 0; their newest bar is from
|
|
28855
|
+
* when they last held shares.
|
|
28856
|
+
*
|
|
28857
|
+
* For a fixed-term lock holder the bars can sum to slightly more than the
|
|
28858
|
+
* lifetime interest (up to about 0.2 %), because each top-up is valued at
|
|
28859
|
+
* today's share price — show "earned to date" from the lifetime figure,
|
|
28860
|
+
* never from the sum of the bars.
|
|
28861
|
+
*
|
|
28862
|
+
* `[]` for a lender with no history, and on a LITE deployment whose
|
|
28863
|
+
* subgraph lacks the entities; on a full deployment that error throws.
|
|
28864
|
+
*/
|
|
28865
|
+
getYieldHistory(userAddress, options) {
|
|
28866
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28867
|
+
return yield this._portfolio.getYieldHistory(userAddress, options);
|
|
28868
|
+
});
|
|
28869
|
+
}
|
|
28257
28870
|
/**
|
|
28258
28871
|
* Fetch a user's full transaction history (deposits, withdrawals, cancellations).
|
|
28259
28872
|
*/
|
|
@@ -29219,6 +29832,7 @@ exports.StrategiesFacade = StrategiesFacade;
|
|
|
29219
29832
|
exports.UPPER_MEZZANINE = UPPER_MEZZANINE;
|
|
29220
29833
|
exports.WithdrawFlow = WithdrawFlow;
|
|
29221
29834
|
exports.accruedInterest = accruedInterest;
|
|
29835
|
+
exports.aggregateYieldHistory = aggregateYieldHistory;
|
|
29222
29836
|
exports.apyToEpochRate = apyToEpochRate;
|
|
29223
29837
|
exports.asContractType = asContractType;
|
|
29224
29838
|
exports.auMinimumRemaining = auMinimumRemaining;
|
|
@@ -29249,6 +29863,7 @@ exports.encodeRequestWithdrawalCall = encodeRequestWithdrawalCall;
|
|
|
29249
29863
|
exports.encodeStableTransfer = encodeStableTransfer;
|
|
29250
29864
|
exports.encodeWithdrawAtExpiryCall = encodeWithdrawAtExpiryCall;
|
|
29251
29865
|
exports.epochRateToApy = epochRateToApy;
|
|
29866
|
+
exports.epochTrancheYield = epochTrancheYield;
|
|
29252
29867
|
exports.explorerAddressUrl = explorerAddressUrl;
|
|
29253
29868
|
exports.explorerTxUrl = explorerTxUrl;
|
|
29254
29869
|
exports.extractRevertData = extractRevertData;
|