@curvefi/llamalend-api 2.0.3 → 2.0.4
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/docs/SUPPORT_LLv2.md +6 -0
- package/lib/lendMarkets/LendMarketTemplate.js +11 -10
- package/lib/lendMarkets/interfaces/v1/statsV1.d.ts +1 -0
- package/lib/lendMarkets/interfaces/v2/statsV2.d.ts +1 -0
- package/lib/lendMarkets/modules/common/statsBase.d.ts +4 -2
- package/lib/lendMarkets/modules/common/statsBase.js +56 -85
- package/lib/lendMarkets/modules/v2/statsV2.d.ts +2 -0
- package/lib/lendMarkets/modules/v2/statsV2.js +19 -0
- package/lib/lendMarkets/utils.d.ts +14 -0
- package/lib/lendMarkets/utils.js +31 -0
- package/package.json +1 -1
- package/src/lendMarkets/LendMarketTemplate.ts +11 -10
- package/src/lendMarkets/interfaces/v1/statsV1.ts +1 -0
- package/src/lendMarkets/interfaces/v2/statsV2.ts +1 -0
- package/src/lendMarkets/modules/common/statsBase.ts +74 -92
- package/src/lendMarkets/modules/v2/statsV2.ts +9 -1
- package/src/lendMarkets/utils.ts +44 -0
package/docs/SUPPORT_LLv2.md
CHANGED
|
@@ -143,6 +143,12 @@ This document tracks feature support across market versions.
|
|
|
143
143
|
| ammBalances() | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
144
144
|
| capAndAvailable() | ✅ | ✅ | ❌ | ✅ | ❌ |
|
|
145
145
|
|
|
146
|
+
## Stats Module (`market.stats`) new methods
|
|
147
|
+
| Method | v1 | v2 | Same logic | Same params | Same type |
|
|
148
|
+
|--------|----|----|-----------------|----------------------|-----------------------|
|
|
149
|
+
| adminPercentage() | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
150
|
+
|
|
151
|
+
|
|
146
152
|
---
|
|
147
153
|
|
|
148
154
|
## Wallet Module (`market.wallet`)
|
|
@@ -68,16 +68,17 @@ export class LendMarketTemplate {
|
|
|
68
68
|
getCurrentLeverageParams: userPosition.getCurrentLeverageParams.bind(userPosition),
|
|
69
69
|
};
|
|
70
70
|
this.stats = {
|
|
71
|
-
parameters: stats.statsParameters.bind(
|
|
72
|
-
rates: stats.statsRates.bind(
|
|
73
|
-
futureRates: stats.statsFutureRates.bind(
|
|
74
|
-
balances: stats.statsBalances.bind(
|
|
75
|
-
bandsInfo: stats.statsBandsInfo.bind(
|
|
76
|
-
bandBalances: stats.statsBandBalances.bind(
|
|
77
|
-
bandsBalances: stats.statsBandsBalances.bind(
|
|
78
|
-
totalDebt: stats.statsTotalDebt.bind(
|
|
79
|
-
ammBalances: stats.statsAmmBalances.bind(
|
|
80
|
-
capAndAvailable: stats.statsCapAndAvailable.bind(
|
|
71
|
+
parameters: stats.statsParameters.bind(stats),
|
|
72
|
+
rates: stats.statsRates.bind(stats),
|
|
73
|
+
futureRates: stats.statsFutureRates.bind(stats),
|
|
74
|
+
balances: stats.statsBalances.bind(stats),
|
|
75
|
+
bandsInfo: stats.statsBandsInfo.bind(stats),
|
|
76
|
+
bandBalances: stats.statsBandBalances.bind(stats),
|
|
77
|
+
bandsBalances: stats.statsBandsBalances.bind(stats),
|
|
78
|
+
totalDebt: stats.statsTotalDebt.bind(stats),
|
|
79
|
+
ammBalances: stats.statsAmmBalances.bind(stats),
|
|
80
|
+
capAndAvailable: stats.statsCapAndAvailable.bind(stats),
|
|
81
|
+
adminPercentage: stats.statsAdminPercentage.bind(stats),
|
|
81
82
|
};
|
|
82
83
|
this.wallet = {
|
|
83
84
|
balances: wallet.balances.bind(this),
|
|
@@ -6,6 +6,9 @@ export declare class StatsBaseModule {
|
|
|
6
6
|
protected market: LendMarketTemplate;
|
|
7
7
|
protected llamalend: Llamalend;
|
|
8
8
|
constructor(market: LendMarketTemplate);
|
|
9
|
+
protected _fetchAdminPercentage: () => Promise<bigint>;
|
|
10
|
+
private _getRate;
|
|
11
|
+
private _getFutureRate;
|
|
9
12
|
statsParameters: (() => Promise<{
|
|
10
13
|
fee: string;
|
|
11
14
|
admin_fee: string;
|
|
@@ -21,8 +24,6 @@ export declare class StatsBaseModule {
|
|
|
21
24
|
base_price: string;
|
|
22
25
|
A: string;
|
|
23
26
|
}>>;
|
|
24
|
-
private _getRate;
|
|
25
|
-
private _getFutureRate;
|
|
26
27
|
statsRates(isGetter?: boolean, useAPI?: boolean): Promise<{
|
|
27
28
|
borrowApr: string;
|
|
28
29
|
lendApr: string;
|
|
@@ -66,4 +67,5 @@ export declare class StatsBaseModule {
|
|
|
66
67
|
cap: string;
|
|
67
68
|
available: string;
|
|
68
69
|
}>;
|
|
70
|
+
statsAdminPercentage: () => Promise<string>;
|
|
69
71
|
}
|
|
@@ -8,11 +8,39 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import memoize from "memoizee";
|
|
11
|
-
import { parseUnits,
|
|
11
|
+
import { parseUnits, toBN, formatUnits, formatNumber, } from "../../../utils";
|
|
12
12
|
import { _getMarketsData } from "../../../external-api";
|
|
13
13
|
import { cacheKey, cacheStats } from "../../../cache";
|
|
14
|
+
import { computeRatesFromRate, fetchMarketDataByVault } from "../../utils";
|
|
15
|
+
const PRECISION = BigInt("1000000000000000000"); // 1e18
|
|
14
16
|
export class StatsBaseModule {
|
|
15
17
|
constructor(market) {
|
|
18
|
+
this._fetchAdminPercentage = () => __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
return BigInt(0);
|
|
20
|
+
});
|
|
21
|
+
this._getRate = (...args_1) => __awaiter(this, [...args_1], void 0, function* (isGetter = true) {
|
|
22
|
+
if (isGetter) {
|
|
23
|
+
const _rate = cacheStats.get(cacheKey(this.market.addresses.amm, 'rate'));
|
|
24
|
+
const _adminPercentage = yield this._fetchAdminPercentage();
|
|
25
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const [_rate, _adminPercentage] = yield Promise.all([
|
|
29
|
+
this.llamalend.contracts[this.market.addresses.amm].contract.rate(this.llamalend.constantOptions),
|
|
30
|
+
this._fetchAdminPercentage(),
|
|
31
|
+
]);
|
|
32
|
+
cacheStats.set(cacheKey(this.market.addresses.controller, 'rate'), _rate);
|
|
33
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
this._getFutureRate = (_dReserves, _dDebt) => __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
const mpContract = this.llamalend.contracts[this.market.addresses.monetary_policy].contract;
|
|
38
|
+
const [_rate, _adminPercentage] = yield Promise.all([
|
|
39
|
+
mpContract.future_rate(this.market.addresses.controller, _dReserves, _dDebt),
|
|
40
|
+
this._fetchAdminPercentage(),
|
|
41
|
+
]);
|
|
42
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
43
|
+
});
|
|
16
44
|
this.statsParameters = memoize(() => __awaiter(this, void 0, void 0, function* () {
|
|
17
45
|
const llammaContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
|
|
18
46
|
const controllerContract = this.llamalend.contracts[this.market.addresses.controller].multicallContract;
|
|
@@ -34,21 +62,6 @@ export class StatsBaseModule {
|
|
|
34
62
|
promise: true,
|
|
35
63
|
maxAge: 5 * 60 * 1000, // 5m
|
|
36
64
|
});
|
|
37
|
-
this._getRate = (...args_1) => __awaiter(this, [...args_1], void 0, function* (isGetter = true) {
|
|
38
|
-
let _rate;
|
|
39
|
-
if (isGetter) {
|
|
40
|
-
_rate = cacheStats.get(cacheKey(this.market.addresses.amm, 'rate'));
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
_rate = yield this.llamalend.contracts[this.market.addresses.amm].contract.rate(this.llamalend.constantOptions);
|
|
44
|
-
cacheStats.set(cacheKey(this.market.addresses.controller, 'rate'), _rate);
|
|
45
|
-
}
|
|
46
|
-
return _rate;
|
|
47
|
-
});
|
|
48
|
-
this._getFutureRate = (_dReserves, _dDebt) => __awaiter(this, void 0, void 0, function* () {
|
|
49
|
-
const mpContract = this.llamalend.contracts[this.market.addresses.monetary_policy].contract;
|
|
50
|
-
return yield mpContract.future_rate(this.market.addresses.controller, _dReserves, _dDebt);
|
|
51
|
-
});
|
|
52
65
|
this.statsBandsInfo = memoize(() => __awaiter(this, void 0, void 0, function* () {
|
|
53
66
|
const ammContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
|
|
54
67
|
const calls = [
|
|
@@ -68,17 +81,11 @@ export class StatsBaseModule {
|
|
|
68
81
|
});
|
|
69
82
|
this.statsAmmBalances = (...args_1) => __awaiter(this, [...args_1], void 0, function* (isGetter = true, useAPI = false) {
|
|
70
83
|
if (useAPI) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
collateral: market.ammBalances.ammBalanceCollateral.toString(),
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
throw new Error('Market not found in API');
|
|
81
|
-
}
|
|
84
|
+
const market = yield fetchMarketDataByVault(this.llamalend.constants.NETWORK_NAME, this.market.addresses.vault, _getMarketsData);
|
|
85
|
+
return {
|
|
86
|
+
borrowed: market.ammBalances.ammBalanceBorrowed.toString(),
|
|
87
|
+
collateral: market.ammBalances.ammBalanceCollateral.toString(),
|
|
88
|
+
};
|
|
82
89
|
}
|
|
83
90
|
else {
|
|
84
91
|
const borrowedContract = this.llamalend.contracts[this.market.addresses.borrowed_token].multicallContract;
|
|
@@ -111,42 +118,29 @@ export class StatsBaseModule {
|
|
|
111
118
|
};
|
|
112
119
|
}
|
|
113
120
|
});
|
|
121
|
+
this.statsAdminPercentage = () => __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
const _adminPercentage = yield this._fetchAdminPercentage();
|
|
123
|
+
return formatUnits(_adminPercentage * BigInt(100));
|
|
124
|
+
});
|
|
114
125
|
this.market = market;
|
|
115
126
|
this.llamalend = market.getLlamalend();
|
|
116
127
|
}
|
|
117
128
|
statsRates() {
|
|
118
129
|
return __awaiter(this, arguments, void 0, function* (isGetter = true, useAPI = false) {
|
|
119
130
|
if (useAPI) {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
lendApy: (market.rates.lendApy * 100).toString(),
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
throw new Error('Market not found in API');
|
|
132
|
-
}
|
|
131
|
+
const market = yield fetchMarketDataByVault(this.llamalend.constants.NETWORK_NAME, this.market.addresses.vault, _getMarketsData);
|
|
132
|
+
return {
|
|
133
|
+
borrowApr: (market.rates.borrowApr * 100).toString(),
|
|
134
|
+
lendApr: (market.rates.lendApr * 100).toString(),
|
|
135
|
+
borrowApy: (market.rates.borrowApy * 100).toString(),
|
|
136
|
+
lendApy: (market.rates.lendApy * 100).toString(),
|
|
137
|
+
};
|
|
133
138
|
}
|
|
134
139
|
else {
|
|
135
140
|
const _rate = yield this._getRate(isGetter);
|
|
136
|
-
const borrowApr = toBN(_rate).times(365).times(86400).times(100).toString();
|
|
137
|
-
// borrowApy = e**(rate*365*86400) - 1
|
|
138
|
-
const borrowApy = String(((Math.pow(2.718281828459, (toBN(_rate).times(365).times(86400)).toNumber())) - 1) * 100);
|
|
139
|
-
let lendApr = "0";
|
|
140
|
-
let lendApy = "0";
|
|
141
141
|
const debt = yield this.statsTotalDebt(isGetter);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
lendApr = toBN(_rate).times(365).times(86400).times(debt).div(cap).times(100).toString();
|
|
145
|
-
// lendApy = (debt * e**(rate*365*86400) - debt) / cap
|
|
146
|
-
const debtInAYearBN = BN(debt).times(Math.pow(2.718281828459, (toBN(_rate).times(365).times(86400)).toNumber()));
|
|
147
|
-
lendApy = debtInAYearBN.minus(debt).div(cap).times(100).toString();
|
|
148
|
-
}
|
|
149
|
-
return { borrowApr, lendApr, borrowApy, lendApy };
|
|
142
|
+
const { cap } = Number(debt) > 0 ? yield this.statsCapAndAvailable(isGetter) : { cap: "0" };
|
|
143
|
+
return computeRatesFromRate(_rate, debt, cap);
|
|
150
144
|
}
|
|
151
145
|
});
|
|
152
146
|
}
|
|
@@ -155,20 +149,9 @@ export class StatsBaseModule {
|
|
|
155
149
|
const _dReserves = parseUnits(dReserves, this.market.borrowed_token.decimals);
|
|
156
150
|
const _dDebt = parseUnits(dDebt, this.market.borrowed_token.decimals);
|
|
157
151
|
const _rate = yield this._getFutureRate(_dReserves, _dDebt);
|
|
158
|
-
const borrowApr = toBN(_rate).times(365).times(86400).times(100).toString();
|
|
159
|
-
// borrowApy = e**(rate*365*86400) - 1
|
|
160
|
-
const borrowApy = String(((Math.pow(2.718281828459, (toBN(_rate).times(365).times(86400)).toNumber())) - 1) * 100);
|
|
161
|
-
let lendApr = "0";
|
|
162
|
-
let lendApy = "0";
|
|
163
152
|
const debt = Number(yield this.statsTotalDebt()) + Number(dDebt);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
lendApr = toBN(_rate).times(365).times(86400).times(debt).div(cap).times(100).toString();
|
|
167
|
-
// lendApy = (debt * e**(rate*365*86400) - debt) / cap
|
|
168
|
-
const debtInAYearBN = BN(debt).times(Math.pow(2.718281828459, (toBN(_rate).times(365).times(86400)).toNumber()));
|
|
169
|
-
lendApy = debtInAYearBN.minus(debt).div(cap).times(100).toString();
|
|
170
|
-
}
|
|
171
|
-
return { borrowApr, lendApr, borrowApy, lendApy };
|
|
153
|
+
const cap = Number((yield this.statsCapAndAvailable(true, useAPI)).cap) + Number(dReserves);
|
|
154
|
+
return computeRatesFromRate(_rate, debt, cap);
|
|
172
155
|
});
|
|
173
156
|
}
|
|
174
157
|
statsBalances() {
|
|
@@ -226,14 +209,8 @@ export class StatsBaseModule {
|
|
|
226
209
|
statsTotalDebt() {
|
|
227
210
|
return __awaiter(this, arguments, void 0, function* (isGetter = true, useAPI = true) {
|
|
228
211
|
if (useAPI) {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
if (market) {
|
|
232
|
-
return market.borrowed.total.toString();
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
throw new Error('Market not found in API');
|
|
236
|
-
}
|
|
212
|
+
const market = yield fetchMarketDataByVault(this.llamalend.constants.NETWORK_NAME, this.market.addresses.vault, _getMarketsData);
|
|
213
|
+
return market.borrowed.total.toString();
|
|
237
214
|
}
|
|
238
215
|
else {
|
|
239
216
|
let _debt;
|
|
@@ -251,17 +228,11 @@ export class StatsBaseModule {
|
|
|
251
228
|
statsCapAndAvailable() {
|
|
252
229
|
return __awaiter(this, arguments, void 0, function* (isGetter = true, useAPI = false) {
|
|
253
230
|
if (useAPI) {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
available: market.availableToBorrow.total.toString(),
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
throw new Error('Market not found in API');
|
|
264
|
-
}
|
|
231
|
+
const market = yield fetchMarketDataByVault(this.llamalend.constants.NETWORK_NAME, this.market.addresses.vault, _getMarketsData);
|
|
232
|
+
return {
|
|
233
|
+
cap: market.totalSupplied.total.toString(),
|
|
234
|
+
available: market.availableToBorrow.total.toString(),
|
|
235
|
+
};
|
|
265
236
|
}
|
|
266
237
|
else {
|
|
267
238
|
const vaultContract = this.llamalend.contracts[this.market.addresses.vault].multicallContract;
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import memoize from "memoizee";
|
|
1
11
|
import { StatsBaseModule } from "../common/statsBase.js";
|
|
2
12
|
export class StatsV2Module extends StatsBaseModule {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this._fetchAdminPercentage = memoize(() => __awaiter(this, void 0, void 0, function* () {
|
|
16
|
+
return yield this.llamalend.contracts[this.market.addresses.controller].contract.admin_percentage(this.llamalend.constantOptions);
|
|
17
|
+
}), {
|
|
18
|
+
promise: true,
|
|
19
|
+
maxAge: 30 * 60 * 1000, // 30m
|
|
20
|
+
});
|
|
21
|
+
}
|
|
3
22
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { INetworkName, IMarketData, IMarketDataAPI } from "../interfaces";
|
|
2
|
+
export type RatesResult = {
|
|
3
|
+
borrowApr: string;
|
|
4
|
+
lendApr: string;
|
|
5
|
+
borrowApy: string;
|
|
6
|
+
lendApy: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Computes borrow/lend APR and APY from a raw per-second rate and current debt/cap.
|
|
10
|
+
* borrowApy = e^(rate * 365 * 86400) - 1
|
|
11
|
+
* lendApy = (debt * e^(rate * 365 * 86400) - debt) / cap
|
|
12
|
+
*/
|
|
13
|
+
export declare const computeRatesFromRate: (_rate: bigint, debt: string | number, cap: string | number) => RatesResult;
|
|
14
|
+
export declare const fetchMarketDataByVault: (networkName: INetworkName, vaultAddress: string, getData: (network: INetworkName) => Promise<IMarketData>) => Promise<IMarketDataAPI>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { BN, toBN } from "../utils";
|
|
11
|
+
/**
|
|
12
|
+
* Computes borrow/lend APR and APY from a raw per-second rate and current debt/cap.
|
|
13
|
+
* borrowApy = e^(rate * 365 * 86400) - 1
|
|
14
|
+
* lendApy = (debt * e^(rate * 365 * 86400) - debt) / cap
|
|
15
|
+
*/
|
|
16
|
+
export const computeRatesFromRate = (_rate, debt, cap) => {
|
|
17
|
+
const annualFactor = toBN(_rate).times(365).times(86400);
|
|
18
|
+
const expFactor = Math.pow(Math.E, annualFactor.toNumber());
|
|
19
|
+
const borrowApr = annualFactor.times(100).toString();
|
|
20
|
+
const borrowApy = String((expFactor - 1) * 100);
|
|
21
|
+
const lendApr = annualFactor.times(debt).div(cap).times(100).toString();
|
|
22
|
+
const lendApy = BN(debt).times(expFactor).minus(debt).div(cap).times(100).toString();
|
|
23
|
+
return { borrowApr, lendApr, borrowApy, lendApy };
|
|
24
|
+
};
|
|
25
|
+
export const fetchMarketDataByVault = (networkName, vaultAddress, getData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
const response = yield getData(networkName);
|
|
27
|
+
const market = response.lendingVaultData.find((item) => item.address.toLowerCase() === vaultAddress.toLowerCase());
|
|
28
|
+
if (!market)
|
|
29
|
+
throw new Error("Market not found in API");
|
|
30
|
+
return market;
|
|
31
|
+
});
|
package/package.json
CHANGED
|
@@ -166,16 +166,17 @@ export class LendMarketTemplate<V extends 'v1' | 'v2' = 'v1' | 'v2'> {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
this.stats = {
|
|
169
|
-
parameters: stats.statsParameters.bind(
|
|
170
|
-
rates: stats.statsRates.bind(
|
|
171
|
-
futureRates: stats.statsFutureRates.bind(
|
|
172
|
-
balances: stats.statsBalances.bind(
|
|
173
|
-
bandsInfo: stats.statsBandsInfo.bind(
|
|
174
|
-
bandBalances: stats.statsBandBalances.bind(
|
|
175
|
-
bandsBalances: stats.statsBandsBalances.bind(
|
|
176
|
-
totalDebt: stats.statsTotalDebt.bind(
|
|
177
|
-
ammBalances: stats.statsAmmBalances.bind(
|
|
178
|
-
capAndAvailable: stats.statsCapAndAvailable.bind(
|
|
169
|
+
parameters: stats.statsParameters.bind(stats),
|
|
170
|
+
rates: stats.statsRates.bind(stats),
|
|
171
|
+
futureRates: stats.statsFutureRates.bind(stats),
|
|
172
|
+
balances: stats.statsBalances.bind(stats),
|
|
173
|
+
bandsInfo: stats.statsBandsInfo.bind(stats),
|
|
174
|
+
bandBalances: stats.statsBandBalances.bind(stats),
|
|
175
|
+
bandsBalances: stats.statsBandsBalances.bind(stats),
|
|
176
|
+
totalDebt: stats.statsTotalDebt.bind(stats),
|
|
177
|
+
ammBalances: stats.statsAmmBalances.bind(stats),
|
|
178
|
+
capAndAvailable: stats.statsCapAndAvailable.bind(stats),
|
|
179
|
+
adminPercentage: stats.statsAdminPercentage.bind(stats),
|
|
179
180
|
} as StatsForVersion<V>
|
|
180
181
|
|
|
181
182
|
this.wallet = {
|
|
@@ -18,4 +18,5 @@ export interface IStatsV1 {
|
|
|
18
18
|
totalDebt: (isGetter?: boolean, useAPI?: boolean) => Promise<string>,
|
|
19
19
|
ammBalances: (isGetter?: boolean, useAPI?: boolean) => Promise<{ borrowed: string, collateral: string }>,
|
|
20
20
|
capAndAvailable: (isGetter?: boolean, useAPI?: boolean) => Promise<{ cap: string, available: string }>,
|
|
21
|
+
adminPercentage: () => Promise<string>,
|
|
21
22
|
}
|
|
@@ -18,4 +18,5 @@ export interface IStatsV2 {
|
|
|
18
18
|
totalDebt: (isGetter?: boolean, useAPI?: boolean) => Promise<string>,
|
|
19
19
|
ammBalances: (isGetter?: boolean, useAPI?: boolean) => Promise<{ borrowed: string, collateral: string }>,
|
|
20
20
|
capAndAvailable: (isGetter?: boolean, useAPI?: boolean) => Promise<{ cap: string, available: string }>,
|
|
21
|
+
adminPercentage: () => Promise<string>,
|
|
21
22
|
}
|
|
@@ -3,7 +3,6 @@ import {TAmount} from "../../../interfaces";
|
|
|
3
3
|
import type { LendMarketTemplate } from "../../LendMarketTemplate";
|
|
4
4
|
import {
|
|
5
5
|
parseUnits,
|
|
6
|
-
BN,
|
|
7
6
|
toBN,
|
|
8
7
|
formatUnits,
|
|
9
8
|
formatNumber,
|
|
@@ -11,6 +10,8 @@ import {
|
|
|
11
10
|
import {Llamalend} from "../../../llamalend";
|
|
12
11
|
import {_getMarketsData} from "../../../external-api";
|
|
13
12
|
import {cacheKey, cacheStats} from "../../../cache";
|
|
13
|
+
import { computeRatesFromRate, fetchMarketDataByVault } from "../../utils";
|
|
14
|
+
const PRECISION = BigInt("1000000000000000000"); // 1e18
|
|
14
15
|
|
|
15
16
|
export class StatsBaseModule {
|
|
16
17
|
protected market: LendMarketTemplate;
|
|
@@ -21,6 +22,34 @@ export class StatsBaseModule {
|
|
|
21
22
|
this.llamalend = market.getLlamalend();
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
protected _fetchAdminPercentage = async (): Promise<bigint> => {
|
|
26
|
+
return BigInt(0)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private _getRate = async (isGetter = true): Promise<bigint> => {
|
|
30
|
+
if (isGetter) {
|
|
31
|
+
const _rate: bigint = cacheStats.get(cacheKey(this.market.addresses.amm, 'rate'));
|
|
32
|
+
const _adminPercentage = await this._fetchAdminPercentage();
|
|
33
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
34
|
+
} else {
|
|
35
|
+
const [_rate, _adminPercentage] = await Promise.all([
|
|
36
|
+
this.llamalend.contracts[this.market.addresses.amm].contract.rate(this.llamalend.constantOptions),
|
|
37
|
+
this._fetchAdminPercentage(),
|
|
38
|
+
]);
|
|
39
|
+
cacheStats.set(cacheKey(this.market.addresses.controller, 'rate'), _rate);
|
|
40
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private _getFutureRate = async (_dReserves: bigint, _dDebt: bigint): Promise<bigint> => {
|
|
45
|
+
const mpContract = this.llamalend.contracts[this.market.addresses.monetary_policy].contract;
|
|
46
|
+
const [_rate, _adminPercentage] = await Promise.all([
|
|
47
|
+
mpContract.future_rate(this.market.addresses.controller, _dReserves, _dDebt),
|
|
48
|
+
this._fetchAdminPercentage(),
|
|
49
|
+
]);
|
|
50
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
51
|
+
}
|
|
52
|
+
|
|
24
53
|
public statsParameters = memoize(async (): Promise<{
|
|
25
54
|
fee: string, // %
|
|
26
55
|
admin_fee: string, // %
|
|
@@ -53,55 +82,24 @@ export class StatsBaseModule {
|
|
|
53
82
|
maxAge: 5 * 60 * 1000, // 5m
|
|
54
83
|
});
|
|
55
84
|
|
|
56
|
-
private _getRate = async (isGetter = true): Promise<bigint> => {
|
|
57
|
-
let _rate;
|
|
58
|
-
if(isGetter) {
|
|
59
|
-
_rate = cacheStats.get(cacheKey(this.market.addresses.amm, 'rate'));
|
|
60
|
-
} else {
|
|
61
|
-
_rate = await this.llamalend.contracts[this.market.addresses.amm].contract.rate(this.llamalend.constantOptions);
|
|
62
|
-
cacheStats.set(cacheKey(this.market.addresses.controller, 'rate'), _rate);
|
|
63
|
-
}
|
|
64
|
-
return _rate;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private _getFutureRate = async (_dReserves: bigint, _dDebt: bigint): Promise<bigint> => {
|
|
68
|
-
const mpContract = this.llamalend.contracts[this.market.addresses.monetary_policy].contract;
|
|
69
|
-
return await mpContract.future_rate(this.market.addresses.controller, _dReserves, _dDebt);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
85
|
public async statsRates(isGetter = true, useAPI = false): Promise<{borrowApr: string, lendApr: string, borrowApy: string, lendApy: string}> {
|
|
73
86
|
if(useAPI) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
} else {
|
|
86
|
-
throw new Error('Market not found in API')
|
|
87
|
-
}
|
|
87
|
+
const market = await fetchMarketDataByVault(
|
|
88
|
+
this.llamalend.constants.NETWORK_NAME,
|
|
89
|
+
this.market.addresses.vault,
|
|
90
|
+
_getMarketsData
|
|
91
|
+
);
|
|
92
|
+
return {
|
|
93
|
+
borrowApr: (market.rates.borrowApr * 100).toString(),
|
|
94
|
+
lendApr: (market.rates.lendApr * 100).toString(),
|
|
95
|
+
borrowApy: (market.rates.borrowApy * 100).toString(),
|
|
96
|
+
lendApy: (market.rates.lendApy * 100).toString(),
|
|
97
|
+
};
|
|
88
98
|
} else {
|
|
89
99
|
const _rate = await this._getRate(isGetter);
|
|
90
|
-
const borrowApr = toBN(_rate).times(365).times(86400).times(100).toString();
|
|
91
|
-
// borrowApy = e**(rate*365*86400) - 1
|
|
92
|
-
const borrowApy = String(((2.718281828459 ** (toBN(_rate).times(365).times(86400)).toNumber()) - 1) * 100);
|
|
93
|
-
let lendApr = "0";
|
|
94
|
-
let lendApy = "0";
|
|
95
100
|
const debt = await this.statsTotalDebt(isGetter);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
lendApr = toBN(_rate).times(365).times(86400).times(debt).div(cap).times(100).toString();
|
|
99
|
-
// lendApy = (debt * e**(rate*365*86400) - debt) / cap
|
|
100
|
-
const debtInAYearBN = BN(debt).times(2.718281828459 ** (toBN(_rate).times(365).times(86400)).toNumber());
|
|
101
|
-
lendApy = debtInAYearBN.minus(debt).div(cap).times(100).toString();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return { borrowApr, lendApr, borrowApy, lendApy }
|
|
101
|
+
const { cap } = Number(debt) > 0 ? await this.statsCapAndAvailable(isGetter) : { cap: "0" };
|
|
102
|
+
return computeRatesFromRate(_rate, debt, cap);
|
|
105
103
|
}
|
|
106
104
|
}
|
|
107
105
|
|
|
@@ -109,21 +107,9 @@ export class StatsBaseModule {
|
|
|
109
107
|
const _dReserves = parseUnits(dReserves, this.market.borrowed_token.decimals);
|
|
110
108
|
const _dDebt = parseUnits(dDebt, this.market.borrowed_token.decimals);
|
|
111
109
|
const _rate = await this._getFutureRate(_dReserves, _dDebt);
|
|
112
|
-
const borrowApr = toBN(_rate).times(365).times(86400).times(100).toString();
|
|
113
|
-
// borrowApy = e**(rate*365*86400) - 1
|
|
114
|
-
const borrowApy = String(((2.718281828459 ** (toBN(_rate).times(365).times(86400)).toNumber()) - 1) * 100);
|
|
115
|
-
let lendApr = "0";
|
|
116
|
-
let lendApy = "0";
|
|
117
110
|
const debt = Number(await this.statsTotalDebt()) + Number(dDebt);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
lendApr = toBN(_rate).times(365).times(86400).times(debt).div(cap).times(100).toString();
|
|
121
|
-
// lendApy = (debt * e**(rate*365*86400) - debt) / cap
|
|
122
|
-
const debtInAYearBN = BN(debt).times(2.718281828459 ** (toBN(_rate).times(365).times(86400)).toNumber());
|
|
123
|
-
lendApy = debtInAYearBN.minus(debt).div(cap).times(100).toString();
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return { borrowApr, lendApr, borrowApy, lendApy }
|
|
111
|
+
const cap = Number((await this.statsCapAndAvailable(true, useAPI)).cap) + Number(dReserves);
|
|
112
|
+
return computeRatesFromRate(_rate, debt, cap);
|
|
127
113
|
}
|
|
128
114
|
|
|
129
115
|
public async statsBalances(): Promise<[string, string]> {
|
|
@@ -202,15 +188,12 @@ export class StatsBaseModule {
|
|
|
202
188
|
|
|
203
189
|
public async statsTotalDebt(isGetter = true, useAPI = true): Promise<string> {
|
|
204
190
|
if(useAPI) {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
} else {
|
|
212
|
-
throw new Error('Market not found in API')
|
|
213
|
-
}
|
|
191
|
+
const market = await fetchMarketDataByVault(
|
|
192
|
+
this.llamalend.constants.NETWORK_NAME,
|
|
193
|
+
this.market.addresses.vault,
|
|
194
|
+
_getMarketsData
|
|
195
|
+
);
|
|
196
|
+
return market.borrowed.total.toString();
|
|
214
197
|
} else {
|
|
215
198
|
let _debt;
|
|
216
199
|
if(isGetter) {
|
|
@@ -226,18 +209,15 @@ export class StatsBaseModule {
|
|
|
226
209
|
|
|
227
210
|
public statsAmmBalances = async (isGetter = true, useAPI = false): Promise<{ borrowed: string, collateral: string }> => {
|
|
228
211
|
if(useAPI) {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
} else {
|
|
239
|
-
throw new Error('Market not found in API')
|
|
240
|
-
}
|
|
212
|
+
const market = await fetchMarketDataByVault(
|
|
213
|
+
this.llamalend.constants.NETWORK_NAME,
|
|
214
|
+
this.market.addresses.vault,
|
|
215
|
+
_getMarketsData
|
|
216
|
+
);
|
|
217
|
+
return {
|
|
218
|
+
borrowed: market.ammBalances.ammBalanceBorrowed.toString(),
|
|
219
|
+
collateral: market.ammBalances.ammBalanceCollateral.toString(),
|
|
220
|
+
};
|
|
241
221
|
} else {
|
|
242
222
|
const borrowedContract = this.llamalend.contracts[this.market.addresses.borrowed_token].multicallContract;
|
|
243
223
|
const collateralContract = this.llamalend.contracts[this.market.addresses.collateral_token].multicallContract;
|
|
@@ -273,18 +253,15 @@ export class StatsBaseModule {
|
|
|
273
253
|
|
|
274
254
|
public async statsCapAndAvailable(isGetter = true, useAPI = false): Promise<{ cap: string, available: string }> {
|
|
275
255
|
if(useAPI) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
} else {
|
|
286
|
-
throw new Error('Market not found in API')
|
|
287
|
-
}
|
|
256
|
+
const market = await fetchMarketDataByVault(
|
|
257
|
+
this.llamalend.constants.NETWORK_NAME,
|
|
258
|
+
this.market.addresses.vault,
|
|
259
|
+
_getMarketsData
|
|
260
|
+
);
|
|
261
|
+
return {
|
|
262
|
+
cap: market.totalSupplied.total.toString(),
|
|
263
|
+
available: market.availableToBorrow.total.toString(),
|
|
264
|
+
};
|
|
288
265
|
} else {
|
|
289
266
|
const vaultContract = this.llamalend.contracts[this.market.addresses.vault].multicallContract;
|
|
290
267
|
const borrowedContract = this.llamalend.contracts[this.market.addresses.borrowed_token].multicallContract;
|
|
@@ -310,4 +287,9 @@ export class StatsBaseModule {
|
|
|
310
287
|
// add cap: controller.borrow_cap() // Display
|
|
311
288
|
}
|
|
312
289
|
}
|
|
290
|
+
|
|
291
|
+
public statsAdminPercentage = async (): Promise<string> => {
|
|
292
|
+
const _adminPercentage = await this._fetchAdminPercentage();
|
|
293
|
+
return formatUnits(_adminPercentage * BigInt(100));
|
|
294
|
+
}
|
|
313
295
|
}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import memoize from "memoizee";
|
|
1
2
|
import { StatsBaseModule } from "../common/statsBase.js";
|
|
2
3
|
|
|
3
|
-
export class StatsV2Module extends StatsBaseModule {
|
|
4
|
+
export class StatsV2Module extends StatsBaseModule {
|
|
5
|
+
protected _fetchAdminPercentage = memoize(async (): Promise<bigint> => {
|
|
6
|
+
return await this.llamalend.contracts[this.market.addresses.controller].contract.admin_percentage(this.llamalend.constantOptions);
|
|
7
|
+
}, {
|
|
8
|
+
promise: true,
|
|
9
|
+
maxAge: 30 * 60 * 1000, // 30m
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BN, toBN } from "../utils";
|
|
2
|
+
import { INetworkName, IMarketData, IMarketDataAPI } from "../interfaces";
|
|
3
|
+
|
|
4
|
+
export type RatesResult = {
|
|
5
|
+
borrowApr: string;
|
|
6
|
+
lendApr: string;
|
|
7
|
+
borrowApy: string;
|
|
8
|
+
lendApy: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Computes borrow/lend APR and APY from a raw per-second rate and current debt/cap.
|
|
13
|
+
* borrowApy = e^(rate * 365 * 86400) - 1
|
|
14
|
+
* lendApy = (debt * e^(rate * 365 * 86400) - debt) / cap
|
|
15
|
+
*/
|
|
16
|
+
export const computeRatesFromRate = (
|
|
17
|
+
_rate: bigint,
|
|
18
|
+
debt: string | number,
|
|
19
|
+
cap: string | number
|
|
20
|
+
): RatesResult => {
|
|
21
|
+
const annualFactor = toBN(_rate).times(365).times(86400);
|
|
22
|
+
const expFactor = Math.E ** annualFactor.toNumber();
|
|
23
|
+
|
|
24
|
+
const borrowApr = annualFactor.times(100).toString();
|
|
25
|
+
const borrowApy = String((expFactor - 1) * 100);
|
|
26
|
+
|
|
27
|
+
const lendApr = annualFactor.times(debt).div(cap).times(100).toString();
|
|
28
|
+
const lendApy = BN(debt).times(expFactor).minus(debt).div(cap).times(100).toString();
|
|
29
|
+
|
|
30
|
+
return { borrowApr, lendApr, borrowApy, lendApy };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const fetchMarketDataByVault = async (
|
|
34
|
+
networkName: INetworkName,
|
|
35
|
+
vaultAddress: string,
|
|
36
|
+
getData: (network: INetworkName) => Promise<IMarketData>
|
|
37
|
+
): Promise<IMarketDataAPI> => {
|
|
38
|
+
const response = await getData(networkName);
|
|
39
|
+
const market = response.lendingVaultData.find(
|
|
40
|
+
(item) => item.address.toLowerCase() === vaultAddress.toLowerCase()
|
|
41
|
+
);
|
|
42
|
+
if (!market) throw new Error("Market not found in API");
|
|
43
|
+
return market;
|
|
44
|
+
}
|