@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.
Files changed (39) hide show
  1. package/docs/SUPPORT_LLv2.md +6 -0
  2. package/lib/constants/abis/ControllerV2.json +1891 -0
  3. package/lib/index.d.ts +2 -2
  4. package/lib/lendMarkets/LendMarketTemplate.d.ts +9 -5
  5. package/lib/lendMarkets/LendMarketTemplate.js +12 -11
  6. package/lib/lendMarkets/fetch/fetchLendMarkets.js +6 -1
  7. package/lib/lendMarkets/interfaces/v1/statsV1.d.ts +1 -0
  8. package/lib/lendMarkets/interfaces/v2/loanV2.d.ts +1 -1
  9. package/lib/lendMarkets/interfaces/v2/statsV2.d.ts +1 -0
  10. package/lib/lendMarkets/lendMarketConstructor.d.ts +1 -1
  11. package/lib/lendMarkets/lendMarketConstructor.js +6 -1
  12. package/lib/lendMarkets/modules/common/loanBase.d.ts +0 -1
  13. package/lib/lendMarkets/modules/common/loanBase.js +8 -23
  14. package/lib/lendMarkets/modules/common/statsBase.d.ts +4 -2
  15. package/lib/lendMarkets/modules/common/statsBase.js +56 -85
  16. package/lib/lendMarkets/modules/v1/loanV1.d.ts +1 -0
  17. package/lib/lendMarkets/modules/v1/loanV1.js +19 -0
  18. package/lib/lendMarkets/modules/v2/loanV2.d.ts +5 -0
  19. package/lib/lendMarkets/modules/v2/loanV2.js +57 -0
  20. package/lib/lendMarkets/modules/v2/statsV2.d.ts +2 -0
  21. package/lib/lendMarkets/modules/v2/statsV2.js +19 -0
  22. package/lib/lendMarkets/utils.d.ts +14 -0
  23. package/lib/lendMarkets/utils.js +31 -0
  24. package/lib/llamalend.d.ts +1 -1
  25. package/package.json +9 -3
  26. package/src/constants/abis/ControllerV2.json +1891 -0
  27. package/src/lendMarkets/LendMarketTemplate.ts +24 -19
  28. package/src/lendMarkets/fetch/fetchLendMarkets.ts +7 -1
  29. package/src/lendMarkets/interfaces/v1/statsV1.ts +1 -0
  30. package/src/lendMarkets/interfaces/v2/loanV2.ts +1 -1
  31. package/src/lendMarkets/interfaces/v2/statsV2.ts +1 -0
  32. package/src/lendMarkets/lendMarketConstructor.ts +6 -2
  33. package/src/lendMarkets/modules/common/loanBase.ts +9 -24
  34. package/src/lendMarkets/modules/common/statsBase.ts +74 -92
  35. package/src/lendMarkets/modules/v1/loanV1.ts +12 -1
  36. package/src/lendMarkets/modules/v2/loanV2.ts +55 -1
  37. package/src/lendMarkets/modules/v2/statsV2.ts +9 -1
  38. package/src/lendMarkets/utils.ts +44 -0
  39. 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: 'v1' | 'v2';
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: IStatsV1 | IStatsV2;
115
- loan: ILoanV1 | ILoanV2;
116
- leverage: ILeverageV1 | ILeverageV2;
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(this),
166
- rates: stats.statsRates.bind(this),
167
- futureRates: stats.statsFutureRates.bind(this),
168
- balances: stats.statsBalances.bind(this),
169
- bandsInfo: stats.statsBandsInfo.bind(this),
170
- bandBalances: stats.statsBandBalances.bind(this),
171
- bandsBalances: stats.statsBandsBalances.bind(this),
172
- totalDebt: stats.statsTotalDebt.bind(this),
173
- ammBalances: stats.statsAmmBalances.bind(this),
174
- capAndAvailable: stats.statsCapAndAvailable.bind(this),
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], ControllerABI);
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
- this.lendMarkets[lendMarketId] = new LendMarketTemplate(lendMarketId, marketData, this);
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
- return formatUnits(_health);
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
- let _health = await contract.health_calculator(address, _collateral, _debt, full, 0, this.llamalend.constantOptions) as bigint;
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
- let _health = await contract.health_calculator(address, _collateral, 0, full, 0, this.llamalend.constantOptions) as bigint;
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
- let _health = await contract.health_calculator(address, _collateral, 0, full, 0, this.llamalend.constantOptions) as bigint;
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 response = await _getMarketsData(this.llamalend.constants.NETWORK_NAME);
75
-
76
- const market = response.lendingVaultData.find((item) => item.address.toLowerCase() === this.market.addresses.vault.toLowerCase())
77
-
78
- if(market) {
79
- return {
80
- borrowApr: (market.rates.borrowApr * 100).toString(),
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
- }
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
- 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 }
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
- if (Number(debt) > 0) {
119
- const cap = Number((await this.statsCapAndAvailable(true, useAPI)).cap) + Number(dReserves);
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 response = await _getMarketsData(this.llamalend.constants.NETWORK_NAME);
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
- }
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 response = await _getMarketsData(this.llamalend.constants.NETWORK_NAME);
230
-
231
- const market = response.lendingVaultData.find((item) => item.address.toLowerCase() === this.market.addresses.vault.toLowerCase())
232
-
233
- if(market) {
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
- }
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 response = await _getMarketsData(this.llamalend.constants.NETWORK_NAME);
277
-
278
- const market = response.lendingVaultData.find((item) => item.address.toLowerCase() === this.market.addresses.vault.toLowerCase())
279
-
280
- if(market) {
281
- return {
282
- cap: market.totalSupplied.total.toString(),
283
- available: market.availableToBorrow.total.toString(),
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: { [addres: string]: LendMarketTemplate };
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 };