@curvefi/llamalend-api 1.1.9 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/docs/MIGRATION.md +325 -0
  2. package/docs/SUPPORT_LLv2.md +409 -0
  3. package/lib/constants/abis/OneWayLendingFactoryV2ABI.json +543 -0
  4. package/lib/constants/aliases.d.ts +0 -11
  5. package/lib/constants/aliases.js +1 -86
  6. package/lib/constants/coins.d.ts +0 -11
  7. package/lib/constants/coins.js +0 -14
  8. package/lib/constants/utils.d.ts +2 -0
  9. package/lib/constants/utils.js +2 -0
  10. package/lib/index.d.ts +10 -4
  11. package/lib/interfaces.d.ts +3 -2
  12. package/lib/lendMarkets/LendMarketTemplate.d.ts +13 -501
  13. package/lib/lendMarkets/LendMarketTemplate.js +237 -2854
  14. package/lib/lendMarkets/fetch/fetchFactoryData.d.ts +13 -0
  15. package/lib/lendMarkets/fetch/fetchFactoryData.js +101 -0
  16. package/lib/lendMarkets/fetch/fetchLendMarkets.d.ts +3 -0
  17. package/lib/lendMarkets/fetch/fetchLendMarkets.js +94 -0
  18. package/lib/lendMarkets/interfaces/common/amm.d.ts +10 -0
  19. package/lib/lendMarkets/interfaces/common/amm.js +1 -0
  20. package/lib/lendMarkets/interfaces/common/index.d.ts +5 -0
  21. package/lib/lendMarkets/interfaces/common/index.js +5 -0
  22. package/lib/lendMarkets/interfaces/common/prices.d.ts +13 -0
  23. package/lib/lendMarkets/interfaces/common/prices.js +1 -0
  24. package/lib/lendMarkets/interfaces/common/userPosition.d.ts +39 -0
  25. package/lib/lendMarkets/interfaces/common/userPosition.js +1 -0
  26. package/lib/lendMarkets/interfaces/common/vault.d.ts +55 -0
  27. package/lib/lendMarkets/interfaces/common/vault.js +1 -0
  28. package/lib/lendMarkets/interfaces/common/wallet.d.ts +8 -0
  29. package/lib/lendMarkets/interfaces/common/wallet.js +1 -0
  30. package/lib/lendMarkets/interfaces/v1/index.d.ts +4 -0
  31. package/lib/lendMarkets/interfaces/v1/index.js +4 -0
  32. package/lib/lendMarkets/interfaces/v1/leverageV1.d.ts +90 -0
  33. package/lib/lendMarkets/interfaces/v1/leverageV1.js +1 -0
  34. package/lib/lendMarkets/interfaces/v1/loanV1.d.ts +73 -0
  35. package/lib/lendMarkets/interfaces/v1/loanV1.js +1 -0
  36. package/lib/lendMarkets/interfaces/v1/statsV1.d.ts +49 -0
  37. package/lib/lendMarkets/interfaces/v1/statsV1.js +1 -0
  38. package/lib/lendMarkets/interfaces/v2/index.d.ts +3 -0
  39. package/lib/lendMarkets/interfaces/v2/index.js +3 -0
  40. package/lib/lendMarkets/interfaces/v2/leverageV2.d.ts +90 -0
  41. package/lib/lendMarkets/interfaces/v2/leverageV2.js +1 -0
  42. package/lib/lendMarkets/interfaces/v2/loanV2.d.ts +73 -0
  43. package/lib/lendMarkets/interfaces/v2/loanV2.js +1 -0
  44. package/lib/lendMarkets/interfaces/v2/statsV2.d.ts +49 -0
  45. package/lib/lendMarkets/interfaces/v2/statsV2.js +1 -0
  46. package/lib/lendMarkets/lendMarketConstructor.js +1 -1
  47. package/lib/lendMarkets/modules/common/amm.d.ts +19 -0
  48. package/lib/lendMarkets/modules/common/amm.js +137 -0
  49. package/lib/lendMarkets/modules/common/index.d.ts +9 -0
  50. package/lib/lendMarkets/modules/common/index.js +9 -0
  51. package/lib/lendMarkets/modules/common/leverageZapV1Base.d.ts +118 -0
  52. package/lib/lendMarkets/modules/common/leverageZapV1Base.js +770 -0
  53. package/lib/lendMarkets/modules/{leverageZapV2.d.ts → common/leverageZapV2Base.d.ts} +10 -5
  54. package/lib/lendMarkets/modules/{leverageZapV2.js → common/leverageZapV2Base.js} +36 -32
  55. package/lib/lendMarkets/modules/common/loanBase.d.ts +115 -0
  56. package/lib/lendMarkets/modules/common/loanBase.js +793 -0
  57. package/lib/lendMarkets/modules/common/prices.d.ts +19 -0
  58. package/lib/lendMarkets/modules/common/prices.js +104 -0
  59. package/lib/lendMarkets/modules/common/statsBase.d.ts +69 -0
  60. package/lib/lendMarkets/modules/common/statsBase.js +291 -0
  61. package/lib/lendMarkets/modules/common/userPosition.d.ts +46 -0
  62. package/lib/lendMarkets/modules/common/userPosition.js +223 -0
  63. package/lib/lendMarkets/modules/common/vault.d.ts +69 -0
  64. package/lib/lendMarkets/modules/common/vault.js +535 -0
  65. package/lib/lendMarkets/modules/common/wallet.d.ts +13 -0
  66. package/lib/lendMarkets/modules/common/wallet.js +28 -0
  67. package/lib/lendMarkets/modules/index.d.ts +1 -1
  68. package/lib/lendMarkets/modules/index.js +1 -1
  69. package/lib/lendMarkets/modules/v1/index.d.ts +4 -0
  70. package/lib/lendMarkets/modules/v1/index.js +4 -0
  71. package/lib/lendMarkets/modules/v1/leverageV1ZapV1.d.ts +3 -0
  72. package/lib/lendMarkets/modules/v1/leverageV1ZapV1.js +3 -0
  73. package/lib/lendMarkets/modules/v1/leverageV1ZapV2.d.ts +3 -0
  74. package/lib/lendMarkets/modules/v1/leverageV1ZapV2.js +3 -0
  75. package/lib/lendMarkets/modules/v1/loanV1.d.ts +4 -0
  76. package/lib/lendMarkets/modules/v1/loanV1.js +3 -0
  77. package/lib/lendMarkets/modules/v1/statsV1.d.ts +3 -0
  78. package/lib/lendMarkets/modules/v1/statsV1.js +3 -0
  79. package/lib/lendMarkets/modules/v2/index.d.ts +4 -0
  80. package/lib/lendMarkets/modules/v2/index.js +4 -0
  81. package/lib/lendMarkets/modules/v2/leverageV2ZapV1.d.ts +3 -0
  82. package/lib/lendMarkets/modules/v2/leverageV2ZapV1.js +3 -0
  83. package/lib/lendMarkets/modules/v2/leverageV2ZapV2.d.ts +3 -0
  84. package/lib/lendMarkets/modules/v2/leverageV2ZapV2.js +3 -0
  85. package/lib/lendMarkets/modules/v2/loanV2.d.ts +4 -0
  86. package/lib/lendMarkets/modules/v2/loanV2.js +3 -0
  87. package/lib/lendMarkets/modules/v2/statsV2.d.ts +3 -0
  88. package/lib/lendMarkets/modules/v2/statsV2.js +3 -0
  89. package/lib/llamalend.d.ts +6 -6
  90. package/lib/llamalend.js +46 -210
  91. package/lib/utils.js +2 -33
  92. package/package.json +1 -1
  93. package/src/constants/abis/OneWayLendingFactoryV2ABI.json +543 -0
  94. package/src/constants/aliases.ts +1 -97
  95. package/src/constants/coins.ts +0 -25
  96. package/src/constants/utils.ts +4 -0
  97. package/src/interfaces.ts +3 -2
  98. package/src/lendMarkets/LendMarketTemplate.ts +318 -3214
  99. package/src/lendMarkets/fetch/fetchFactoryData.ts +113 -0
  100. package/src/lendMarkets/fetch/fetchLendMarkets.ts +108 -0
  101. package/src/lendMarkets/interfaces/common/amm.ts +11 -0
  102. package/src/lendMarkets/interfaces/common/index.ts +5 -0
  103. package/src/lendMarkets/interfaces/common/prices.ts +13 -0
  104. package/src/lendMarkets/interfaces/common/userPosition.ts +24 -0
  105. package/src/lendMarkets/interfaces/common/vault.ts +48 -0
  106. package/src/lendMarkets/interfaces/common/wallet.ts +3 -0
  107. package/src/lendMarkets/interfaces/v1/index.ts +4 -0
  108. package/src/lendMarkets/interfaces/v1/leverageV1.ts +84 -0
  109. package/src/lendMarkets/interfaces/v1/loanV1.ts +77 -0
  110. package/src/lendMarkets/interfaces/v1/statsV1.ts +21 -0
  111. package/src/lendMarkets/interfaces/v2/index.ts +3 -0
  112. package/src/lendMarkets/interfaces/v2/leverageV2.ts +84 -0
  113. package/src/lendMarkets/interfaces/v2/loanV2.ts +77 -0
  114. package/src/lendMarkets/interfaces/v2/statsV2.ts +21 -0
  115. package/src/lendMarkets/lendMarketConstructor.ts +1 -1
  116. package/src/lendMarkets/modules/common/amm.ts +132 -0
  117. package/src/lendMarkets/modules/common/index.ts +9 -0
  118. package/src/lendMarkets/modules/common/leverageZapV1Base.ts +912 -0
  119. package/src/lendMarkets/modules/{leverageZapV2.ts → common/leverageZapV2Base.ts} +41 -37
  120. package/src/lendMarkets/modules/common/loanBase.ts +773 -0
  121. package/src/lendMarkets/modules/common/prices.ts +111 -0
  122. package/src/lendMarkets/modules/common/statsBase.ts +313 -0
  123. package/src/lendMarkets/modules/common/userPosition.ts +243 -0
  124. package/src/lendMarkets/modules/common/vault.ts +551 -0
  125. package/src/lendMarkets/modules/common/wallet.ts +29 -0
  126. package/src/lendMarkets/modules/index.ts +1 -1
  127. package/src/lendMarkets/modules/v1/index.ts +4 -0
  128. package/src/lendMarkets/modules/v1/leverageV1ZapV1.ts +3 -0
  129. package/src/lendMarkets/modules/v1/leverageV1ZapV2.ts +3 -0
  130. package/src/lendMarkets/modules/v1/loanV1.ts +4 -0
  131. package/src/lendMarkets/modules/v1/statsV1.ts +3 -0
  132. package/src/lendMarkets/modules/v2/index.ts +4 -0
  133. package/src/lendMarkets/modules/v2/leverageV2ZapV1.ts +3 -0
  134. package/src/lendMarkets/modules/v2/leverageV2ZapV2.ts +3 -0
  135. package/src/lendMarkets/modules/v2/loanV2.ts +4 -0
  136. package/src/lendMarkets/modules/v2/statsV2.ts +3 -0
  137. package/src/llamalend.ts +47 -244
  138. package/src/utils.ts +2 -35
@@ -0,0 +1,111 @@
1
+ import memoize from "memoizee";
2
+ import type { LendMarketTemplate } from "../../LendMarketTemplate";
3
+ import {
4
+ BN,
5
+ formatUnits,
6
+ _cutZeros,
7
+ } from "../../../utils";
8
+ import {Llamalend} from "../../../llamalend";
9
+ import BigNumber from "bignumber.js";
10
+ import {IPrices} from "../../interfaces/common";
11
+
12
+
13
+ export class PricesModule implements IPrices {
14
+ private market: LendMarketTemplate;
15
+ private llamalend: Llamalend;
16
+
17
+ constructor(market: LendMarketTemplate) {
18
+ this.market = market;
19
+ this.llamalend = market.getLlamalend();
20
+ }
21
+
22
+ public A = memoize(async(): Promise<string> => {
23
+ const _A = await this.llamalend.contracts[this.market.addresses.amm].contract.A(this.llamalend.constantOptions) as bigint;
24
+ return formatUnits(_A, 0);
25
+ },
26
+ {
27
+ promise: true,
28
+ maxAge: 86400 * 1000, // 1d
29
+ });
30
+
31
+ public basePrice = memoize(async(): Promise<string> => {
32
+ const _price = await this.llamalend.contracts[this.market.addresses.amm].contract.get_base_price(this.llamalend.constantOptions) as bigint;
33
+ return formatUnits(_price);
34
+ },
35
+ {
36
+ promise: true,
37
+ maxAge: 86400 * 1000, // 1d
38
+ });
39
+
40
+ public oraclePrice = memoize(async (): Promise<string> => {
41
+ const _price = await this.llamalend.contracts[this.market.addresses.amm].contract.price_oracle(this.llamalend.constantOptions) as bigint;
42
+ return formatUnits(_price);
43
+ },
44
+ {
45
+ promise: true,
46
+ maxAge: 60 * 1000, // 1m
47
+ });
48
+
49
+ public async oraclePriceBand(): Promise<number> {
50
+ const oraclePriceBN = BN(await this.oraclePrice());
51
+ const basePriceBN = BN(await this.basePrice());
52
+ const A_BN = BN(await this.A());
53
+ const multiplier = oraclePriceBN.lte(basePriceBN) ? A_BN.minus(1).div(A_BN) : A_BN.div(A_BN.minus(1));
54
+ const term = oraclePriceBN.lte(basePriceBN) ? 1 : -1;
55
+ const compareFunc = oraclePriceBN.lte(basePriceBN) ?
56
+ (oraclePriceBN: BigNumber, currentTickPriceBN: BigNumber) => oraclePriceBN.lte(currentTickPriceBN) :
57
+ (oraclePriceBN: BigNumber, currentTickPriceBN: BigNumber) => oraclePriceBN.gt(currentTickPriceBN);
58
+
59
+ let band = 0;
60
+ let currentTickPriceBN = oraclePriceBN.lte(basePriceBN) ? basePriceBN.times(multiplier) : basePriceBN;
61
+ while (compareFunc(oraclePriceBN, currentTickPriceBN)) {
62
+ currentTickPriceBN = currentTickPriceBN.times(multiplier);
63
+ band += term;
64
+ }
65
+
66
+ return band;
67
+ }
68
+
69
+ public async price(): Promise<string> {
70
+ const _price = await this.llamalend.contracts[this.market.addresses.amm].contract.get_p(this.llamalend.constantOptions) as bigint;
71
+ return formatUnits(_price);
72
+ }
73
+
74
+ public async calcTickPrice(n: number): Promise<string> {
75
+ const basePrice = await this.basePrice();
76
+ const basePriceBN = BN(basePrice);
77
+ const A_BN = BN(await this.A());
78
+
79
+ return _cutZeros(basePriceBN.times(A_BN.minus(1).div(A_BN).pow(n)).toFixed(18))
80
+ }
81
+
82
+ public async calcBandPrices(n: number): Promise<[string, string]> {
83
+ return [await this.calcTickPrice(n + 1), await this.calcTickPrice(n)]
84
+ }
85
+
86
+ public async calcRangePct(range: number): Promise<string> {
87
+ const A_BN = BN(await this.A());
88
+ const startBN = BN(1);
89
+ const endBN = A_BN.minus(1).div(A_BN).pow(range);
90
+
91
+ return startBN.minus(endBN).times(100).toFixed(6)
92
+ }
93
+
94
+ public async getPrices(_n2: bigint, _n1: bigint): Promise<[string,string]> {
95
+ const contract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
96
+ return (await this.llamalend.multicallProvider.all([
97
+ contract.p_oracle_down(_n2),
98
+ contract.p_oracle_up(_n1),
99
+ ]) as bigint[]).map((_p) => formatUnits(_p)) as [string,string];
100
+ }
101
+
102
+
103
+ public async calcPrices(_n2: bigint, _n1: bigint): Promise<[string, string]> {
104
+ return [await this.calcTickPrice(Number(_n2) + 1), await this.calcTickPrice(Number(_n1))];
105
+ }
106
+
107
+ public checkRange(range: number): void {
108
+ if (range < this.market.minBands) throw Error(`range must be >= ${this.market.minBands}`);
109
+ if (range > this.market.maxBands) throw Error(`range must be <= ${this.market.maxBands}`);
110
+ }
111
+ }
@@ -0,0 +1,313 @@
1
+ import memoize from "memoizee";
2
+ import {TAmount} from "../../../interfaces";
3
+ import type { LendMarketTemplate } from "../../LendMarketTemplate";
4
+ import {
5
+ parseUnits,
6
+ BN,
7
+ toBN,
8
+ formatUnits,
9
+ formatNumber,
10
+ } from "../../../utils";
11
+ import {Llamalend} from "../../../llamalend";
12
+ import {_getMarketsData} from "../../../external-api";
13
+ import {cacheKey, cacheStats} from "../../../cache";
14
+
15
+ export class StatsBaseModule {
16
+ protected market: LendMarketTemplate;
17
+ protected llamalend: Llamalend;
18
+
19
+ constructor(market: LendMarketTemplate) {
20
+ this.market = market;
21
+ this.llamalend = market.getLlamalend();
22
+ }
23
+
24
+ public statsParameters = memoize(async (): Promise<{
25
+ fee: string, // %
26
+ admin_fee: string, // %
27
+ liquidation_discount: string, // %
28
+ loan_discount: string, // %
29
+ base_price: string,
30
+ A: string,
31
+ }> => {
32
+ const llammaContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
33
+ const controllerContract = this.llamalend.contracts[this.market.addresses.controller].multicallContract;
34
+
35
+ const calls = [
36
+ llammaContract.fee(),
37
+ llammaContract.admin_fee(), // TODO: removed
38
+ controllerContract.liquidation_discount(),
39
+ controllerContract.loan_discount(),
40
+ llammaContract.get_base_price(),
41
+ llammaContract.A(),
42
+ ]
43
+
44
+ const [_fee, _admin_fee, _liquidation_discount, _loan_discount, _base_price, _A]: bigint[] = await this.llamalend.multicallProvider.all(calls) as bigint[];
45
+ const A = formatUnits(_A, 0)
46
+ const base_price = formatUnits(_base_price)
47
+ const [fee, admin_fee, liquidation_discount, loan_discount] = [_fee, _admin_fee, _liquidation_discount, _loan_discount]
48
+ .map((_x) => formatUnits(_x * BigInt(100)));
49
+
50
+ return { fee, admin_fee, liquidation_discount, loan_discount, base_price, A }
51
+ }, {
52
+ promise: true,
53
+ maxAge: 5 * 60 * 1000, // 5m
54
+ });
55
+
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
+ public async statsRates(isGetter = true, useAPI = false): Promise<{borrowApr: string, lendApr: string, borrowApy: string, lendApy: string}> {
73
+ 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
+ }
88
+ } else {
89
+ 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
+ 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 }
105
+ }
106
+ }
107
+
108
+ public async statsFutureRates(dReserves: TAmount, dDebt: TAmount, useAPI = true): Promise<{borrowApr: string, lendApr: string, borrowApy: string, lendApy: string}> {
109
+ const _dReserves = parseUnits(dReserves, this.market.borrowed_token.decimals);
110
+ const _dDebt = parseUnits(dDebt, this.market.borrowed_token.decimals);
111
+ 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
+ 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 }
127
+ }
128
+
129
+ public async statsBalances(): Promise<[string, string]> {
130
+ const borrowedContract = this.llamalend.contracts[this.market.borrowed_token.address].multicallContract;
131
+ const collateralContract = this.llamalend.contracts[this.market.collateral_token.address].multicallContract;
132
+ const ammContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
133
+ const calls = [
134
+ borrowedContract.balanceOf(this.market.addresses.amm),
135
+ collateralContract.balanceOf(this.market.addresses.amm),
136
+ ammContract.admin_fees_x(), // TODO: always 0
137
+ ammContract.admin_fees_y(), // TODO: always 0
138
+ ]
139
+ const [_borrowedBalance, _collateralBalance, _borrowedAdminFees, _collateralAdminFees]: bigint[] = await this.llamalend.multicallProvider.all(calls);
140
+
141
+ return [
142
+ formatUnits(_borrowedBalance - _borrowedAdminFees, this.market.borrowed_token.decimals),
143
+ formatUnits(_collateralBalance - _collateralAdminFees, this.market.collateral_token.decimals),
144
+ ];
145
+ }
146
+
147
+ public statsBandsInfo = memoize(async (): Promise<{ activeBand: number, maxBand: number, minBand: number, liquidationBand: number | null }> => {
148
+ const ammContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
149
+ const calls = [
150
+ ammContract.active_band_with_skip(),
151
+ ammContract.max_band(),
152
+ ammContract.min_band(),
153
+ ]
154
+
155
+ const [activeBand, maxBand, minBand] = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_b) => Number(_b));
156
+ const { borrowed, collateral } = await this.statsBandBalances(activeBand);
157
+ let liquidationBand = null;
158
+ if (Number(borrowed) > 0 && Number(collateral) > 0) liquidationBand = activeBand;
159
+ return { activeBand, maxBand, minBand, liquidationBand }
160
+ },
161
+ {
162
+ promise: true,
163
+ maxAge: 60 * 1000, // 1m
164
+ });
165
+
166
+ public async statsBandBalances(n: number): Promise<{ borrowed: string, collateral: string }> {
167
+ const ammContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
168
+ const calls = [];
169
+ calls.push(ammContract.bands_x(n), ammContract.bands_y(n));
170
+ const _balances: bigint[] = await this.llamalend.multicallProvider.all(calls);
171
+
172
+ // bands_x and bands_y always return amounts with 18 decimals
173
+ return {
174
+ borrowed: formatNumber(formatUnits(_balances[0]), this.market.borrowed_token.decimals),
175
+ collateral: formatNumber(formatUnits(_balances[1]), this.market.collateral_token.decimals),
176
+ }
177
+ }
178
+
179
+ public async statsBandsBalances(): Promise<{ [index: number]: { borrowed: string, collateral: string } }> {
180
+ const { maxBand, minBand } = await this.statsBandsInfo();
181
+
182
+ const ammContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
183
+ const calls = [];
184
+ for (let i = minBand; i <= maxBand; i++) {
185
+ calls.push(ammContract.bands_x(i), ammContract.bands_y(i));
186
+ }
187
+
188
+ const _bands: bigint[] = await this.llamalend.multicallProvider.all(calls);
189
+
190
+ const bands: { [index: number]: { borrowed: string, collateral: string } } = {};
191
+ for (let i = minBand; i <= maxBand; i++) {
192
+ const _i = i - minBand
193
+ // bands_x and bands_y always return amounts with 18 decimals
194
+ bands[i] = {
195
+ borrowed: formatNumber(formatUnits(_bands[2 * _i]), this.market.borrowed_token.decimals),
196
+ collateral: formatNumber(formatUnits(_bands[(2 * _i) + 1]), this.market.collateral_token.decimals),
197
+ }
198
+ }
199
+
200
+ return bands
201
+ }
202
+
203
+ public async statsTotalDebt(isGetter = true, useAPI = true): Promise<string> {
204
+ 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
+ }
214
+ } else {
215
+ let _debt;
216
+ if(isGetter) {
217
+ _debt = cacheStats.get(cacheKey(this.market.addresses.controller, 'total_debt'));
218
+ } else {
219
+ _debt = await this.llamalend.contracts[this.market.addresses.controller].contract.total_debt(this.llamalend.constantOptions);
220
+ cacheStats.set(cacheKey(this.market.addresses.controller, 'total_debt'), _debt);
221
+ }
222
+
223
+ return formatUnits(_debt, this.market.borrowed_token.decimals);
224
+ }
225
+ }
226
+
227
+ public statsAmmBalances = async (isGetter = true, useAPI = false): Promise<{ borrowed: string, collateral: string }> => {
228
+ 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
+ }
241
+ } else {
242
+ const borrowedContract = this.llamalend.contracts[this.market.addresses.borrowed_token].multicallContract;
243
+ const collateralContract = this.llamalend.contracts[this.market.addresses.collateral_token].multicallContract;
244
+ const ammContract = this.llamalend.contracts[this.market.addresses.amm].multicallContract;
245
+
246
+ let _balance_x, _fee_x, _balance_y, _fee_y; // TODO: fees are always 0
247
+ if(isGetter) {
248
+ [_balance_x, _fee_x, _balance_y, _fee_y] = [
249
+ cacheStats.get(cacheKey(this.market.addresses.borrowed_token, 'balanceOf', this.market.addresses.amm)),
250
+ cacheStats.get(cacheKey(this.market.addresses.amm, 'admin_fees_x')),
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
+ ]
254
+ } else {
255
+ [_balance_x, _fee_x, _balance_y, _fee_y] = await this.llamalend.multicallProvider.all([
256
+ borrowedContract.balanceOf(this.market.addresses.amm),
257
+ ammContract.admin_fees_x(),
258
+ collateralContract.balanceOf(this.market.addresses.amm),
259
+ ammContract.admin_fees_y(),
260
+ ]);
261
+ 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
+ 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
+ }
266
+
267
+ return {
268
+ borrowed: toBN(_balance_x, this.market.borrowed_token.decimals).minus(toBN(_fee_x, this.market.borrowed_token.decimals)).toString(),
269
+ collateral: toBN(_balance_y, this.market.collateral_token.decimals).minus(toBN(_fee_y, this.market.collateral_token.decimals)).toString(),
270
+ }
271
+ }
272
+ }
273
+
274
+ public async statsCapAndAvailable(isGetter = true, useAPI = false): Promise<{ cap: string, available: string }> {
275
+ 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
+ }
288
+ } else {
289
+ const vaultContract = this.llamalend.contracts[this.market.addresses.vault].multicallContract;
290
+ const borrowedContract = this.llamalend.contracts[this.market.addresses.borrowed_token].multicallContract;
291
+
292
+ let _cap, _available;
293
+ if(isGetter) { // TODO: should call controller.available_balance() instead of borrowed_token.balanceOf(controller)
294
+ _cap = cacheStats.get(cacheKey(this.market.addresses.vault, 'totalAssets', this.market.addresses.controller));
295
+ _available = cacheStats.get(cacheKey(this.market.addresses.borrowed_token, 'balanceOf', this.market.addresses.controller));
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
+ }
304
+
305
+ return {
306
+ cap: this.llamalend.formatUnits(_cap, this.market.borrowed_token.decimals),
307
+ available: this.llamalend.formatUnits(_available, this.market.borrowed_token.decimals),
308
+ }
309
+ // cap -> totalAssets
310
+ // add cap: controller.borrow_cap() // Display
311
+ }
312
+ }
313
+ }
@@ -0,0 +1,243 @@
1
+ import memoize from "memoizee";
2
+ import {IDict} from "../../../interfaces";
3
+ import type { LendMarketTemplate } from "../../LendMarketTemplate";
4
+ import {
5
+ BN,
6
+ formatUnits,
7
+ _getAddress, toBN,
8
+ } from "../../../utils";
9
+ import {Llamalend} from "../../../llamalend";
10
+ import {_getUserCollateral, _getUserCollateralForce} from "../../../external-api";
11
+ import {IUserPosition} from "../../interfaces/common";
12
+
13
+ export class UserPositionModule implements IUserPosition {
14
+ private market: LendMarketTemplate;
15
+ private llamalend: Llamalend;
16
+
17
+ constructor(market: LendMarketTemplate) {
18
+ this.market = market;
19
+ this.llamalend = market.getLlamalend();
20
+ }
21
+
22
+ public async userLoanExists(address = ""): Promise<boolean> {
23
+ address = _getAddress.call(this.llamalend, address);
24
+ return await this.llamalend.contracts[this.market.addresses.controller].contract.loan_exists(address, this.llamalend.constantOptions);
25
+ }
26
+
27
+ private _userState = memoize(async (address = ""): Promise<{ _collateral: bigint, _borrowed: bigint, _debt: bigint, _N: bigint }> => {
28
+ address = _getAddress.call(this.llamalend, address);
29
+ const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
30
+ const [_collateral, _borrowed, _debt, _N] = await contract.user_state(address, this.llamalend.constantOptions) as bigint[];
31
+
32
+ return { _collateral, _borrowed, _debt, _N }
33
+ },
34
+ {
35
+ promise: true,
36
+ maxAge: 10 * 1000, // 10s
37
+ });
38
+
39
+ public async userState(address = ""): Promise<{ collateral: string, borrowed: string, debt: string, N: string }> {
40
+ const { _collateral, _borrowed, _debt, _N } = await this._userState(address);
41
+
42
+ return {
43
+ collateral: formatUnits(_collateral, this.market.collateral_token.decimals),
44
+ borrowed: formatUnits(_borrowed, this.market.borrowed_token.decimals),
45
+ debt: formatUnits(_debt, this.market.borrowed_token.decimals),
46
+ N: formatUnits(_N, 0),
47
+ };
48
+ }
49
+
50
+ public async userStateBigInt(address = ""): Promise<{ _collateral: bigint, _borrowed: bigint, _debt: bigint, _N: bigint }> {
51
+ return await this._userState(address);
52
+ }
53
+
54
+ public async userHealth(full = true, address = ""): Promise<string> {
55
+ address = _getAddress.call(this.llamalend, address);
56
+ let _health = await this.llamalend.contracts[this.market.addresses.controller].contract.health(address, full, this.llamalend.constantOptions) as bigint;
57
+ _health = _health * BigInt(100);
58
+
59
+ return formatUnits(_health);
60
+ }
61
+
62
+ private async _userBands(address: string): Promise<bigint[]> {
63
+ address = _getAddress.call(this.llamalend, address);
64
+ const _bands = await this.llamalend.contracts[this.market.addresses.amm].contract.read_user_tick_numbers(address, this.llamalend.constantOptions) as bigint[];
65
+
66
+ return Array.from(_bands).reverse();
67
+ }
68
+
69
+ public async userBands(address = ""): Promise<number[]> {
70
+ return (await this._userBands(address)).map((_t) => Number(_t));
71
+ }
72
+
73
+ public async userBandsBigInt(address = ""): Promise<bigint[]> {
74
+ return await this._userBands(address);
75
+ }
76
+
77
+ public async userRange(address = ""): Promise<number> {
78
+ const [n2, n1] = await this.userBands(address);
79
+ if (n1 == n2) return 0;
80
+ return n2 - n1 + 1;
81
+ }
82
+
83
+ public async userPrices(address = ""): Promise<string[]> {
84
+ address = _getAddress.call(this.llamalend, address);
85
+ const _prices = await this.llamalend.contracts[this.market.addresses.controller].contract.user_prices(address, this.llamalend.constantOptions) as bigint[];
86
+
87
+ return _prices.map((_p) => formatUnits(_p)).reverse();
88
+ }
89
+
90
+ public async userLoss(userAddress = ""): Promise<{ deposited_collateral: string, current_collateral_estimation: string, loss: string, loss_pct: string }> {
91
+ userAddress = _getAddress.call(this.llamalend, userAddress);
92
+ const [userCollateral, _current_collateral_estimation] = await Promise.all([
93
+ _getUserCollateral(this.llamalend.constants.NETWORK_NAME, this.market.addresses.controller, userAddress),
94
+ this.llamalend.contracts[this.market.addresses.amm].contract.get_y_up(userAddress),
95
+ ]);
96
+
97
+ const deposited_collateral = userCollateral.total_deposit_precise;
98
+
99
+ const current_collateral_estimation = this.llamalend.formatUnits(_current_collateral_estimation, this.market.collateral_token.decimals);
100
+ if (BN(deposited_collateral).lte(0)) {
101
+ return {
102
+ deposited_collateral,
103
+ current_collateral_estimation,
104
+ loss: "0.0",
105
+ loss_pct: "0.0",
106
+ };
107
+ }
108
+ const loss = BN(deposited_collateral).minus(current_collateral_estimation).toString()
109
+ const loss_pct = BN(loss).div(deposited_collateral).times(100).toString();
110
+
111
+ return {
112
+ deposited_collateral,
113
+ current_collateral_estimation,
114
+ loss,
115
+ loss_pct,
116
+ };
117
+ }
118
+
119
+ public async userBandsBalances(address = ""): Promise<IDict<{ collateral: string, borrowed: string }>> {
120
+ const [n2, n1] = await this.userBands(address);
121
+ if (n1 == 0 && n2 == 0) return {};
122
+
123
+ address = _getAddress.call(this.llamalend, address);
124
+ const contract = this.llamalend.contracts[this.market.addresses.amm].contract;
125
+ const [_borrowed, _collateral] = await contract.get_xy(address, this.llamalend.constantOptions) as [bigint[], bigint[]];
126
+
127
+ const res: IDict<{ borrowed: string, collateral: string }> = {};
128
+ for (let i = n1; i <= n2; i++) {
129
+ res[i] = {
130
+ collateral: formatUnits(_collateral[i - n1], this.market.collateral_token.decimals),
131
+ borrowed: formatUnits(_borrowed[i - n1], this.market.borrowed_token.decimals),
132
+ };
133
+ }
134
+
135
+ return res
136
+ }
137
+
138
+ public async currentLeverage(userAddress = ''): Promise<string> {
139
+ userAddress = _getAddress.call(this.llamalend, userAddress);
140
+ const [userCollateral, {collateral}] = await Promise.all([
141
+ _getUserCollateral(this.llamalend.constants.NETWORK_NAME, this.market.addresses.controller, userAddress),
142
+ this.userState(userAddress),
143
+ ]);
144
+
145
+ const total_deposit_from_user = userCollateral.total_deposit_from_user_precise;
146
+
147
+ return BN(collateral).div(total_deposit_from_user).toString();
148
+ }
149
+
150
+ public async currentPnL(userAddress = ''): Promise<Record<string, string>> {
151
+ userAddress = _getAddress.call(this.llamalend, userAddress);
152
+
153
+ const calls = [
154
+ this.llamalend.contracts[this.market.addresses.controller].multicallContract.user_state(userAddress, this.llamalend.constantOptions),
155
+ this.llamalend.contracts[this.market.addresses.amm].multicallContract.price_oracle(userAddress),
156
+ ];
157
+
158
+ const [userState, oraclePrice] = await this.llamalend.multicallProvider.all(calls) as [bigint[],bigint];
159
+
160
+ if(!(userState || oraclePrice)) {
161
+ throw new Error('Multicall error')
162
+ }
163
+
164
+ const debt = userState[2];
165
+
166
+ const userCollateral = await _getUserCollateral(this.llamalend.constants.NETWORK_NAME, this.market.addresses.controller, userAddress);
167
+ const totalDepositUsdValueFull = userCollateral.total_deposit_usd_value;
168
+ const totalDepositUsdValueUser = userCollateral.total_deposit_from_user_usd_value;
169
+ const totalBorrowed = userCollateral.total_borrowed;
170
+
171
+ const oraclePriceFormatted = this.llamalend.formatUnits(oraclePrice, 18);
172
+ const debtFormatted = this.llamalend.formatUnits(debt, 18);
173
+
174
+ const {_collateral: AmmCollateral, _borrowed: AmmBorrowed} = await this._userState(userAddress)
175
+ const [AmmCollateralFormatted, AmmBorrowedFormatted] = [this.llamalend.formatUnits(AmmCollateral, this.market.collateral_token.decimals), this.llamalend.formatUnits(AmmBorrowed, this.market.borrowed_token.decimals)];
176
+
177
+ const a = BN(AmmCollateralFormatted).times(oraclePriceFormatted);
178
+ const b = BN(totalBorrowed).minus(debtFormatted)
179
+
180
+ const currentPosition = a.plus(AmmBorrowedFormatted).plus(b);
181
+
182
+ const currentProfit = currentPosition.minus(totalDepositUsdValueFull);
183
+
184
+ const percentage = currentProfit.div(totalDepositUsdValueUser).times(100);
185
+
186
+ return {
187
+ currentPosition: currentPosition.toFixed(this.market.borrowed_token.decimals).toString(),
188
+ deposited: totalDepositUsdValueUser.toString(),
189
+ currentProfit: currentProfit.toFixed(this.market.borrowed_token.decimals).toString(),
190
+ percentage: percentage.toFixed(2).toString(),
191
+ };
192
+ }
193
+
194
+ public async userBoost(address = ""): Promise<string> {
195
+ if (this.market.addresses.gauge === this.llamalend.constants.ZERO_ADDRESS) {
196
+ throw Error(`${this.market.name} doesn't have gauge`);
197
+ }
198
+ if (this.market.vault.rewardsOnly()) {
199
+ throw Error(`${this.market.name} has Rewards-Only Gauge. Use stats.rewardsApy instead`);
200
+ }
201
+ address = _getAddress.call(this.llamalend, address);
202
+
203
+ const gaugeContract = this.llamalend.contracts[this.market.addresses.gauge].multicallContract;
204
+ const [workingBalanceBN, balanceBN] = (await this.llamalend.multicallProvider.all([
205
+ gaugeContract.working_balances(address),
206
+ gaugeContract.balanceOf(address),
207
+ ]) as bigint[]).map((value: bigint) => toBN(value));
208
+
209
+ if (balanceBN.isZero()) {
210
+ return '1.0';
211
+ }
212
+
213
+ const boostBN = workingBalanceBN.div(0.4).div(balanceBN);
214
+ if (boostBN.lt(1)) return '1.0';
215
+ if (boostBN.gt(2.5)) return '2.5';
216
+
217
+ return boostBN.toFixed(4).replace(/([0-9])0+$/, '$1');
218
+ }
219
+
220
+ public async getCurrentLeverageParams(userAddress: string): Promise<{ stateCollateral: string, totalDepositFromUser: string }> {
221
+ const [userCollateralData, { collateral: stateCollateral }] = await Promise.all([
222
+ _getUserCollateral(this.llamalend.constants.NETWORK_NAME, this.market.addresses.controller, userAddress),
223
+ this.userState(userAddress),
224
+ ]);
225
+
226
+ return {
227
+ stateCollateral,
228
+ totalDepositFromUser: String(userCollateralData.total_deposit_from_user_precise),
229
+ };
230
+ }
231
+
232
+ public async forceUpdateUserState(newTx: string, userAddress?: string): Promise<void> {
233
+ const address = userAddress || this.llamalend.signerAddress;
234
+ if (!address) throw Error("Need to connect wallet or pass address into args");
235
+
236
+ await _getUserCollateralForce(
237
+ this.llamalend.constants.NETWORK_NAME,
238
+ this.market.addresses.controller,
239
+ address,
240
+ newTx
241
+ );
242
+ }
243
+ }