@curvefi/llamalend-api 2.0.2 → 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/constants/abis/ControllerV2.json +1891 -0
- package/lib/index.d.ts +2 -2
- package/lib/lendMarkets/LendMarketTemplate.d.ts +9 -5
- package/lib/lendMarkets/LendMarketTemplate.js +12 -11
- package/lib/lendMarkets/fetch/fetchLendMarkets.js +6 -1
- package/lib/lendMarkets/interfaces/v1/statsV1.d.ts +1 -0
- package/lib/lendMarkets/interfaces/v2/loanV2.d.ts +1 -1
- package/lib/lendMarkets/interfaces/v2/statsV2.d.ts +1 -0
- package/lib/lendMarkets/lendMarketConstructor.d.ts +1 -1
- package/lib/lendMarkets/lendMarketConstructor.js +6 -1
- package/lib/lendMarkets/modules/common/loanBase.d.ts +0 -1
- package/lib/lendMarkets/modules/common/loanBase.js +8 -23
- package/lib/lendMarkets/modules/common/statsBase.d.ts +4 -2
- package/lib/lendMarkets/modules/common/statsBase.js +56 -85
- package/lib/lendMarkets/modules/v1/loanV1.d.ts +1 -0
- package/lib/lendMarkets/modules/v1/loanV1.js +19 -0
- package/lib/lendMarkets/modules/v2/loanV2.d.ts +5 -0
- package/lib/lendMarkets/modules/v2/loanV2.js +57 -0
- 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/lib/llamalend.d.ts +1 -1
- package/package.json +9 -3
- package/src/constants/abis/ControllerV2.json +1891 -0
- package/src/lendMarkets/LendMarketTemplate.ts +24 -19
- package/src/lendMarkets/fetch/fetchLendMarkets.ts +7 -1
- package/src/lendMarkets/interfaces/v1/statsV1.ts +1 -0
- package/src/lendMarkets/interfaces/v2/loanV2.ts +1 -1
- package/src/lendMarkets/interfaces/v2/statsV2.ts +1 -0
- package/src/lendMarkets/lendMarketConstructor.ts +6 -2
- package/src/lendMarkets/modules/common/loanBase.ts +9 -24
- package/src/lendMarkets/modules/common/statsBase.ts +74 -92
- package/src/lendMarkets/modules/v1/loanV1.ts +12 -1
- package/src/lendMarkets/modules/v2/loanV2.ts +55 -1
- package/src/lendMarkets/modules/v2/statsV2.ts +9 -1
- package/src/lendMarkets/utils.ts +44 -0
- package/src/llamalend.ts +1 -1
|
@@ -24,6 +24,10 @@ import {
|
|
|
24
24
|
VaultModule,
|
|
25
25
|
} from "./modules/common";
|
|
26
26
|
|
|
27
|
+
type LoanForVersion<V extends 'v1' | 'v2'> = V extends 'v1' ? ILoanV1 : ILoanV2;
|
|
28
|
+
type StatsForVersion<V extends 'v1' | 'v2'> = V extends 'v1' ? IStatsV1 : IStatsV2;
|
|
29
|
+
type LeverageForVersion<V extends 'v1' | 'v2'> = V extends 'v1' ? ILeverageV1 : ILeverageV2;
|
|
30
|
+
|
|
27
31
|
type V1ModuleConstructors = {
|
|
28
32
|
UserPosition: typeof UserPositionModule;
|
|
29
33
|
Stats: typeof StatsV1Module;
|
|
@@ -73,11 +77,11 @@ const versionModules: { v1: V1ModuleConstructors; v2: V2ModuleConstructors } = {
|
|
|
73
77
|
},
|
|
74
78
|
};
|
|
75
79
|
|
|
76
|
-
export class LendMarketTemplate {
|
|
80
|
+
export class LendMarketTemplate<V extends 'v1' | 'v2' = 'v1' | 'v2'> {
|
|
77
81
|
private llamalend: Llamalend;
|
|
78
82
|
id: string;
|
|
79
83
|
name: string;
|
|
80
|
-
version:
|
|
84
|
+
version: V;
|
|
81
85
|
addresses: {
|
|
82
86
|
amm: string,
|
|
83
87
|
controller: string,
|
|
@@ -111,14 +115,14 @@ export class LendMarketTemplate {
|
|
|
111
115
|
prices: IPrices;
|
|
112
116
|
amm: IAmm;
|
|
113
117
|
vault: IVault;
|
|
114
|
-
stats:
|
|
115
|
-
loan:
|
|
116
|
-
leverage:
|
|
118
|
+
stats: StatsForVersion<V>;
|
|
119
|
+
loan: LoanForVersion<V>;
|
|
120
|
+
leverage: LeverageForVersion<V>;
|
|
117
121
|
leverageZapV2: ILeverageZapV2;
|
|
118
122
|
|
|
119
123
|
constructor(id: string, marketData: IOneWayMarket, llamalend: Llamalend) {
|
|
120
124
|
this.llamalend = llamalend;
|
|
121
|
-
this.version = marketData.version || 'v1';
|
|
125
|
+
this.version = (marketData.version || 'v1') as V;
|
|
122
126
|
this.id = id;
|
|
123
127
|
this.name = marketData.name;
|
|
124
128
|
this.addresses = marketData.addresses;
|
|
@@ -162,17 +166,18 @@ export class LendMarketTemplate {
|
|
|
162
166
|
}
|
|
163
167
|
|
|
164
168
|
this.stats = {
|
|
165
|
-
parameters: stats.statsParameters.bind(
|
|
166
|
-
rates: stats.statsRates.bind(
|
|
167
|
-
futureRates: stats.statsFutureRates.bind(
|
|
168
|
-
balances: stats.statsBalances.bind(
|
|
169
|
-
bandsInfo: stats.statsBandsInfo.bind(
|
|
170
|
-
bandBalances: stats.statsBandBalances.bind(
|
|
171
|
-
bandsBalances: stats.statsBandsBalances.bind(
|
|
172
|
-
totalDebt: stats.statsTotalDebt.bind(
|
|
173
|
-
ammBalances: stats.statsAmmBalances.bind(
|
|
174
|
-
capAndAvailable: stats.statsCapAndAvailable.bind(
|
|
175
|
-
|
|
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),
|
|
180
|
+
} as StatsForVersion<V>
|
|
176
181
|
|
|
177
182
|
this.wallet = {
|
|
178
183
|
balances: wallet.balances.bind(this),
|
|
@@ -285,7 +290,7 @@ export class LendMarketTemplate {
|
|
|
285
290
|
partialSelfLiquidateApprove: loan.estimateGas.partialSelfLiquidateApprove,
|
|
286
291
|
partialSelfLiquidate: loan.partialSelfLiquidateEstimateGas.bind(loan),
|
|
287
292
|
},
|
|
288
|
-
}
|
|
293
|
+
} as LoanForVersion<V>
|
|
289
294
|
|
|
290
295
|
this.vault = {
|
|
291
296
|
maxDeposit: vault.vaultMaxDeposit.bind(vault),
|
|
@@ -389,7 +394,7 @@ export class LendMarketTemplate {
|
|
|
389
394
|
repayApprove: leverageZapV1.leverageRepayApproveEstimateGas.bind(leverageZapV1),
|
|
390
395
|
repay: leverageZapV1.leverageRepayEstimateGas.bind(leverageZapV1),
|
|
391
396
|
},
|
|
392
|
-
}
|
|
397
|
+
} as LeverageForVersion<V>
|
|
393
398
|
|
|
394
399
|
this.leverageZapV2 = {
|
|
395
400
|
hasLeverage: leverageZapV2.hasLeverage.bind(leverageZapV2),
|
|
@@ -4,11 +4,17 @@ import { getFactoryMarketDataV1, getFactoryMarketDataV2, getFactoryMarketDataByA
|
|
|
4
4
|
|
|
5
5
|
import LlammaABI from '../../constants/abis/Llamma.json' with {type: 'json'};
|
|
6
6
|
import ControllerABI from '../../constants/abis/Controller.json' with {type: 'json'};
|
|
7
|
+
import ControllerV2ABI from '../../constants/abis/ControllerV2.json' with {type: 'json'};
|
|
7
8
|
import MonetaryPolicyABI from '../../constants/abis/MonetaryPolicy.json' with {type: 'json'};
|
|
8
9
|
import VaultABI from '../../constants/abis/Vault.json' with {type: 'json'};
|
|
9
10
|
import GaugeABI from '../../constants/abis/GaugeV5.json' with {type: 'json'};
|
|
10
11
|
import SidechainGaugeABI from '../../constants/abis/SidechainGauge.json' with {type: 'json'};
|
|
11
12
|
|
|
13
|
+
const controllerAbiMap = {
|
|
14
|
+
'v1' : ControllerABI,
|
|
15
|
+
'v2' : ControllerV2ABI,
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
const registerMarkets = (
|
|
13
19
|
llamalend: Llamalend,
|
|
14
20
|
names: string[],
|
|
@@ -24,7 +30,7 @@ const registerMarkets = (
|
|
|
24
30
|
) => {
|
|
25
31
|
amms.forEach((amm: string, index: number) => {
|
|
26
32
|
llamalend.setContract(amms[index], LlammaABI);
|
|
27
|
-
llamalend.setContract(controllers[index],
|
|
33
|
+
llamalend.setContract(controllers[index], controllerAbiMap[version]);
|
|
28
34
|
llamalend.setContract(monetary_policies[index], MonetaryPolicyABI);
|
|
29
35
|
llamalend.setContract(vaults[index], VaultABI);
|
|
30
36
|
if (gauges[index]) {
|
|
@@ -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
|
}
|
|
@@ -41,7 +41,7 @@ export interface ILoanV2 {
|
|
|
41
41
|
repayPrices: (debt: TAmount, address?: string) => Promise<string[]>;
|
|
42
42
|
repayIsApproved: (debt: TAmount) => Promise<boolean>;
|
|
43
43
|
repayApprove: (debt: TAmount) => Promise<string[]>;
|
|
44
|
-
repayHealth: (debt: TAmount, full?: boolean, address?: string) => Promise<string>;
|
|
44
|
+
repayHealth: (debt: TAmount, shrink?: boolean, full?: boolean, address?: string) => Promise<string>;
|
|
45
45
|
repay: (debt: TAmount, address?: string) => Promise<string>;
|
|
46
46
|
repayFutureLeverage: (debt: TAmount, userAddress?: string) => Promise<string>;
|
|
47
47
|
|
|
@@ -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
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { LendMarketTemplate} from "./LendMarketTemplate.js";
|
|
2
2
|
import type { Llamalend } from "../llamalend.js";
|
|
3
3
|
|
|
4
|
-
export const getLendMarket = function (this: Llamalend, lendMarketId: string): LendMarketTemplate {
|
|
4
|
+
export const getLendMarket = function (this: Llamalend, lendMarketId: string): LendMarketTemplate<'v1'> | LendMarketTemplate<'v2'> {
|
|
5
5
|
if (!(lendMarketId in this.lendMarkets)) {
|
|
6
6
|
const marketData = this.constants.ONE_WAY_MARKETS[lendMarketId] || this.constants.ONE_WAY_MARKETS_V2[lendMarketId];
|
|
7
7
|
if (!marketData) throw new Error(`Lend market with id ${lendMarketId} not found`);
|
|
8
|
-
|
|
8
|
+
if (marketData.version === 'v2') {
|
|
9
|
+
this.lendMarkets[lendMarketId] = new LendMarketTemplate<'v2'>(lendMarketId, marketData, this);
|
|
10
|
+
} else {
|
|
11
|
+
this.lendMarkets[lendMarketId] = new LendMarketTemplate<'v1'>(lendMarketId, marketData, this);
|
|
12
|
+
}
|
|
9
13
|
}
|
|
10
14
|
return this.lendMarkets[lendMarketId];
|
|
11
15
|
}
|
|
@@ -155,11 +155,10 @@ export class LoanBaseModule {
|
|
|
155
155
|
const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
|
|
156
156
|
|
|
157
157
|
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
158
|
-
// TODO: verify parameters
|
|
159
|
-
let _health = await contract.health_calculator(this.llamalend.constants.ZERO_ADDRESS, _collateral, _debt, full, range, this.llamalend.constantOptions) as bigint;
|
|
160
|
-
_health = _health * BigInt(100);
|
|
161
158
|
|
|
162
|
-
|
|
159
|
+
const _health = await contract.health_calculator(this.llamalend.constants.ZERO_ADDRESS, _collateral, _debt, full, range, this.llamalend.constantOptions) as bigint;
|
|
160
|
+
|
|
161
|
+
return formatUnits(_health * BigInt(100));
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
public async createLoanIsApproved(collateral: number | string): Promise<boolean> {
|
|
@@ -243,10 +242,9 @@ export class LoanBaseModule {
|
|
|
243
242
|
const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
|
|
244
243
|
|
|
245
244
|
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
246
|
-
|
|
247
|
-
_health = _health * BigInt(100);
|
|
245
|
+
const _health = await contract.health_calculator(address, _collateral, _debt, full, 0, this.llamalend.constantOptions) as bigint;
|
|
248
246
|
|
|
249
|
-
return formatUnits(_health);
|
|
247
|
+
return formatUnits(_health * BigInt(100));
|
|
250
248
|
}
|
|
251
249
|
|
|
252
250
|
public async borrowMoreIsApproved(collateral: number | string): Promise<boolean> {
|
|
@@ -330,10 +328,9 @@ export class LoanBaseModule {
|
|
|
330
328
|
const _collateral = parseUnits(collateral, this.market.collateral_token.decimals);
|
|
331
329
|
|
|
332
330
|
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
333
|
-
|
|
334
|
-
_health = _health * BigInt(100);
|
|
331
|
+
const _health = await contract.health_calculator(address, _collateral, 0, full, 0, this.llamalend.constantOptions) as bigint;
|
|
335
332
|
|
|
336
|
-
return formatUnits(_health);
|
|
333
|
+
return formatUnits(_health * BigInt(100));
|
|
337
334
|
}
|
|
338
335
|
|
|
339
336
|
public async addCollateralIsApproved(collateral: number | string): Promise<boolean> {
|
|
@@ -424,10 +421,9 @@ export class LoanBaseModule {
|
|
|
424
421
|
const _collateral = parseUnits(collateral, this.market.collateral_token.decimals) * BigInt(-1);
|
|
425
422
|
|
|
426
423
|
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
427
|
-
|
|
428
|
-
_health = _health * BigInt(100);
|
|
424
|
+
const _health = await contract.health_calculator(address, _collateral, 0, full, 0, this.llamalend.constantOptions) as bigint;
|
|
429
425
|
|
|
430
|
-
return formatUnits(_health);
|
|
426
|
+
return formatUnits(_health * BigInt(100));
|
|
431
427
|
}
|
|
432
428
|
|
|
433
429
|
private async _removeCollateral(collateral: number | string, estimateGas: boolean): Promise<string | TGas> {
|
|
@@ -505,17 +501,6 @@ export class LoanBaseModule {
|
|
|
505
501
|
return await ensureAllowance.call(this.llamalend, [this.market.borrowed_token.address], [debt], this.market.addresses.controller);
|
|
506
502
|
}
|
|
507
503
|
|
|
508
|
-
public async repayHealth(debt: number | string, full = true, address = ""): Promise<string> {
|
|
509
|
-
address = _getAddress.call(this.llamalend, address);
|
|
510
|
-
const _debt = parseUnits(debt) * BigInt(-1);
|
|
511
|
-
|
|
512
|
-
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
513
|
-
let _health = await contract.health_calculator(address, 0, _debt, full, 0, this.llamalend.constantOptions) as bigint;
|
|
514
|
-
_health = _health * BigInt(100);
|
|
515
|
-
|
|
516
|
-
return formatUnits(_health);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
504
|
private async _repay(debt: number | string, address: string, estimateGas: boolean): Promise<string | TGas> {
|
|
520
505
|
address = _getAddress.call(this.llamalend, address);
|
|
521
506
|
const { debt: currentDebt } = await this.market.userPosition.userState(address);
|
|
@@ -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,4 +1,15 @@
|
|
|
1
1
|
import { LoanBaseModule } from "../common/loanBase.js";
|
|
2
2
|
import { ILoanV1 } from "../../interfaces/v1/loanV1";
|
|
3
|
+
import {_getAddress, formatUnits, parseUnits} from "../../../utils";
|
|
3
4
|
|
|
4
|
-
export class LoanV1Module extends LoanBaseModule implements ILoanV1 {
|
|
5
|
+
export class LoanV1Module extends LoanBaseModule implements ILoanV1 {
|
|
6
|
+
public async repayHealth(debt: number | string, full = true, address = ""): Promise<string> {
|
|
7
|
+
address = _getAddress.call(this.llamalend, address);
|
|
8
|
+
const _debt = parseUnits(debt) * BigInt(-1);
|
|
9
|
+
|
|
10
|
+
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
11
|
+
const _health = await contract.health_calculator(address, 0, _debt, full, 0, this.llamalend.constantOptions) as bigint;
|
|
12
|
+
|
|
13
|
+
return formatUnits(_health * BigInt(100));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -1,4 +1,58 @@
|
|
|
1
1
|
import { LoanBaseModule } from "../common/loanBase.js";
|
|
2
2
|
import { ILoanV2 } from "../../interfaces/v2";
|
|
3
|
+
import {_getAddress, formatUnits, parseUnits} from "../../../utils";
|
|
3
4
|
|
|
4
|
-
export class LoanV2Module extends LoanBaseModule implements ILoanV2 {
|
|
5
|
+
export class LoanV2Module extends LoanBaseModule implements ILoanV2 {
|
|
6
|
+
public async createLoanHealth(collateral: number | string, debt: number | string, range: number, full = true): Promise<string> {
|
|
7
|
+
const _collateral = parseUnits(collateral, this.market.collateral_token.decimals);
|
|
8
|
+
const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
|
|
9
|
+
const address = _getAddress.call(this.llamalend, '');
|
|
10
|
+
|
|
11
|
+
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
12
|
+
|
|
13
|
+
const _health = await contract.create_loan_health_preview(_collateral, _debt, range, address, full, this.llamalend.constantOptions) as bigint;
|
|
14
|
+
|
|
15
|
+
return formatUnits(_health * BigInt(100));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public async addCollateralHealth(collateral: number | string, full = true, address = ""): Promise<string> {
|
|
19
|
+
address = _getAddress.call(this.llamalend, address);
|
|
20
|
+
const _collateral = parseUnits(collateral, this.market.collateral_token.decimals);
|
|
21
|
+
|
|
22
|
+
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
23
|
+
const _health = await contract.add_collateral_health_preview(_collateral, address, address, full, this.llamalend.constantOptions) as bigint;
|
|
24
|
+
|
|
25
|
+
return formatUnits(_health * BigInt(100));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async removeCollateralHealth(collateral: number | string, full = true, address = ""): Promise<string> {
|
|
29
|
+
address = _getAddress.call(this.llamalend, address);
|
|
30
|
+
const _collateral = parseUnits(collateral, this.market.collateral_token.decimals);
|
|
31
|
+
|
|
32
|
+
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
33
|
+
const _health = await contract.remove_collateral_health_preview(_collateral, address, full, this.llamalend.constantOptions) as bigint;
|
|
34
|
+
|
|
35
|
+
return formatUnits(_health * BigInt(100));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public async borrowMoreHealth(collateral: number | string, debt: number | string, full = true, address = ""): Promise<string> {
|
|
39
|
+
address = _getAddress.call(this.llamalend, address);
|
|
40
|
+
const _collateral = parseUnits(collateral, this.market.collateral_token.decimals);
|
|
41
|
+
const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
|
|
42
|
+
|
|
43
|
+
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
44
|
+
const _health = await contract.borrow_more_health_preview(_collateral, _debt, address, full, this.llamalend.constantOptions) as bigint;
|
|
45
|
+
|
|
46
|
+
return formatUnits(_health * BigInt(100));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public async repayHealth(debt: number | string, shrink = false, full = true, address = ""): Promise<string> {
|
|
50
|
+
address = _getAddress.call(this.llamalend, address);
|
|
51
|
+
const _debt = parseUnits(debt);
|
|
52
|
+
|
|
53
|
+
const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
|
|
54
|
+
const _health = await contract.repay_health_preview(0, _debt, address, address, shrink, full, this.llamalend.constantOptions) as bigint;
|
|
55
|
+
|
|
56
|
+
return formatUnits(_health * BigInt(100));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -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
|
+
}
|
package/src/llamalend.ts
CHANGED
|
@@ -99,7 +99,7 @@ class Llamalend implements ILlamalend {
|
|
|
99
99
|
chainId: IChainId;
|
|
100
100
|
contracts: { [index: string]: ICurveContract };
|
|
101
101
|
mintMarkets: { [addres: string]: MintMarketTemplate };
|
|
102
|
-
lendMarkets: { [
|
|
102
|
+
lendMarkets: { [address: string]: LendMarketTemplate<'v1'> | LendMarketTemplate<'v2'> };
|
|
103
103
|
feeData: { gasPrice?: number, maxFeePerGas?: number, maxPriorityFeePerGas?: number };
|
|
104
104
|
constantOptions: { gasLimit: number };
|
|
105
105
|
options: { gasPrice?: number | bigint, maxFeePerGas?: number | bigint, maxPriorityFeePerGas?: number | bigint };
|