@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,912 @@
1
+ import memoize from "memoizee";
2
+ import {TAmount, TGas, IDict, IQuoteOdos} from "../../../interfaces";
3
+ import type { LendMarketTemplate } from "../../LendMarketTemplate";
4
+ import {
5
+ _getAddress,
6
+ parseUnits,
7
+ BN,
8
+ toBN,
9
+ fromBN,
10
+ ensureAllowance,
11
+ hasAllowance,
12
+ ensureAllowanceEstimateGas,
13
+ formatUnits,
14
+ smartNumber,
15
+ formatNumber,
16
+ _mulBy1_3,
17
+ DIGas,
18
+ } from "../../../utils";
19
+ import {Llamalend} from "../../../llamalend";
20
+ import BigNumber from "bignumber.js";
21
+ import {_assembleTxOdos, _getExpectedOdos, _getQuoteOdos} from "../../../external-api";
22
+
23
+ /**
24
+ * LeverageZapV2 module for LendMarketTemplate
25
+ */
26
+ export class LeverageZapV1BaseModule {
27
+ protected market: LendMarketTemplate;
28
+ protected llamalend: Llamalend;
29
+
30
+ constructor(market: LendMarketTemplate) {
31
+ this.market = market;
32
+ this.llamalend = market.getLlamalend();
33
+ }
34
+
35
+ private _getMarketId = (): number => Number(this.market.id.split("-").slice(-1)[0]);
36
+
37
+ // ============ CREATE LOAN METHODS ============
38
+
39
+ public hasLeverage = (): boolean => {
40
+ return this.llamalend.constants.ALIASES.leverage_zap !== this.llamalend.constants.ZERO_ADDRESS &&
41
+ this._getMarketId() >= Number(this.llamalend.constants.ALIASES["leverage_markets_start_id"]);
42
+ }
43
+
44
+ private _checkLeverageZap(): void {
45
+ if (!this.hasLeverage()) {
46
+ throw Error("This market does not support leverage");
47
+ }
48
+ }
49
+
50
+ private async _get_k_effective_BN(N: number): Promise<BigNumber> {
51
+ // d_k_effective: uint256 = (1 - loan_discount) * sqrt((A-1)/A) / N
52
+ // k_effective = d_k_effective * sum_{0..N-1}(((A-1) / A)**k)
53
+ const { loan_discount, A } = await this.market.stats.parameters();
54
+ const A_BN = BN(A);
55
+ const A_ratio_BN = A_BN.minus(1).div(A_BN);
56
+
57
+ const d_k_effective_BN = BN(100).minus(loan_discount).div(100).times(A_ratio_BN.sqrt()).div(N);
58
+ let S = BN(0);
59
+ for (let n = 0; n < N; n++) {
60
+ S = S.plus(A_ratio_BN.pow(n))
61
+ }
62
+
63
+ return d_k_effective_BN.times(S);
64
+ }
65
+
66
+ public async maxLeverage(N: number): Promise<string> {
67
+ // max_leverage = 1 / (k_effective - 1)
68
+ const k_effective_BN = await this._get_k_effective_BN(N);
69
+
70
+ return BN(1).div(BN(1).minus(k_effective_BN)).toString()
71
+ }
72
+
73
+ public async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number):
74
+ Promise<{
75
+ maxDebt: string,
76
+ maxTotalCollateral: string,
77
+ userCollateral: string,
78
+ collateralFromUserBorrowed: string,
79
+ collateralFromMaxDebt: string,
80
+ maxLeverage: string,
81
+ avgPrice: string,
82
+ }> {
83
+ // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg)
84
+ this._checkLeverageZap();
85
+ if (range > 0) this.market.prices.checkRange(range);
86
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
87
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
88
+
89
+ const oraclePriceBand = await this.market.prices.oraclePriceBand();
90
+ let pAvgBN = BN(await this.market.prices.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band
91
+ let maxBorrowablePrevBN = BN(0);
92
+ let maxBorrowableBN = BN(0);
93
+ let _userEffectiveCollateral = BigInt(0);
94
+ let _maxLeverageCollateral = BigInt(0);
95
+
96
+ const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap].contract;
97
+ for (let i = 0; i < 5; i++) {
98
+ maxBorrowablePrevBN = maxBorrowableBN;
99
+ _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.market.collateral_token.decimals);
100
+ let _maxBorrowable = await contract.max_borrowable(this.market.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN));
101
+ _maxBorrowable = _maxBorrowable * BigInt(998) / BigInt(1000)
102
+ if (_maxBorrowable === BigInt(0)) break;
103
+ maxBorrowableBN = toBN(_maxBorrowable, this.market.borrowed_token.decimals);
104
+
105
+ if (maxBorrowableBN.minus(maxBorrowablePrevBN).abs().div(maxBorrowablePrevBN).lt(0.0005)) {
106
+ maxBorrowableBN = maxBorrowablePrevBN;
107
+ break;
108
+ }
109
+
110
+ // additionalCollateral = (userBorrowed / p) + leverageCollateral
111
+ const _maxAdditionalCollateral = BigInt(await _getExpectedOdos.call(this.llamalend,
112
+ this.market.addresses.borrowed_token, this.market.addresses.collateral_token, _maxBorrowable + _userBorrowed, this.market.addresses.amm));
113
+ pAvgBN = maxBorrowableBN.plus(userBorrowed).div(toBN(_maxAdditionalCollateral, this.market.collateral_token.decimals));
114
+ _maxLeverageCollateral = _maxAdditionalCollateral - fromBN(BN(userBorrowed).div(pAvgBN), this.market.collateral_token.decimals);
115
+ }
116
+
117
+ const userEffectiveCollateralBN = maxBorrowableBN.gt(0) ? toBN(_userEffectiveCollateral, this.market.collateral_token.decimals) : BN(0);
118
+ const maxLeverageCollateralBN = toBN(_maxLeverageCollateral, this.market.collateral_token.decimals);
119
+
120
+ return {
121
+ maxDebt: formatNumber(maxBorrowableBN.toString(), this.market.borrowed_token.decimals),
122
+ maxTotalCollateral: formatNumber(maxLeverageCollateralBN.plus(userEffectiveCollateralBN).toString(), this.market.collateral_token.decimals),
123
+ userCollateral: formatNumber(userCollateral, this.market.collateral_token.decimals),
124
+ collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN).toString(), this.market.collateral_token.decimals),
125
+ collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN.toString(), this.market.collateral_token.decimals),
126
+ maxLeverage: maxLeverageCollateralBN.plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(),
127
+ avgPrice: pAvgBN.toString(),
128
+ };
129
+ }
130
+
131
+ public leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount):
132
+ Promise<IDict<{
133
+ maxDebt: string,
134
+ maxTotalCollateral: string,
135
+ userCollateral: string,
136
+ collateralFromUserBorrowed: string,
137
+ collateralFromMaxDebt: string,
138
+ maxLeverage: string,
139
+ avgPrice: string,
140
+ }>> => {
141
+ this._checkLeverageZap();
142
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
143
+ const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap].multicallContract;
144
+
145
+ const oraclePriceBand = await this.market.prices.oraclePriceBand();
146
+ const pAvgApproxBN = BN(await this.market.prices.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band
147
+ let pAvgBN: BigNumber | null = null;
148
+ const arrLength = this.market.maxBands - this.market.minBands + 1;
149
+ let maxLeverageCollateralBN: BigNumber[] = new Array(arrLength).fill(BN(0));
150
+ let _maxLeverageCollateral: bigint[] = new Array(arrLength).fill(BigInt(0));
151
+ let maxBorrowablePrevBN: BigNumber[] = new Array(arrLength).fill(BN(0));
152
+ let maxBorrowableBN: BigNumber[] = new Array(arrLength).fill(BN(0));
153
+ let _maxBorrowable: bigint[] = new Array(arrLength).fill(BigInt(0));
154
+
155
+ for (let i = 0; i < 5; i++) {
156
+ const pBN = pAvgBN ?? pAvgApproxBN;
157
+ maxBorrowablePrevBN = maxBorrowableBN;
158
+ const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pBN), this.market.collateral_token.decimals);
159
+ const calls = [];
160
+ for (let N = this.market.minBands; N <= this.market.maxBands; N++) {
161
+ const j = N - this.market.minBands;
162
+ calls.push(contract.max_borrowable(this.market.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN)));
163
+ }
164
+ _maxBorrowable = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_mb) => _mb * BigInt(998) / BigInt(1000));
165
+ maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.market.borrowed_token.decimals));
166
+
167
+ const deltaBN = maxBorrowableBN.map((mb, l) => mb.minus(maxBorrowablePrevBN[l]).abs().div(mb));
168
+ if (BigNumber.max(...deltaBN).lt(0.0005)) {
169
+ maxBorrowableBN = maxBorrowablePrevBN;
170
+ break;
171
+ }
172
+
173
+ if (pAvgBN === null){
174
+ const _y = BigInt(await _getExpectedOdos.call(this.llamalend, this.market.addresses.borrowed_token, this.market.addresses.collateral_token, _maxBorrowable[0], this.market.addresses.amm));
175
+ const yBN = toBN(_y, this.market.collateral_token.decimals);
176
+ pAvgBN = maxBorrowableBN[0].div(yBN);
177
+ }
178
+
179
+ maxLeverageCollateralBN = maxBorrowableBN.map((mb) => mb.div(pAvgBN as BigNumber));
180
+ _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.market.collateral_token.decimals));
181
+ }
182
+
183
+ const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber));
184
+
185
+ const res: IDict<{
186
+ maxDebt: string,
187
+ maxTotalCollateral: string,
188
+ userCollateral: string,
189
+ collateralFromUserBorrowed: string,
190
+ collateralFromMaxDebt: string,
191
+ maxLeverage: string,
192
+ avgPrice: string,
193
+ }> = {};
194
+ for (let N = this.market.minBands; N <= this.market.maxBands; N++) {
195
+ const j = N - this.market.minBands;
196
+ res[N] = {
197
+ maxDebt: formatNumber(maxBorrowableBN[j].toString(), this.market.borrowed_token.decimals),
198
+ maxTotalCollateral: formatNumber(maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), this.market.collateral_token.decimals),
199
+ userCollateral: formatNumber(userCollateral, this.market.collateral_token.decimals),
200
+ collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN as BigNumber).toString(), this.market.collateral_token.decimals),
201
+ collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN[j].toString(), this.market.collateral_token.decimals),
202
+ maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(),
203
+ avgPrice: (pAvgBN as BigNumber).toString(),
204
+ };
205
+ }
206
+
207
+ return res;
208
+ },
209
+ {
210
+ promise: true,
211
+ maxAge: 60 * 1000, // 1m
212
+ });
213
+
214
+ private _setSwapDataToCache = async (inputCoinAddress: string, outputCoinAddress: string, _amount: bigint, slippage: number) => {
215
+ let swapData = await _getQuoteOdos.call(this.llamalend, inputCoinAddress, outputCoinAddress, _amount, this.market.addresses.amm, true, slippage);
216
+ while (swapData.pathId == null) {
217
+ swapData = await _getQuoteOdos.call(this.llamalend, inputCoinAddress, outputCoinAddress, _amount, this.market.addresses.amm, true, slippage);
218
+ }
219
+ const key = `${inputCoinAddress}-${_amount}`;
220
+ this.market.swapDataCache[key] = { ...swapData, slippage };
221
+ }
222
+
223
+ private _getSwapDataFromCache = (inputCoinAddress: string, _amount: bigint): IQuoteOdos => {
224
+ const key = `${inputCoinAddress}-${_amount}`;
225
+ if (!(key in this.market.swapDataCache)) throw Error(
226
+ "You must call corresponding `expected` method first " +
227
+ "(leverage.createLoanExpectedCollateral, leverage.borrowMoreExpectedCollateral or leverage.repayExpectedBorrowed)"
228
+ );
229
+
230
+ return this.market.swapDataCache[key]
231
+ }
232
+
233
+ private _leverageExpectedCollateral = async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, user?: string):
234
+ Promise<{ _futureStateCollateral: bigint, _totalCollateral: bigint, _userCollateral: bigint,
235
+ _collateralFromUserBorrowed: bigint, _collateralFromDebt: bigint, avgPrice: string }> => {
236
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
237
+ const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
238
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
239
+ // additionalCollateral = (userBorrowed / p) + leverageCollateral
240
+ const _additionalCollateral = BigInt(this._getSwapDataFromCache(this.market.addresses.borrowed_token, _debt + _userBorrowed).outAmounts[0]);
241
+ const _collateralFromDebt = _debt * BigInt(10**18) / (_debt + _userBorrowed) * _additionalCollateral / BigInt(10**18);
242
+ const _collateralFromUserBorrowed = _additionalCollateral - _collateralFromDebt;
243
+ let _stateCollateral = BigInt(0);
244
+ if (user) {
245
+ const { _collateral, _borrowed } = await this.market.userPosition.userStateBigInt(user);
246
+ if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`);
247
+ _stateCollateral = _collateral;
248
+ }
249
+ const _totalCollateral = _userCollateral + _additionalCollateral;
250
+ const _futureStateCollateral = _stateCollateral + _totalCollateral;
251
+ const avgPrice = toBN(_debt + _userBorrowed, this.market.borrowed_token.decimals).div(toBN(_additionalCollateral, this.market.collateral_token.decimals)).toString();
252
+
253
+ return { _futureStateCollateral, _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice };
254
+ };
255
+
256
+ public async leverageCreateLoanExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage = 0.1):
257
+ Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string, avgPrice: string }> {
258
+ this._checkLeverageZap();
259
+ const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
260
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
261
+ await this._setSwapDataToCache(this.market.addresses.borrowed_token, this.market.addresses.collateral_token, _debt + _userBorrowed, slippage);
262
+ const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice } =
263
+ await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt);
264
+ return {
265
+ totalCollateral: formatUnits(_totalCollateral, this.market.collateral_token.decimals),
266
+ userCollateral: formatUnits(_userCollateral, this.market.collateral_token.decimals),
267
+ collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.market.collateral_token.decimals),
268
+ collateralFromDebt: formatUnits(_collateralFromDebt, this.market.collateral_token.decimals),
269
+ leverage: toBN(_collateralFromDebt + _userCollateral + _collateralFromUserBorrowed, this.market.collateral_token.decimals)
270
+ .div(toBN(_userCollateral + _collateralFromUserBorrowed, this.market.collateral_token.decimals)).toString(),
271
+ avgPrice,
272
+ }
273
+ }
274
+
275
+ public async leverageCreateLoanPriceImpact(userBorrowed: TAmount, debt: TAmount): Promise<string> {
276
+ this._checkLeverageZap();
277
+ const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
278
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
279
+ return this._getSwapDataFromCache(this.market.addresses.borrowed_token, _debt + _userBorrowed).priceImpact.toString();
280
+ }
281
+
282
+ public async leverageCreateLoanMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise<number> {
283
+ this._checkLeverageZap();
284
+ const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral, userBorrowed);
285
+ for (let N = this.market.minBands; N <= this.market.maxBands; N++) {
286
+ if (BN(debt).gt(maxRecv[N].maxDebt)) return N - 1;
287
+ }
288
+
289
+ return this.market.maxBands;
290
+ }
291
+
292
+ private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, user?: string): Promise<bigint> => {
293
+ if (range > 0) this.market.prices.checkRange(range);
294
+ let _stateDebt = BigInt(0);
295
+ if (user) {
296
+ const { _debt, _borrowed, _N } = await this.market.userPosition.userStateBigInt(user);
297
+ if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`);
298
+ _stateDebt = _debt;
299
+ if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0));
300
+ }
301
+ const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, user);
302
+ const _debt = _stateDebt + parseUnits(debt, this.market.borrowed_token.decimals);
303
+ return await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, this.llamalend.constantOptions);
304
+ },
305
+ {
306
+ promise: true,
307
+ maxAge: 60 * 1000, // 1m
308
+ });
309
+
310
+ private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number): Promise<bigint[]> => {
311
+ const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt);
312
+ const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
313
+ const calls = [];
314
+ for (let N = this.market.minBands; N <= maxN; N++) {
315
+ calls.push(this.llamalend.contracts[this.market.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N));
316
+ }
317
+ return await this.llamalend.multicallProvider.all(calls) as bigint[];
318
+ },
319
+ {
320
+ promise: true,
321
+ maxAge: 60 * 1000, // 1m
322
+ });
323
+
324
+ private async _leverageBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, user?: string): Promise<[bigint, bigint]> {
325
+ const _n1 = await this._leverageCalcN1(userCollateral, userBorrowed, debt, range, user);
326
+ if (range < 0) {
327
+ const { N } = await this.market.userPosition.userState(user);
328
+ range = Number(N);
329
+ }
330
+ const _n2 = _n1 + BigInt(range - 1);
331
+
332
+ return [_n2, _n1];
333
+ }
334
+
335
+ private async _leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise<IDict<[bigint, bigint]>> {
336
+ const maxN = await this.leverageCreateLoanMaxRange(userCollateral, userBorrowed, debt);
337
+ const _n1_arr = await this._leverageCalcN1AllRanges(userCollateral, userBorrowed, debt, maxN);
338
+ const _n2_arr: bigint[] = [];
339
+ for (let N = this.market.minBands; N <= maxN; N++) {
340
+ _n2_arr.push(_n1_arr[N - this.market.minBands] + BigInt(N - 1));
341
+ }
342
+
343
+ const _bands: IDict<[bigint, bigint]> = {};
344
+ for (let N = this.market.minBands; N <= maxN; N++) {
345
+ _bands[N] = [_n2_arr[N - this.market.minBands], _n1_arr[N - this.market.minBands]];
346
+ }
347
+
348
+ return _bands;
349
+ }
350
+
351
+ public async leverageCreateLoanBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise<[number, number]> {
352
+ this._checkLeverageZap();
353
+ const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, range);
354
+
355
+ return [Number(_n2), Number(_n1)];
356
+ }
357
+
358
+ public async leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise<IDict<[number, number] | null>> {
359
+ this._checkLeverageZap();
360
+ const _bands = await this._leverageCreateLoanBandsAllRanges(userCollateral, userBorrowed, debt);
361
+
362
+ const bands: { [index: number]: [number, number] | null } = {};
363
+ for (let N = this.market.minBands; N <= this.market.maxBands; N++) {
364
+ if (_bands[N]) {
365
+ bands[N] = _bands[N].map(Number) as [number, number];
366
+ } else {
367
+ bands[N] = null
368
+ }
369
+ }
370
+
371
+ return bands;
372
+ }
373
+
374
+ public async leverageCreateLoanPrices(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise<string[]> {
375
+ this._checkLeverageZap();
376
+ const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, range);
377
+
378
+ return await this.market.prices.getPrices(_n2, _n1);
379
+ }
380
+
381
+ public async leverageCreateLoanPricesAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise<IDict<[string, string] | null>> {
382
+ this._checkLeverageZap();
383
+ const _bands = await this._leverageCreateLoanBandsAllRanges(userCollateral, userBorrowed, debt);
384
+
385
+ const prices: { [index: number]: [string, string] | null } = {};
386
+ for (let N = this.market.minBands; N <= this.market.maxBands; N++) {
387
+ if (_bands[N]) {
388
+ prices[N] = await this.market.prices.calcPrices(..._bands[N]);
389
+ } else {
390
+ prices[N] = null
391
+ }
392
+ }
393
+
394
+ return prices;
395
+ }
396
+
397
+ private async _leverageHealth(
398
+ userCollateral: TAmount,
399
+ userBorrowed: TAmount,
400
+ dDebt: TAmount,
401
+ range: number,
402
+ full: boolean,
403
+ user = this.llamalend.constants.ZERO_ADDRESS
404
+ ): Promise<string> {
405
+ if (range > 0) this.market.prices.checkRange(range);
406
+ const { _totalCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, user);
407
+ const { _borrowed, _N } = await this.market.userPosition.userStateBigInt(user);
408
+ if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`);
409
+ if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0));
410
+ const _dDebt = parseUnits(dDebt, this.market.borrowed_token.decimals);
411
+
412
+ const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
413
+ let _health = await contract.health_calculator(user, _totalCollateral, _dDebt, full, range, this.llamalend.constantOptions) as bigint;
414
+ _health = _health * BigInt(100);
415
+
416
+ return formatUnits(_health);
417
+ }
418
+
419
+ public async leverageCreateLoanHealth(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full = true): Promise<string> {
420
+ this._checkLeverageZap();
421
+ return await this._leverageHealth(userCollateral, userBorrowed, debt, range, full);
422
+ }
423
+
424
+ public async leverageCreateLoanIsApproved(userCollateral: TAmount, userBorrowed: TAmount): Promise<boolean> {
425
+ this._checkLeverageZap();
426
+ const collateralAllowance = await hasAllowance.call(this.llamalend,
427
+ [this.market.collateral_token.address], [userCollateral], this.llamalend.signerAddress, this.market.addresses.controller);
428
+ const borrowedAllowance = await hasAllowance.call(this.llamalend,
429
+ [this.market.borrowed_token.address], [userBorrowed], this.llamalend.signerAddress, this.llamalend.constants.ALIASES.leverage_zap);
430
+
431
+ return collateralAllowance && borrowedAllowance
432
+ }
433
+
434
+ public async leverageCreateLoanApproveEstimateGas (userCollateral: TAmount, userBorrowed: TAmount): Promise<TGas> {
435
+ this._checkLeverageZap();
436
+ const collateralGas = await ensureAllowanceEstimateGas.call(this.llamalend,
437
+ [this.market.collateral_token.address], [userCollateral], this.market.addresses.controller);
438
+ const borrowedGas = await ensureAllowanceEstimateGas.call(this.llamalend,
439
+ [this.market.borrowed_token.address], [userBorrowed], this.llamalend.constants.ALIASES.leverage_zap);
440
+
441
+ if(Array.isArray(collateralGas) && Array.isArray(borrowedGas)) {
442
+ return [collateralGas[0] + borrowedGas[0], collateralGas[1] + borrowedGas[1]]
443
+ } else {
444
+ return (collateralGas as number) + (borrowedGas as number)
445
+ }
446
+ }
447
+
448
+ public async leverageCreateLoanApprove(userCollateral: TAmount, userBorrowed: TAmount): Promise<string[]> {
449
+ this._checkLeverageZap();
450
+ const collateralApproveTx = await ensureAllowance.call(this.llamalend,
451
+ [this.market.collateral_token.address], [userCollateral], this.market.addresses.controller);
452
+ const borrowedApproveTx = await ensureAllowance.call(this.llamalend,
453
+ [this.market.borrowed_token.address], [userBorrowed], this.llamalend.constants.ALIASES.leverage_zap);
454
+
455
+ return [...collateralApproveTx, ...borrowedApproveTx]
456
+ }
457
+
458
+ public async leverageCreateLoanRouteImage(userBorrowed: TAmount, debt: TAmount): Promise<string> {
459
+ this._checkLeverageZap();
460
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
461
+ const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
462
+
463
+ return this._getSwapDataFromCache(this.market.addresses.borrowed_token, _debt + _userBorrowed).pathVizImage;
464
+ }
465
+
466
+ private async _leverageCreateLoan(
467
+ userCollateral: TAmount,
468
+ userBorrowed: TAmount,
469
+ debt: TAmount,
470
+ range: number,
471
+ slippage: number,
472
+ estimateGas: boolean
473
+ ): Promise<string | TGas> {
474
+ if (await this.market.userPosition.userLoanExists()) throw Error("Loan already created");
475
+ this.market.prices.checkRange(range);
476
+
477
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
478
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
479
+ const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
480
+ const swapData = this._getSwapDataFromCache(this.market.addresses.borrowed_token, _debt + _userBorrowed);
481
+ if (slippage !== swapData.slippage) throw Error(`You must call leverage.createLoanExpectedCollateral() with slippage=${slippage} first`);
482
+ const calldata = await _assembleTxOdos.call(this.llamalend, swapData.pathId as string);
483
+ const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
484
+ const gas = await contract.create_loan_extended.estimateGas(
485
+ _userCollateral,
486
+ _debt,
487
+ range,
488
+ this.llamalend.constants.ALIASES.leverage_zap,
489
+ [0, parseUnits(this._getMarketId(), 0), _userBorrowed],
490
+ calldata,
491
+ { ...this.llamalend.constantOptions }
492
+ );
493
+ if (estimateGas) return smartNumber(gas);
494
+
495
+ await this.llamalend.updateFeeData();
496
+ const gasLimit = _mulBy1_3(DIGas(gas));
497
+ return (await contract.create_loan_extended(
498
+ _userCollateral,
499
+ _debt,
500
+ range,
501
+ this.llamalend.constants.ALIASES.leverage_zap,
502
+ [0, parseUnits(this._getMarketId(), 0), _userBorrowed],
503
+ calldata,
504
+ { ...this.llamalend.options, gasLimit }
505
+ )).hash
506
+ }
507
+
508
+ public async leverageCreateLoanEstimateGas(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage = 0.1): Promise<number> {
509
+ this._checkLeverageZap();
510
+ if (!(await this.leverageCreateLoanIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation");
511
+ return await this._leverageCreateLoan(userCollateral, userBorrowed, debt, range, slippage, true) as number;
512
+ }
513
+
514
+ public async leverageCreateLoan(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage = 0.1): Promise<string> {
515
+ this._checkLeverageZap();
516
+ await this.leverageCreateLoanApprove(userCollateral, userBorrowed);
517
+ return await this._leverageCreateLoan(userCollateral, userBorrowed, debt, range, slippage, false) as string;
518
+ }
519
+
520
+ // ---------------- LEVERAGE BORROW MORE ----------------
521
+
522
+ public async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, address = ""):
523
+ Promise<{
524
+ maxDebt: string,
525
+ maxTotalCollateral: string,
526
+ userCollateral: string,
527
+ collateralFromUserBorrowed: string,
528
+ collateralFromMaxDebt: string,
529
+ avgPrice: string,
530
+ }> {
531
+ // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg)
532
+ this._checkLeverageZap();
533
+ address = _getAddress.call(this.llamalend, address);
534
+ const { _collateral: _stateCollateral, _borrowed: _stateBorrowed, _debt: _stateDebt, _N } = await this.market.userPosition.userStateBigInt(address);
535
+ if (_stateBorrowed > BigInt(0)) throw Error(`User ${address} is already in liquidation mode`);
536
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
537
+ const controllerContract = this.llamalend.contracts[this.market.addresses.controller].contract;
538
+ const _borrowedFromStateCollateral = await controllerContract.max_borrowable(_stateCollateral, _N, _stateDebt, this.llamalend.constantOptions) - _stateDebt;
539
+ const _userBorrowed = _borrowedFromStateCollateral + parseUnits(userBorrowed, this.market.borrowed_token.decimals);
540
+ userBorrowed = formatUnits(_userBorrowed, this.market.borrowed_token.decimals);
541
+
542
+ const oraclePriceBand = await this.market.prices.oraclePriceBand();
543
+ let pAvgBN = BN(await this.market.prices.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band
544
+ let maxBorrowablePrevBN = BN(0);
545
+ let maxBorrowableBN = BN(0);
546
+ let _userEffectiveCollateral = BigInt(0);
547
+ let _maxLeverageCollateral = BigInt(0);
548
+
549
+ const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap].contract;
550
+ for (let i = 0; i < 5; i++) {
551
+ maxBorrowablePrevBN = maxBorrowableBN;
552
+ _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.market.collateral_token.decimals);
553
+ let _maxBorrowable = await contract.max_borrowable(this.market.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, _N, fromBN(pAvgBN));
554
+ _maxBorrowable = _maxBorrowable * BigInt(998) / BigInt(1000);
555
+ if (_maxBorrowable === BigInt(0)) break;
556
+ maxBorrowableBN = toBN(_maxBorrowable, this.market.borrowed_token.decimals);
557
+
558
+ if (maxBorrowableBN.minus(maxBorrowablePrevBN).abs().div(maxBorrowablePrevBN).lt(0.0005)) {
559
+ maxBorrowableBN = maxBorrowablePrevBN;
560
+ break;
561
+ }
562
+
563
+ // additionalCollateral = (userBorrowed / p) + leverageCollateral
564
+ const _maxAdditionalCollateral = BigInt(await _getExpectedOdos.call(this.llamalend,
565
+ this.market.addresses.borrowed_token, this.market.addresses.collateral_token, _maxBorrowable + _userBorrowed, this.market.addresses.amm));
566
+ pAvgBN = maxBorrowableBN.plus(userBorrowed).div(toBN(_maxAdditionalCollateral, this.market.collateral_token.decimals));
567
+ _maxLeverageCollateral = _maxAdditionalCollateral - fromBN(BN(userBorrowed).div(pAvgBN), this.market.collateral_token.decimals);
568
+ }
569
+
570
+ if (maxBorrowableBN.eq(0)) _userEffectiveCollateral = BigInt(0);
571
+ const _maxTotalCollateral = _userEffectiveCollateral + _maxLeverageCollateral
572
+ let _maxBorrowable = await controllerContract.max_borrowable(_stateCollateral + _maxTotalCollateral, _N, _stateDebt, this.llamalend.constantOptions) - _stateDebt;
573
+ _maxBorrowable = _maxBorrowable * BigInt(998) / BigInt(1000);
574
+
575
+ return {
576
+ maxDebt: formatUnits(_maxBorrowable, this.market.borrowed_token.decimals),
577
+ maxTotalCollateral: formatUnits(_maxTotalCollateral, this.market.collateral_token.decimals),
578
+ userCollateral: formatNumber(userCollateral, this.market.collateral_token.decimals),
579
+ collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN).toString(), this.market.collateral_token.decimals),
580
+ collateralFromMaxDebt: formatUnits(_maxLeverageCollateral, this.market.collateral_token.decimals),
581
+ avgPrice: pAvgBN.toString(),
582
+ };
583
+ }
584
+
585
+ public async leverageBorrowMoreExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, slippage = 0.1, address = ""):
586
+ Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, avgPrice: string }> {
587
+ this._checkLeverageZap();
588
+ address = _getAddress.call(this.llamalend, address);
589
+ const _dDebt = parseUnits(dDebt, this.market.borrowed_token.decimals);
590
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
591
+ await this._setSwapDataToCache(this.market.addresses.borrowed_token, this.market.addresses.collateral_token, _dDebt + _userBorrowed, slippage);
592
+ const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice } =
593
+ await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, address);
594
+ return {
595
+ totalCollateral: formatUnits(_totalCollateral, this.market.collateral_token.decimals),
596
+ userCollateral: formatUnits(_userCollateral, this.market.collateral_token.decimals),
597
+ collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.market.collateral_token.decimals),
598
+ collateralFromDebt: formatUnits(_collateralFromDebt, this.market.collateral_token.decimals),
599
+ avgPrice,
600
+ }
601
+ }
602
+
603
+ public async leverageBorrowMorePriceImpact(userBorrowed: TAmount, dDebt: TAmount): Promise<string> {
604
+ this._checkLeverageZap();
605
+ const _dDebt = parseUnits(dDebt, this.market.borrowed_token.decimals);
606
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
607
+ return this._getSwapDataFromCache(this.market.addresses.borrowed_token, _dDebt + _userBorrowed).priceImpact.toString();
608
+ }
609
+
610
+ public async leverageBorrowMoreBands(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): Promise<[number, number]> {
611
+ address = _getAddress.call(this.llamalend, address);
612
+ this._checkLeverageZap();
613
+ const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, dDebt, -1, address);
614
+
615
+ return [Number(_n2), Number(_n1)];
616
+ }
617
+
618
+ public async leverageBorrowMorePrices(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): Promise<string[]> {
619
+ address = _getAddress.call(this.llamalend, address);
620
+ this._checkLeverageZap();
621
+ const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, dDebt, -1, address);
622
+
623
+ return await this.market.prices.getPrices(_n2, _n1);
624
+ }
625
+
626
+ public async leverageBorrowMoreHealth(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full = true, address = ""): Promise<string> {
627
+ this._checkLeverageZap();
628
+ address = _getAddress.call(this.llamalend, address);
629
+ return await this._leverageHealth(userCollateral, userBorrowed, dDebt, -1, full, address);
630
+ }
631
+
632
+ public async leverageBorrowMoreRouteImage(userBorrowed: TAmount, debt: TAmount): Promise<string> {
633
+ this._checkLeverageZap();
634
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
635
+ const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
636
+
637
+ return this._getSwapDataFromCache(this.market.addresses.borrowed_token, _debt + _userBorrowed).pathVizImage;
638
+ }
639
+
640
+ private async _leverageBorrowMore(
641
+ userCollateral: TAmount,
642
+ userBorrowed: TAmount,
643
+ debt: TAmount,
644
+ slippage: number,
645
+ estimateGas: boolean
646
+ ): Promise<string | TGas> {
647
+ if (!(await this.market.userPosition.userLoanExists())) throw Error("Loan does not exist");
648
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
649
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
650
+ const _debt = parseUnits(debt, this.market.borrowed_token.decimals);
651
+ const swapData = this._getSwapDataFromCache(this.market.addresses.borrowed_token, _debt + _userBorrowed);
652
+ if (slippage !== swapData.slippage) throw Error(`You must call leverage.borrowMoreExpectedCollateral() with slippage=${slippage} first`)
653
+ const calldata = await _assembleTxOdos.call(this.llamalend, swapData.pathId as string);
654
+ const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
655
+ const gas = await contract.borrow_more_extended.estimateGas(
656
+ _userCollateral,
657
+ _debt,
658
+ this.llamalend.constants.ALIASES.leverage_zap,
659
+ [0, parseUnits(this._getMarketId(), 0), _userBorrowed],
660
+ calldata,
661
+ { ...this.llamalend.constantOptions }
662
+ );
663
+ if (estimateGas) return smartNumber(gas);
664
+
665
+ await this.llamalend.updateFeeData();
666
+ const gasLimit = _mulBy1_3(DIGas(gas));
667
+
668
+ return (await contract.borrow_more_extended(
669
+ _userCollateral,
670
+ _debt,
671
+ this.llamalend.constants.ALIASES.leverage_zap,
672
+ [0, parseUnits(this._getMarketId(), 0), _userBorrowed],
673
+ calldata,
674
+ { ...this.llamalend.options, gasLimit }
675
+ )).hash
676
+ }
677
+
678
+ public async leverageBorrowMoreEstimateGas(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage = 0.1): Promise<number> {
679
+ this._checkLeverageZap();
680
+ if (!(await this.leverageCreateLoanIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation");
681
+ return await this._leverageBorrowMore(userCollateral, userBorrowed, debt, slippage, true) as number;
682
+ }
683
+
684
+ public async leverageBorrowMore(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage = 0.1): Promise<string> {
685
+ this._checkLeverageZap();
686
+ await this.leverageCreateLoanApprove(userCollateral, userBorrowed);
687
+ return await this._leverageBorrowMore(userCollateral, userBorrowed, debt, slippage, false) as string;
688
+ }
689
+
690
+ // ---------------- LEVERAGE REPAY ----------------
691
+
692
+ private _leverageRepayExpectedBorrowed = (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount):
693
+ { _totalBorrowed: bigint, _borrowedFromStateCollateral: bigint, _borrowedFromUserCollateral: bigint, avgPrice: string } => {
694
+ this._checkLeverageZap();
695
+ const _stateCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals);
696
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
697
+ let _borrowedExpected = BigInt(0);
698
+ let _borrowedFromStateCollateral = BigInt(0);
699
+ let _borrowedFromUserCollateral = BigInt(0);
700
+ if (_stateCollateral + _userCollateral > BigInt(0)) {
701
+ _borrowedExpected = BigInt(this._getSwapDataFromCache(this.market.addresses.collateral_token, _stateCollateral + _userCollateral).outAmounts[0]);
702
+ _borrowedFromStateCollateral = _stateCollateral * BigInt(10 ** 18) / (_stateCollateral + _userCollateral) * _borrowedExpected / BigInt(10 ** 18);
703
+ _borrowedFromUserCollateral = _borrowedExpected - _borrowedFromStateCollateral;
704
+ }
705
+ const _totalBorrowed = _borrowedExpected + parseUnits(userBorrowed, this.market.borrowed_token.decimals);
706
+ const avgPrice = toBN(_borrowedExpected, this.market.borrowed_token.decimals).div(toBN(_stateCollateral + _userCollateral, this.market.collateral_token.decimals)).toString();
707
+
708
+ return { _totalBorrowed, _borrowedFromStateCollateral, _borrowedFromUserCollateral, avgPrice }
709
+ };
710
+
711
+ public leverageRepayExpectedBorrowed = async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1):
712
+ Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string, avgPrice: string }> => {
713
+ this._checkLeverageZap();
714
+ const _stateCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals);
715
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
716
+ if (_stateCollateral + _userCollateral > BigInt(0)) {
717
+ await this._setSwapDataToCache(this.market.addresses.collateral_token, this.market.addresses.borrowed_token, _stateCollateral + _userCollateral, slippage);
718
+ }
719
+ const { _totalBorrowed, _borrowedFromStateCollateral, _borrowedFromUserCollateral, avgPrice } =
720
+ this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed);
721
+
722
+ return {
723
+ totalBorrowed: formatUnits(_totalBorrowed, this.market.borrowed_token.decimals),
724
+ borrowedFromStateCollateral: formatUnits(_borrowedFromStateCollateral, this.market.borrowed_token.decimals),
725
+ borrowedFromUserCollateral: formatUnits(_borrowedFromUserCollateral, this.market.borrowed_token.decimals),
726
+ userBorrowed: formatNumber(userBorrowed, this.market.borrowed_token.decimals),
727
+ avgPrice,
728
+ }
729
+ };
730
+
731
+ public async leverageRepayPriceImpact(stateCollateral: TAmount, userCollateral: TAmount): Promise<string> {
732
+ this._checkLeverageZap();
733
+ const _stateCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals);
734
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
735
+ if (_stateCollateral + _userCollateral > BigInt(0)) {
736
+ return this._getSwapDataFromCache(this.market.addresses.collateral_token, _stateCollateral + _userCollateral).priceImpact.toString();
737
+ } else {
738
+ return "0.0"
739
+ }
740
+ }
741
+
742
+ public async leverageRepayIsFull(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<boolean> {
743
+ this._checkLeverageZap();
744
+ address = _getAddress.call(this.llamalend, address);
745
+ const { _borrowed: _stateBorrowed, _debt } = await this.market.userPosition.userStateBigInt(address);
746
+ const { _totalBorrowed } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed);
747
+
748
+ return _stateBorrowed + _totalBorrowed > _debt;
749
+ }
750
+
751
+ public async leverageRepayIsAvailable(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<boolean> {
752
+ // 0. const { collateral, stablecoin, debt } = await this.market.userPosition.userState(address);
753
+ // 1. maxCollateral for deleverage is collateral from line above.
754
+ // 2. If user is underwater (stablecoin > 0), only full repayment is available:
755
+ // await this.deleverageRepayStablecoins(deleverageCollateral) + stablecoin > debt
756
+ this._checkLeverageZap();
757
+ address = _getAddress.call(this.llamalend, address);
758
+ const { collateral, borrowed, debt } = await this.market.userPosition.userState(address);
759
+ // Loan does not exist
760
+ if (BN(debt).eq(0)) return false;
761
+ // Can't spend more than user has
762
+ if (BN(stateCollateral).gt(collateral)) return false;
763
+ // Only full repayment and closing the position is available if user is underwater+
764
+ if (BN(borrowed).gt(0)) return await this.leverageRepayIsFull(stateCollateral, userCollateral, userBorrowed, address);
765
+
766
+ return true;
767
+ }
768
+
769
+ private _leverageRepayBands = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address: string): Promise<[bigint, bigint]> => {
770
+ address = _getAddress.call(this.llamalend, address);
771
+ if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return [parseUnits(0, 0), parseUnits(0, 0)];
772
+
773
+ const _stateRepayCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals);
774
+ const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this.market.userPosition.userStateBigInt(address);
775
+ if (_stateDebt == BigInt(0)) throw Error(`Loan for ${address} does not exist`);
776
+ if (_stateCollateral < _stateRepayCollateral) throw Error(`Can't use more collateral than user's position has (${_stateRepayCollateral}) > ${_stateCollateral})`);
777
+
778
+ let _n1 = parseUnits(0, 0);
779
+ let _n2 = parseUnits(0, 0);
780
+ const { _totalBorrowed: _repayExpected } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed);
781
+ try {
782
+ _n1 = await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N);
783
+ _n2 = _n1 + (_N - BigInt(1));
784
+ } catch {
785
+ console.log("Full repayment");
786
+ }
787
+
788
+ return [_n2, _n1];
789
+ },
790
+ {
791
+ promise: true,
792
+ maxAge: 5 * 60 * 1000, // 5m
793
+ });
794
+
795
+ public async leverageRepayBands(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<[number, number]> {
796
+ this._checkLeverageZap();
797
+ const [_n2, _n1] = await this._leverageRepayBands(stateCollateral, userCollateral, userBorrowed, address);
798
+
799
+ return [Number(_n2), Number(_n1)];
800
+ }
801
+
802
+ public async leverageRepayPrices(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<string[]> {
803
+ this._checkLeverageZap();
804
+ const [_n2, _n1] = await this._leverageRepayBands(stateCollateral, userCollateral, userBorrowed, address);
805
+
806
+ return await this.market.prices.getPrices(_n2, _n1);
807
+ }
808
+
809
+ public async leverageRepayHealth(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, full = true, address = ""): Promise<string> {
810
+ this._checkLeverageZap();
811
+ address = _getAddress.call(this.llamalend, address);
812
+ const { _borrowed: _stateBorrowed, _debt, _N } = await this.market.userPosition.userStateBigInt(address);
813
+ if (_stateBorrowed > BigInt(0)) return "0.0";
814
+ if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return "0.0";
815
+
816
+ const { _totalBorrowed } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed);
817
+ const _dCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals) * BigInt(-1);
818
+ const _dDebt = _totalBorrowed * BigInt(-1);
819
+
820
+ if (_debt + _dDebt <= BigInt(0)) return "0.0";
821
+ const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
822
+ let _health = await contract.health_calculator(address, _dCollateral, _dDebt, full, _N, this.llamalend.constantOptions) as bigint;
823
+ _health = _health * BigInt(100);
824
+
825
+ return this.llamalend.formatUnits(_health);
826
+ }
827
+
828
+ public async leverageRepayIsApproved(userCollateral: TAmount, userBorrowed: TAmount): Promise<boolean> {
829
+ this._checkLeverageZap();
830
+ return await hasAllowance.call(this.llamalend,
831
+ [this.market.collateral_token.address, this.market.borrowed_token.address],
832
+ [userCollateral, userBorrowed],
833
+ this.llamalend.signerAddress,
834
+ this.llamalend.constants.ALIASES.leverage_zap
835
+ );
836
+ }
837
+
838
+ public async leverageRepayApproveEstimateGas (userCollateral: TAmount, userBorrowed: TAmount): Promise<TGas> {
839
+ this._checkLeverageZap();
840
+ return await ensureAllowanceEstimateGas.call(this.llamalend,
841
+ [this.market.collateral_token.address, this.market.borrowed_token.address],
842
+ [userCollateral, userBorrowed],
843
+ this.llamalend.constants.ALIASES.leverage_zap
844
+ );
845
+ }
846
+
847
+ public async leverageRepayApprove(userCollateral: TAmount, userBorrowed: TAmount): Promise<string[]> {
848
+ this._checkLeverageZap();
849
+ return await ensureAllowance.call(this.llamalend,
850
+ [this.market.collateral_token.address, this.market.borrowed_token.address],
851
+ [userCollateral, userBorrowed],
852
+ this.llamalend.constants.ALIASES.leverage_zap
853
+ );
854
+ }
855
+
856
+ public async leverageRepayRouteImage(stateCollateral: TAmount, userCollateral: TAmount): Promise<string> {
857
+ this._checkLeverageZap();
858
+ const _stateCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals);
859
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
860
+
861
+ return this._getSwapDataFromCache(this.market.addresses.collateral_token, _stateCollateral + _userCollateral).pathVizImage;
862
+ }
863
+
864
+ private async _leverageRepay(
865
+ stateCollateral: TAmount,
866
+ userCollateral: TAmount,
867
+ userBorrowed: TAmount,
868
+ slippage: number,
869
+ estimateGas: boolean
870
+ ): Promise<string | TGas> {
871
+ if (!(await this.market.userPosition.userLoanExists())) throw Error("Loan does not exist");
872
+ const _stateCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals);
873
+ const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals);
874
+ const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals);
875
+ let calldata = "0x";
876
+ if (_stateCollateral + _userCollateral > BigInt(0)) {
877
+ const swapData = this._getSwapDataFromCache(this.market.addresses.collateral_token, _stateCollateral + _userCollateral);
878
+ if (slippage !== swapData.slippage) throw Error(`You must call leverage.repayExpectedBorrowed() with slippage=${slippage} first`)
879
+ calldata = await _assembleTxOdos.call(this.llamalend, swapData.pathId as string);
880
+ }
881
+
882
+ const contract = this.llamalend.contracts[this.market.addresses.controller].contract;
883
+ const gas = await contract.repay_extended.estimateGas(
884
+ this.llamalend.constants.ALIASES.leverage_zap,
885
+ [0, parseUnits(this._getMarketId(), 0), _userCollateral, _userBorrowed],
886
+ calldata
887
+ );
888
+ if (estimateGas) return smartNumber(gas);
889
+
890
+ await this.llamalend.updateFeeData();
891
+ const gasLimit = _mulBy1_3(DIGas(gas));
892
+
893
+ return (await contract.repay_extended(
894
+ this.llamalend.constants.ALIASES.leverage_zap,
895
+ [0, parseUnits(this._getMarketId(), 0), _userCollateral, _userBorrowed],
896
+ calldata,
897
+ { ...this.llamalend.options, gasLimit }
898
+ )).hash
899
+ }
900
+
901
+ public async leverageRepayEstimateGas(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1): Promise<number> {
902
+ this._checkLeverageZap();
903
+ if (!(await this.leverageRepayIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation");
904
+ return await this._leverageRepay(stateCollateral, userCollateral, userBorrowed, slippage, true) as number;
905
+ }
906
+
907
+ public async leverageRepay(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1): Promise<string> {
908
+ this._checkLeverageZap();
909
+ await this.leverageRepayApprove(userCollateral, userBorrowed);
910
+ return await this._leverageRepay(stateCollateral, userCollateral, userBorrowed, slippage, false) as string;
911
+ }
912
+ }