@curvefi/llamalend-api 2.0.3 → 2.0.5
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 +42 -0
- package/lib/interfaces.d.ts +1 -0
- package/lib/lendMarkets/LendMarketTemplate.js +12 -11
- package/lib/lendMarkets/interfaces/v1/statsV1.d.ts +4 -1
- package/lib/lendMarkets/interfaces/v2/statsV2.d.ts +4 -1
- package/lib/lendMarkets/modules/common/statsBase.d.ts +23 -4
- package/lib/lendMarkets/modules/common/statsBase.js +146 -143
- package/lib/lendMarkets/modules/common/vault.js +2 -2
- package/lib/lendMarkets/modules/v2/statsV2.d.ts +16 -0
- package/lib/lendMarkets/modules/v2/statsV2.js +66 -0
- package/lib/lendMarkets/utils.d.ts +14 -0
- package/lib/lendMarkets/utils.js +33 -0
- package/package.json +1 -1
- package/src/interfaces.ts +1 -0
- package/src/lendMarkets/LendMarketTemplate.ts +12 -11
- package/src/lendMarkets/interfaces/v1/statsV1.ts +2 -1
- package/src/lendMarkets/interfaces/v2/statsV2.ts +2 -1
- package/src/lendMarkets/modules/common/statsBase.ts +149 -148
- package/src/lendMarkets/modules/common/vault.ts +2 -2
- package/src/lendMarkets/modules/v2/statsV2.ts +59 -1
- package/src/lendMarkets/utils.ts +46 -0
|
@@ -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,33 @@
|
|
|
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 lendAprRaw = annualFactor.times(debt).div(cap).times(100);
|
|
22
|
+
const lendApr = lendAprRaw.isNaN() ? "0" : lendAprRaw.toString();
|
|
23
|
+
const lendApyRaw = BN(debt).times(expFactor).minus(debt).div(cap).times(100);
|
|
24
|
+
const lendApy = lendApyRaw.isNaN() ? "0" : lendApyRaw.toString();
|
|
25
|
+
return { borrowApr, lendApr, borrowApy, lendApy };
|
|
26
|
+
};
|
|
27
|
+
export const fetchMarketDataByVault = (networkName, vaultAddress, getData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
+
const response = yield getData(networkName);
|
|
29
|
+
const market = response.lendingVaultData.find((item) => item.address.toLowerCase() === vaultAddress.toLowerCase());
|
|
30
|
+
if (!market)
|
|
31
|
+
throw new Error("Market not found in API");
|
|
32
|
+
return market;
|
|
33
|
+
});
|
package/package.json
CHANGED
package/src/interfaces.ts
CHANGED
|
@@ -166,20 +166,21 @@ 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 = {
|
|
182
|
-
balances: wallet.balances.bind(
|
|
183
|
+
balances: wallet.balances.bind(wallet),
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
this.prices = {
|
|
@@ -17,5 +17,6 @@ export interface IStatsV1 {
|
|
|
17
17
|
bandsBalances: () => Promise<{ [index: number]: { borrowed: string, collateral: string } }>,
|
|
18
18
|
totalDebt: (isGetter?: boolean, useAPI?: boolean) => Promise<string>,
|
|
19
19
|
ammBalances: (isGetter?: boolean, useAPI?: boolean) => Promise<{ borrowed: string, collateral: string }>,
|
|
20
|
-
capAndAvailable: (isGetter?: boolean, useAPI?: boolean) => Promise<{
|
|
20
|
+
capAndAvailable: (isGetter?: boolean, useAPI?: boolean) => Promise<{ borrowCap: string, available: string, totalAssets: string, availableForBorrow: string }>,
|
|
21
|
+
adminPercentage: () => Promise<string>,
|
|
21
22
|
}
|
|
@@ -17,5 +17,6 @@ export interface IStatsV2 {
|
|
|
17
17
|
bandsBalances: () => Promise<{ [index: number]: { borrowed: string, collateral: string } }>,
|
|
18
18
|
totalDebt: (isGetter?: boolean, useAPI?: boolean) => Promise<string>,
|
|
19
19
|
ammBalances: (isGetter?: boolean, useAPI?: boolean) => Promise<{ borrowed: string, collateral: string }>,
|
|
20
|
-
capAndAvailable: (isGetter?: boolean, useAPI?: boolean) => Promise<{
|
|
20
|
+
capAndAvailable: (isGetter?: boolean, useAPI?: boolean) => Promise<{ borrowCap: string, available: string, totalAssets: string, availableForBorrow: string }>,
|
|
21
|
+
adminPercentage: () => Promise<string>,
|
|
21
22
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import memoize from "memoizee";
|
|
2
|
-
import {TAmount} from "../../../interfaces";
|
|
2
|
+
import {TAmount, IMarketDataAPI} 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,63 @@ export class StatsBaseModule {
|
|
|
21
22
|
this.llamalend = market.getLlamalend();
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
protected async _fetchMarketDataFromAPI(): Promise<IMarketDataAPI> {
|
|
26
|
+
return fetchMarketDataByVault(
|
|
27
|
+
this.llamalend.constants.NETWORK_NAME,
|
|
28
|
+
this.market.addresses.vault,
|
|
29
|
+
_getMarketsData
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected _fetchAdminPercentage = async (): Promise<bigint> => {
|
|
34
|
+
return BigInt(0)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected _fetchAdminFee = async (): Promise<bigint> => {
|
|
38
|
+
return this.llamalend.contracts[this.market.addresses.amm].contract.admin_fee(this.llamalend.constantOptions);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected async _getAdminFeesXY(isGetter: boolean): Promise<[bigint, bigint]> {
|
|
42
|
+
if(isGetter) {
|
|
43
|
+
return [
|
|
44
|
+
cacheStats.get(cacheKey(this.market.addresses.amm, 'admin_fees_x')),
|
|
45
|
+
cacheStats.get(cacheKey(this.market.addresses.amm, 'admin_fees_y')),
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
const ammContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
|
|
49
|
+
const [_fee_x, _fee_y] = await this.llamalend.multicallProvider.all([
|
|
50
|
+
ammContract.admin_fees_x(),
|
|
51
|
+
ammContract.admin_fees_y(),
|
|
52
|
+
]) as [bigint, bigint];
|
|
53
|
+
cacheStats.set(cacheKey(this.market.addresses.amm, 'admin_fees_x'), _fee_x);
|
|
54
|
+
cacheStats.set(cacheKey(this.market.addresses.amm, 'admin_fees_y'), _fee_y);
|
|
55
|
+
return [_fee_x, _fee_y];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private _getRate = async (isGetter = true): Promise<bigint> => {
|
|
59
|
+
if (isGetter) {
|
|
60
|
+
const _rate: bigint = cacheStats.get(cacheKey(this.market.addresses.amm, 'rate'));
|
|
61
|
+
const _adminPercentage = await this._fetchAdminPercentage();
|
|
62
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
63
|
+
} else {
|
|
64
|
+
const [_rate, _adminPercentage] = await Promise.all([
|
|
65
|
+
this.llamalend.contracts[this.market.addresses.amm].contract.rate(this.llamalend.constantOptions),
|
|
66
|
+
this._fetchAdminPercentage(),
|
|
67
|
+
]);
|
|
68
|
+
cacheStats.set(cacheKey(this.market.addresses.controller, 'rate'), _rate);
|
|
69
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private _getFutureRate = async (_dReserves: bigint, _dDebt: bigint): Promise<bigint> => {
|
|
74
|
+
const mpContract = this.llamalend.contracts[this.market.addresses.monetary_policy].contract;
|
|
75
|
+
const [_rate, _adminPercentage] = await Promise.all([
|
|
76
|
+
mpContract.future_rate(this.market.addresses.controller, _dReserves, _dDebt),
|
|
77
|
+
this._fetchAdminPercentage(),
|
|
78
|
+
]);
|
|
79
|
+
return _rate * (PRECISION - _adminPercentage) / PRECISION;
|
|
80
|
+
}
|
|
81
|
+
|
|
24
82
|
public statsParameters = memoize(async (): Promise<{
|
|
25
83
|
fee: string, // %
|
|
26
84
|
admin_fee: string, // %
|
|
@@ -32,16 +90,17 @@ export class StatsBaseModule {
|
|
|
32
90
|
const llammaContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
|
|
33
91
|
const controllerContract = this.llamalend.contracts[this.market.addresses.controller].multicallContract;
|
|
34
92
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
93
|
+
const [[_fee, _liquidation_discount, _loan_discount, _base_price, _A], _admin_fee] = await Promise.all([
|
|
94
|
+
this.llamalend.multicallProvider.all([
|
|
95
|
+
llammaContract.fee(),
|
|
96
|
+
controllerContract.liquidation_discount(),
|
|
97
|
+
controllerContract.loan_discount(),
|
|
98
|
+
llammaContract.get_base_price(),
|
|
99
|
+
llammaContract.A(),
|
|
100
|
+
]),
|
|
101
|
+
this._fetchAdminFee(),
|
|
102
|
+
]) as [bigint[], bigint];
|
|
43
103
|
|
|
44
|
-
const [_fee, _admin_fee, _liquidation_discount, _loan_discount, _base_price, _A]: bigint[] = await this.llamalend.multicallProvider.all(calls) as bigint[];
|
|
45
104
|
const A = formatUnits(_A, 0)
|
|
46
105
|
const base_price = formatUnits(_base_price)
|
|
47
106
|
const [fee, admin_fee, liquidation_discount, loan_discount] = [_fee, _admin_fee, _liquidation_discount, _loan_discount]
|
|
@@ -53,55 +112,20 @@ export class StatsBaseModule {
|
|
|
53
112
|
maxAge: 5 * 60 * 1000, // 5m
|
|
54
113
|
});
|
|
55
114
|
|
|
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
115
|
public async statsRates(isGetter = true, useAPI = false): Promise<{borrowApr: string, lendApr: string, borrowApy: string, lendApy: string}> {
|
|
73
116
|
if(useAPI) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
lendApr: (market.rates.lendApr * 100).toString(),
|
|
82
|
-
borrowApy: (market.rates.borrowApy * 100).toString(),
|
|
83
|
-
lendApy: (market.rates.lendApy * 100).toString(),
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
throw new Error('Market not found in API')
|
|
87
|
-
}
|
|
117
|
+
const market = await this._fetchMarketDataFromAPI();
|
|
118
|
+
return {
|
|
119
|
+
borrowApr: (market.rates.borrowApr * 100).toString(),
|
|
120
|
+
lendApr: (market.rates.lendApr * 100).toString(),
|
|
121
|
+
borrowApy: (market.rates.borrowApy * 100).toString(),
|
|
122
|
+
lendApy: (market.rates.lendApy * 100).toString(),
|
|
123
|
+
};
|
|
88
124
|
} else {
|
|
89
125
|
const _rate = await this._getRate(isGetter);
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
let lendApr = "0";
|
|
94
|
-
let lendApy = "0";
|
|
95
|
-
const debt = await this.statsTotalDebt(isGetter);
|
|
96
|
-
if (Number(debt) > 0) {
|
|
97
|
-
const { cap } = await this.statsCapAndAvailable(isGetter);
|
|
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 }
|
|
126
|
+
const debt = await this.statsTotalDebt(isGetter, false);
|
|
127
|
+
const { totalAssets } = Number(debt) > 0 ? await this.statsCapAndAvailable(isGetter, false) : { totalAssets: "0" };
|
|
128
|
+
return computeRatesFromRate(_rate, debt, totalAssets);
|
|
105
129
|
}
|
|
106
130
|
}
|
|
107
131
|
|
|
@@ -109,34 +133,22 @@ export class StatsBaseModule {
|
|
|
109
133
|
const _dReserves = parseUnits(dReserves, this.market.borrowed_token.decimals);
|
|
110
134
|
const _dDebt = parseUnits(dDebt, this.market.borrowed_token.decimals);
|
|
111
135
|
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
136
|
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 }
|
|
137
|
+
const cap = Number((await this.statsCapAndAvailable(true, useAPI)).totalAssets) + Number(dReserves);
|
|
138
|
+
return computeRatesFromRate(_rate, debt, cap);
|
|
127
139
|
}
|
|
128
140
|
|
|
129
141
|
public async statsBalances(): Promise<[string, string]> {
|
|
130
142
|
const borrowedContract = this.llamalend.contracts[this.market.borrowed_token.address].multicallContract;
|
|
131
143
|
const collateralContract = this.llamalend.contracts[this.market.collateral_token.address].multicallContract;
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
144
|
+
|
|
145
|
+
const [[_borrowedBalance, _collateralBalance], [_borrowedAdminFees, _collateralAdminFees]] = await Promise.all([
|
|
146
|
+
this.llamalend.multicallProvider.all([
|
|
147
|
+
borrowedContract.balanceOf(this.market.addresses.amm),
|
|
148
|
+
collateralContract.balanceOf(this.market.addresses.amm),
|
|
149
|
+
]),
|
|
150
|
+
this._getAdminFeesXY(false),
|
|
151
|
+
]) as [bigint[], [bigint, bigint]];
|
|
140
152
|
|
|
141
153
|
return [
|
|
142
154
|
formatUnits(_borrowedBalance - _borrowedAdminFees, this.market.borrowed_token.decimals),
|
|
@@ -202,15 +214,8 @@ export class StatsBaseModule {
|
|
|
202
214
|
|
|
203
215
|
public async statsTotalDebt(isGetter = true, useAPI = true): Promise<string> {
|
|
204
216
|
if(useAPI) {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
const market = response.lendingVaultData.find((item) => item.address.toLowerCase() === this.market.addresses.vault.toLowerCase())
|
|
208
|
-
|
|
209
|
-
if(market) {
|
|
210
|
-
return market.borrowed.total.toString();
|
|
211
|
-
} else {
|
|
212
|
-
throw new Error('Market not found in API')
|
|
213
|
-
}
|
|
217
|
+
const market = await this._fetchMarketDataFromAPI();
|
|
218
|
+
return market.borrowed.total.toString();
|
|
214
219
|
} else {
|
|
215
220
|
let _debt;
|
|
216
221
|
if(isGetter) {
|
|
@@ -226,42 +231,32 @@ export class StatsBaseModule {
|
|
|
226
231
|
|
|
227
232
|
public statsAmmBalances = async (isGetter = true, useAPI = false): Promise<{ borrowed: string, collateral: string }> => {
|
|
228
233
|
if(useAPI) {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return {
|
|
235
|
-
borrowed: market.ammBalances.ammBalanceBorrowed.toString(),
|
|
236
|
-
collateral: market.ammBalances.ammBalanceCollateral.toString(),
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
throw new Error('Market not found in API')
|
|
240
|
-
}
|
|
234
|
+
const market = await this._fetchMarketDataFromAPI();
|
|
235
|
+
return {
|
|
236
|
+
borrowed: market.ammBalances.ammBalanceBorrowed.toString(),
|
|
237
|
+
collateral: market.ammBalances.ammBalanceCollateral.toString(),
|
|
238
|
+
};
|
|
241
239
|
} else {
|
|
242
240
|
const borrowedContract = this.llamalend.contracts[this.market.addresses.borrowed_token].multicallContract;
|
|
243
241
|
const collateralContract = this.llamalend.contracts[this.market.addresses.collateral_token].multicallContract;
|
|
244
|
-
const ammContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
|
|
245
242
|
|
|
246
|
-
let _balance_x
|
|
243
|
+
let _balance_x: bigint, _balance_y: bigint;
|
|
244
|
+
let _fee_x: bigint, _fee_y: bigint;
|
|
245
|
+
|
|
247
246
|
if(isGetter) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
cacheStats.get(cacheKey(this.market.addresses.collateral_token, 'balanceOf', this.market.addresses.amm)),
|
|
252
|
-
cacheStats.get(cacheKey(this.market.addresses.amm, 'admin_fees_y')),
|
|
253
|
-
]
|
|
247
|
+
_balance_x = cacheStats.get(cacheKey(this.market.addresses.borrowed_token, 'balanceOf', this.market.addresses.amm));
|
|
248
|
+
_balance_y = cacheStats.get(cacheKey(this.market.addresses.collateral_token, 'balanceOf', this.market.addresses.amm));
|
|
249
|
+
[_fee_x, _fee_y] = await this._getAdminFeesXY(true);
|
|
254
250
|
} else {
|
|
255
|
-
[_balance_x,
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
251
|
+
[[_balance_x, _balance_y], [_fee_x, _fee_y]] = await Promise.all([
|
|
252
|
+
this.llamalend.multicallProvider.all([
|
|
253
|
+
borrowedContract.balanceOf(this.market.addresses.amm),
|
|
254
|
+
collateralContract.balanceOf(this.market.addresses.amm),
|
|
255
|
+
]),
|
|
256
|
+
this._getAdminFeesXY(false),
|
|
257
|
+
]) as [[bigint, bigint], [bigint, bigint]];
|
|
261
258
|
cacheStats.set(cacheKey(this.market.addresses.borrowed_token, 'balanceOf', this.market.addresses.amm), _balance_x);
|
|
262
|
-
cacheStats.set(cacheKey(this.market.addresses.amm, 'admin_fees_x'), _fee_x);
|
|
263
259
|
cacheStats.set(cacheKey(this.market.addresses.collateral_token, 'balanceOf', this.market.addresses.amm), _balance_y);
|
|
264
|
-
cacheStats.set(cacheKey(this.market.addresses.amm, 'admin_fees_y'), _fee_y);
|
|
265
260
|
}
|
|
266
261
|
|
|
267
262
|
return {
|
|
@@ -271,43 +266,49 @@ export class StatsBaseModule {
|
|
|
271
266
|
}
|
|
272
267
|
}
|
|
273
268
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
269
|
+
protected async _statsCapAndAvailableFromAPI(): Promise<{ borrowCap: string, available: string, totalAssets: string, availableForBorrow: string }> {
|
|
270
|
+
const market = await this._fetchMarketDataFromAPI();
|
|
271
|
+
return {
|
|
272
|
+
totalAssets: market.totalSupplied.total.toString(),
|
|
273
|
+
borrowCap: Infinity.toString(),
|
|
274
|
+
available: market.availableToBorrow.total.toString(),
|
|
275
|
+
availableForBorrow: market.availableToBorrow.total.toString(),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
277
278
|
|
|
278
|
-
|
|
279
|
+
protected async _statsCapAndAvailableOnChain(isGetter: boolean): Promise<{ borrowCap: string, available: string, totalAssets: string, availableForBorrow: string }> {
|
|
280
|
+
const vaultContract = this.llamalend.contracts[this.market.addresses.vault].multicallContract;
|
|
281
|
+
const borrowedContract = this.llamalend.contracts[this.market.addresses.borrowed_token].multicallContract;
|
|
279
282
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
} else {
|
|
286
|
-
throw new Error('Market not found in API')
|
|
287
|
-
}
|
|
283
|
+
let _cap, _available;
|
|
284
|
+
if(isGetter) {
|
|
285
|
+
_cap = cacheStats.get(cacheKey(this.market.addresses.vault, 'totalAssets', this.market.addresses.controller));
|
|
286
|
+
_available = cacheStats.get(cacheKey(this.market.addresses.borrowed_token, 'balanceOf', this.market.addresses.controller));
|
|
288
287
|
} else {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
} else {
|
|
297
|
-
[_cap, _available] =await this.llamalend.multicallProvider.all([
|
|
298
|
-
vaultContract.totalAssets(this.market.addresses.controller),
|
|
299
|
-
borrowedContract.balanceOf(this.market.addresses.controller),
|
|
300
|
-
]);
|
|
301
|
-
cacheStats.set(cacheKey(this.market.addresses.vault, 'totalAssets', this.market.addresses.controller), _cap);
|
|
302
|
-
cacheStats.set(cacheKey(this.market.addresses.borrowed_token, 'balanceOf', this.market.addresses.controller), _available);
|
|
303
|
-
}
|
|
288
|
+
[_cap, _available] = await this.llamalend.multicallProvider.all([
|
|
289
|
+
vaultContract.totalAssets(this.market.addresses.controller),
|
|
290
|
+
borrowedContract.balanceOf(this.market.addresses.controller),
|
|
291
|
+
]);
|
|
292
|
+
cacheStats.set(cacheKey(this.market.addresses.vault, 'totalAssets', this.market.addresses.controller), _cap);
|
|
293
|
+
cacheStats.set(cacheKey(this.market.addresses.borrowed_token, 'balanceOf', this.market.addresses.controller), _available);
|
|
294
|
+
}
|
|
304
295
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
296
|
+
const available = this.llamalend.formatUnits(_available, this.market.borrowed_token.decimals);
|
|
297
|
+
return {
|
|
298
|
+
totalAssets: this.llamalend.formatUnits(_cap, this.market.borrowed_token.decimals),
|
|
299
|
+
borrowCap: Infinity.toString(),
|
|
300
|
+
available,
|
|
301
|
+
availableForBorrow: available,
|
|
311
302
|
}
|
|
312
303
|
}
|
|
304
|
+
|
|
305
|
+
public async statsCapAndAvailable(isGetter = true, useAPI = false): Promise<{ borrowCap: string, available: string, totalAssets: string, availableForBorrow: string }> {
|
|
306
|
+
if(useAPI) return this._statsCapAndAvailableFromAPI();
|
|
307
|
+
return this._statsCapAndAvailableOnChain(isGetter);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
public statsAdminPercentage = async (): Promise<string> => {
|
|
311
|
+
const _adminPercentage = await this._fetchAdminPercentage();
|
|
312
|
+
return formatUnits(_adminPercentage * BigInt(100));
|
|
313
|
+
}
|
|
313
314
|
}
|
|
@@ -289,10 +289,10 @@ export class VaultModule {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
public async vaultTotalLiquidity(useAPI = true): Promise<string> {
|
|
292
|
-
const {
|
|
292
|
+
const { totalAssets } = await this.market.stats.capAndAvailable(true, useAPI);
|
|
293
293
|
const price = await _getUsdRate.call(this.llamalend, this.market.addresses.borrowed_token);
|
|
294
294
|
|
|
295
|
-
return BN(
|
|
295
|
+
return BN(totalAssets).times(price).toFixed(6)
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
private _calcCrvApr = async (futureWorkingSupplyBN: BigNumber | null = null): Promise<[baseApy: number, boostedApy: number]> => {
|
|
@@ -1,3 +1,61 @@
|
|
|
1
|
+
import memoize from "memoizee";
|
|
1
2
|
import { StatsBaseModule } from "../common/statsBase.js";
|
|
3
|
+
import {cacheKey, cacheStats} from "../../../cache";
|
|
4
|
+
import BigNumber from "bignumber.js";
|
|
5
|
+
import {BN} from "../../../utils";
|
|
2
6
|
|
|
3
|
-
export class StatsV2Module extends StatsBaseModule {
|
|
7
|
+
export class StatsV2Module extends StatsBaseModule {
|
|
8
|
+
protected _fetchAdminPercentage = memoize(async (): Promise<bigint> => {
|
|
9
|
+
return await this.llamalend.contracts[this.market.addresses.controller].contract.admin_percentage(this.llamalend.constantOptions);
|
|
10
|
+
}, {
|
|
11
|
+
promise: true,
|
|
12
|
+
maxAge: 30 * 60 * 1000, // 30m
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
protected _fetchAdminFee = async (): Promise<bigint> => BigInt(0);
|
|
16
|
+
|
|
17
|
+
protected _getAdminFeesXY = async (): Promise<[bigint, bigint]> => [BigInt(0), BigInt(0)];
|
|
18
|
+
|
|
19
|
+
protected async _statsCapAndAvailableFromAPI() {
|
|
20
|
+
const market = await this._fetchMarketDataFromAPI();
|
|
21
|
+
return {
|
|
22
|
+
totalAssets: market.totalSupplied.total.toString(),
|
|
23
|
+
borrowCap: market.borrowCap.total.toString(),
|
|
24
|
+
available: market.availableToBorrow.total.toString(),
|
|
25
|
+
availableForBorrow: market.availableToBorrow.total.toString(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected async _statsCapAndAvailableOnChain(isGetter: boolean): Promise<{ borrowCap: string, available: string, totalAssets: string, availableForBorrow: string }> {
|
|
30
|
+
const vaultContract = this.llamalend.contracts[this.market.addresses.vault].multicallContract;
|
|
31
|
+
const controllerContract = this.llamalend.contracts[this.market.addresses.controller].multicallContract;
|
|
32
|
+
|
|
33
|
+
let _cap: bigint, _available: bigint, _totalAssets: bigint, _totalDebt: bigint;
|
|
34
|
+
if(isGetter) {
|
|
35
|
+
_totalAssets = cacheStats.get(cacheKey(this.market.addresses.vault, 'totalAssets', this.market.addresses.controller));
|
|
36
|
+
_cap = cacheStats.get(cacheKey(this.market.addresses.controller, 'borrow_cap'));
|
|
37
|
+
_available = cacheStats.get(cacheKey(this.market.addresses.controller, 'available_balance'));
|
|
38
|
+
_totalDebt = cacheStats.get(cacheKey(this.market.addresses.controller, 'total_debt'));
|
|
39
|
+
} else {
|
|
40
|
+
[_totalAssets, _available, _cap, _totalDebt] = await this.llamalend.multicallProvider.all([
|
|
41
|
+
vaultContract.totalAssets(this.market.addresses.controller),
|
|
42
|
+
controllerContract.available_balance(),
|
|
43
|
+
controllerContract.borrow_cap(),
|
|
44
|
+
controllerContract.total_debt(),
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
cacheStats.set(cacheKey(this.market.addresses.vault, 'totalAssets', this.market.addresses.controller), _totalAssets);
|
|
48
|
+
cacheStats.set(cacheKey(this.market.addresses.controller, 'borrow_cap'), _cap);
|
|
49
|
+
cacheStats.set(cacheKey(this.market.addresses.controller, 'available_balance'), _available);
|
|
50
|
+
cacheStats.set(cacheKey(this.market.addresses.controller, 'total_debt'), _totalDebt);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const totalAssets = this.llamalend.formatUnits(_totalAssets, this.market.borrowed_token.decimals);
|
|
54
|
+
const borrowCap = this.llamalend.formatUnits(_cap, this.market.borrowed_token.decimals);
|
|
55
|
+
const available = this.llamalend.formatUnits(_available, this.market.borrowed_token.decimals);
|
|
56
|
+
const totalDebt = this.llamalend.formatUnits(_totalDebt, this.market.borrowed_token.decimals);
|
|
57
|
+
const availableForBorrow = BigNumber.min(BN(available), BN(borrowCap).minus(BN(totalDebt))).toFixed();
|
|
58
|
+
|
|
59
|
+
return { totalAssets, borrowCap, available, availableForBorrow }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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 lendAprRaw = annualFactor.times(debt).div(cap).times(100);
|
|
28
|
+
const lendApr = lendAprRaw.isNaN() ? "0" : lendAprRaw.toString();
|
|
29
|
+
const lendApyRaw = BN(debt).times(expFactor).minus(debt).div(cap).times(100);
|
|
30
|
+
const lendApy = lendApyRaw.isNaN() ? "0" : lendApyRaw.toString();
|
|
31
|
+
|
|
32
|
+
return { borrowApr, lendApr, borrowApy, lendApy };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const fetchMarketDataByVault = async (
|
|
36
|
+
networkName: INetworkName,
|
|
37
|
+
vaultAddress: string,
|
|
38
|
+
getData: (network: INetworkName) => Promise<IMarketData>
|
|
39
|
+
): Promise<IMarketDataAPI> => {
|
|
40
|
+
const response = await getData(networkName);
|
|
41
|
+
const market = response.lendingVaultData.find(
|
|
42
|
+
(item) => item.address.toLowerCase() === vaultAddress.toLowerCase()
|
|
43
|
+
);
|
|
44
|
+
if (!market) throw new Error("Market not found in API");
|
|
45
|
+
return market;
|
|
46
|
+
}
|