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