@curvefi/llamalend-api 1.0.2

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 (125) hide show
  1. package/.eslintrc.json +40 -0
  2. package/.github/workflows/lint.yml +15 -0
  3. package/.github/workflows/publish.yml +55 -0
  4. package/LICENSE +21 -0
  5. package/README.md +1976 -0
  6. package/lib/cache/index.d.ts +14 -0
  7. package/lib/cache/index.js +31 -0
  8. package/lib/constants/L2Networks.d.ts +1 -0
  9. package/lib/constants/L2Networks.js +1 -0
  10. package/lib/constants/abis/Controller.json +1027 -0
  11. package/lib/constants/abis/ERC20.json +222 -0
  12. package/lib/constants/abis/ERC4626.json +1674 -0
  13. package/lib/constants/abis/GaugeController.json +794 -0
  14. package/lib/constants/abis/GaugeFactoryMainnet.json +1 -0
  15. package/lib/constants/abis/GaugeFactorySidechain.json +475 -0
  16. package/lib/constants/abis/GaugeV5.json +958 -0
  17. package/lib/constants/abis/LeverageZap.json +35 -0
  18. package/lib/constants/abis/Llamma.json +984 -0
  19. package/lib/constants/abis/Minter.json +1 -0
  20. package/lib/constants/abis/MonetaryPolicy.json +221 -0
  21. package/lib/constants/abis/OneWayLendingFactoryABI.json +899 -0
  22. package/lib/constants/abis/SidechainGauge.json +939 -0
  23. package/lib/constants/abis/Vault.json +721 -0
  24. package/lib/constants/abis/crvUSD/DeleverageZap.json +248 -0
  25. package/lib/constants/abis/crvUSD/Factory.json +514 -0
  26. package/lib/constants/abis/crvUSD/HealthCalculatorZap.json +54 -0
  27. package/lib/constants/abis/crvUSD/LeverageZap.json +312 -0
  28. package/lib/constants/abis/crvUSD/MonetaryPolicy.json +294 -0
  29. package/lib/constants/abis/crvUSD/MonetaryPolicy2.json +299 -0
  30. package/lib/constants/abis/crvUSD/PegKeeper.json +411 -0
  31. package/lib/constants/abis/crvUSD/controller.json +991 -0
  32. package/lib/constants/abis/crvUSD/llamma.json +984 -0
  33. package/lib/constants/abis/gas_oracle_optimism.json +149 -0
  34. package/lib/constants/abis/gas_oracle_optimism_blob.json +203 -0
  35. package/lib/constants/aliases.d.ts +16 -0
  36. package/lib/constants/aliases.js +124 -0
  37. package/lib/constants/coins.d.ts +16 -0
  38. package/lib/constants/coins.js +24 -0
  39. package/lib/constants/llammas.d.ts +2 -0
  40. package/lib/constants/llammas.js +96 -0
  41. package/lib/constants/utils.d.ts +4 -0
  42. package/lib/constants/utils.js +27 -0
  43. package/lib/external-api.d.ts +13 -0
  44. package/lib/external-api.js +436 -0
  45. package/lib/index.d.ts +104 -0
  46. package/lib/index.js +123 -0
  47. package/lib/interfaces.d.ts +228 -0
  48. package/lib/interfaces.js +1 -0
  49. package/lib/lendMarkets/LendMarketTemplate.d.ts +510 -0
  50. package/lib/lendMarkets/LendMarketTemplate.js +4682 -0
  51. package/lib/lendMarkets/index.d.ts +3 -0
  52. package/lib/lendMarkets/index.js +3 -0
  53. package/lib/lendMarkets/lendMarketConstructor.d.ts +2 -0
  54. package/lib/lendMarkets/lendMarketConstructor.js +6 -0
  55. package/lib/llamalend.d.ts +80 -0
  56. package/lib/llamalend.js +878 -0
  57. package/lib/mintMarkets/MintMarketTemplate.d.ts +308 -0
  58. package/lib/mintMarkets/MintMarketTemplate.js +2998 -0
  59. package/lib/mintMarkets/index.d.ts +3 -0
  60. package/lib/mintMarkets/index.js +3 -0
  61. package/lib/mintMarkets/mintMarketConstructor.d.ts +2 -0
  62. package/lib/mintMarkets/mintMarketConstructor.js +4 -0
  63. package/lib/st-crvUSD.d.ts +35 -0
  64. package/lib/st-crvUSD.js +505 -0
  65. package/lib/utils.d.ts +58 -0
  66. package/lib/utils.js +661 -0
  67. package/package.json +42 -0
  68. package/src/cache/index.ts +41 -0
  69. package/src/constants/L2Networks.ts +1 -0
  70. package/src/constants/abis/Controller.json +1027 -0
  71. package/src/constants/abis/ERC20.json +222 -0
  72. package/src/constants/abis/ERC4626.json +1674 -0
  73. package/src/constants/abis/GaugeController.json +794 -0
  74. package/src/constants/abis/GaugeFactoryMainnet.json +1 -0
  75. package/src/constants/abis/GaugeFactorySidechain.json +475 -0
  76. package/src/constants/abis/GaugeV5.json +958 -0
  77. package/src/constants/abis/LeverageZap.json +35 -0
  78. package/src/constants/abis/Llamma.json +984 -0
  79. package/src/constants/abis/Minter.json +1 -0
  80. package/src/constants/abis/MonetaryPolicy.json +221 -0
  81. package/src/constants/abis/OneWayLendingFactoryABI.json +899 -0
  82. package/src/constants/abis/SidechainGauge.json +939 -0
  83. package/src/constants/abis/Vault.json +721 -0
  84. package/src/constants/abis/crvUSD/DeleverageZap.json +248 -0
  85. package/src/constants/abis/crvUSD/ERC20.json +222 -0
  86. package/src/constants/abis/crvUSD/Factory.json +514 -0
  87. package/src/constants/abis/crvUSD/HealthCalculatorZap.json +54 -0
  88. package/src/constants/abis/crvUSD/LeverageZap.json +312 -0
  89. package/src/constants/abis/crvUSD/MonetaryPolicy.json +294 -0
  90. package/src/constants/abis/crvUSD/MonetaryPolicy2.json +299 -0
  91. package/src/constants/abis/crvUSD/PegKeeper.json +411 -0
  92. package/src/constants/abis/crvUSD/controller.json +991 -0
  93. package/src/constants/abis/crvUSD/llamma.json +984 -0
  94. package/src/constants/abis/gas_oracle_optimism.json +149 -0
  95. package/src/constants/abis/gas_oracle_optimism_blob.json +203 -0
  96. package/src/constants/aliases.ts +141 -0
  97. package/src/constants/coins.ts +41 -0
  98. package/src/constants/llammas.ts +99 -0
  99. package/src/constants/utils.ts +33 -0
  100. package/src/external-api.ts +325 -0
  101. package/src/index.ts +128 -0
  102. package/src/interfaces.ts +237 -0
  103. package/src/lendMarkets/LendMarketTemplate.ts +3022 -0
  104. package/src/lendMarkets/index.ts +7 -0
  105. package/src/lendMarkets/lendMarketConstructor.ts +7 -0
  106. package/src/llamalend.ts +785 -0
  107. package/src/mintMarkets/MintMarketTemplate.ts +1781 -0
  108. package/src/mintMarkets/index.ts +7 -0
  109. package/src/mintMarkets/mintMarketConstructor.ts +5 -0
  110. package/src/st-crvUSD.ts +244 -0
  111. package/src/utils.ts +497 -0
  112. package/test/fetch.test.ts +152 -0
  113. package/test/general.test.ts +216 -0
  114. package/test/leverageBorrowMore.test.ts +245 -0
  115. package/test/leverageCreateLoan.test.ts +236 -0
  116. package/test/leverageRepay.test.ts +240 -0
  117. package/test/readme.test.ts +475 -0
  118. package/test/selfLiquidate.test.ts +57 -0
  119. package/test/selfLiquidateCrvUSD.test.ts +54 -0
  120. package/test/st_crvUSD.test.ts +68 -0
  121. package/test/swap.test.ts +62 -0
  122. package/test/swapCrvUSD.test.ts +56 -0
  123. package/test/vault.test.ts +112 -0
  124. package/tsconfig.build.json +10 -0
  125. package/tsconfig.json +72 -0
@@ -0,0 +1,785 @@
1
+ import { ethers,
2
+ Contract, Networkish, BigNumberish, Numeric, AbstractProvider } from "ethers";
3
+ import { Provider as MulticallProvider, Contract as MulticallContract, Call } from '@curvefi/ethcall';
4
+ import {
5
+ IChainId,
6
+ ILlamalend,
7
+ ILlamma,
8
+ IDict,
9
+ INetworkName,
10
+ ICurveContract,
11
+ IOneWayMarket,
12
+ ICoin,
13
+ IMarketDataAPI,
14
+ } from "./interfaces.js";
15
+ // OneWayMarket ABIs
16
+ import OneWayLendingFactoryABI from "./constants/abis/OneWayLendingFactoryABI.json" assert { type: 'json' };
17
+ import ERC20ABI from './constants/abis/ERC20.json' assert { type: 'json' };
18
+ import ERC4626ABI from './constants/abis/ERC4626.json' assert { type: 'json' };
19
+ import LlammaABI from './constants/abis/Llamma.json' assert { type: 'json' };
20
+ import ControllerABI from './constants/abis/Controller.json' assert { type: 'json' };
21
+ import MonetaryPolicyABI from './constants/abis/MonetaryPolicy.json' assert { type: 'json' };
22
+ import VaultABI from './constants/abis/Vault.json' assert { type: 'json' };
23
+ import GaugeABI from './constants/abis/GaugeV5.json' assert { type: 'json' };
24
+ import SidechainGaugeABI from './constants/abis/SidechainGauge.json' assert { type: 'json' };
25
+ import GaugeControllerABI from './constants/abis/GaugeController.json' assert { type: 'json' };
26
+ import GaugeFactoryMainnetABI from './constants/abis/GaugeFactoryMainnet.json' assert { type: 'json' };
27
+ import GaugeFactorySidechainABI from './constants/abis/GaugeFactorySidechain.json' assert { type: 'json' };
28
+ import MinterABI from './constants/abis/Minter.json' assert { type: 'json' };
29
+ import LeverageZapABI from './constants/abis/LeverageZap.json' assert { type: 'json' };
30
+ import gasOracleABI from './constants/abis/gas_oracle_optimism.json' assert { type: 'json'};
31
+ import gasOracleBlobABI from './constants/abis/gas_oracle_optimism_blob.json' assert { type: 'json'};
32
+ // crvUSD ABIs
33
+ import llammaABI from "./constants/abis/crvUSD/llamma.json" assert { type: 'json'};
34
+ import controllerABI from "./constants/abis/crvUSD/controller.json" assert { type: 'json'};
35
+ import PegKeeper from "./constants/abis/crvUSD/PegKeeper.json" assert { type: 'json'};
36
+ import FactoryABI from "./constants/abis/crvUSD/Factory.json" assert { type: 'json'};
37
+ import MonetaryPolicy2ABI from "./constants/abis/crvUSD/MonetaryPolicy2.json" assert { type: 'json'};
38
+ import HealthCalculatorZapABI from "./constants/abis/crvUSD/HealthCalculatorZap.json" assert { type: 'json'};
39
+ import LeverageZapCrvUSDABI from "./constants/abis/crvUSD/LeverageZap.json" assert { type: 'json'};
40
+ import DeleverageZapABI from "./constants/abis/crvUSD/DeleverageZap.json" assert { type: 'json'};
41
+
42
+ import {
43
+ ALIASES_ETHEREUM,
44
+ ALIASES_OPTIMISM,
45
+ ALIASES_POLYGON,
46
+ ALIASES_FANTOM,
47
+ ALIASES_AVALANCHE,
48
+ ALIASES_ARBITRUM,
49
+ ALIASES_XDAI,
50
+ ALIASES_MOONBEAM,
51
+ ALIASES_AURORA,
52
+ ALIASES_KAVA,
53
+ ALIASES_CELO,
54
+ ALIASES_ZKSYNC,
55
+ ALIASES_BASE,
56
+ ALIASES_BSC,
57
+ ALIASES_FRAXTAL,
58
+ ALIASES_SONIC,
59
+ } from "./constants/aliases.js";
60
+ import {
61
+ COINS_ETHEREUM,
62
+ COINS_OPTIMISM,
63
+ COINS_POLYGON,
64
+ COINS_FANTOM,
65
+ COINS_AVALANCHE,
66
+ COINS_ARBITRUM,
67
+ COINS_XDAI,
68
+ COINS_MOONBEAM,
69
+ COINS_AURORA,
70
+ COINS_KAVA,
71
+ COINS_CELO,
72
+ COINS_ZKSYNC,
73
+ COINS_BASE,
74
+ COINS_BSC,
75
+ COINS_FRAXTAL,
76
+ COINS_SONIC,
77
+ } from "./constants/coins.js";
78
+ import { LLAMMAS } from "./constants/llammas";
79
+ import { L2Networks } from "./constants/L2Networks.js";
80
+ import { createCall, handleMultiCallResponse} from "./utils.js";
81
+ import {cacheKey, cacheStats} from "./cache/index.js";
82
+ import {_getMarketsData} from "./external-api.js";
83
+ import { extractDecimals } from "./constants/utils.js";
84
+
85
+ export const NETWORK_CONSTANTS: { [index: number]: any } = {
86
+ 1: {
87
+ NAME: 'ethereum',
88
+ ALIASES: ALIASES_ETHEREUM,
89
+ COINS: COINS_ETHEREUM,
90
+ EXCLUDED_PROTOCOLS_1INCH: "CURVE_V2_LLAMMA",
91
+ },
92
+ 10: {
93
+ NAME: 'optimism',
94
+ ALIASES: ALIASES_OPTIMISM,
95
+ COINS: COINS_OPTIMISM,
96
+ },
97
+ 56: {
98
+ NAME: 'bsc',
99
+ ALIASES: ALIASES_BSC,
100
+ COINS: COINS_BSC,
101
+ },
102
+ 100: {
103
+ NAME: 'xdai',
104
+ ALIASES: ALIASES_XDAI,
105
+ COINS: COINS_XDAI,
106
+ },
107
+ 137: {
108
+ NAME: 'polygon',
109
+ ALIASES: ALIASES_POLYGON,
110
+ COINS: COINS_POLYGON,
111
+ },
112
+ 146: {
113
+ NAME: 'sonic',
114
+ ALIASES: ALIASES_SONIC,
115
+ COINS: COINS_SONIC,
116
+ },
117
+ 250: {
118
+ NAME: 'fantom',
119
+ ALIASES: ALIASES_FANTOM,
120
+ COINS: COINS_FANTOM,
121
+ },
122
+ 252: {
123
+ NAME: 'fraxtal',
124
+ ALIASES: ALIASES_FRAXTAL,
125
+ COINS: COINS_FRAXTAL,
126
+ },
127
+ 324: {
128
+ NAME: 'zksync',
129
+ ALIASES: ALIASES_ZKSYNC,
130
+ COINS: COINS_ZKSYNC,
131
+ },
132
+ 1284: {
133
+ NAME: 'moonbeam',
134
+ ALIASES: ALIASES_MOONBEAM,
135
+ COINS: COINS_MOONBEAM,
136
+ },
137
+ 2222: {
138
+ NAME: 'kava',
139
+ ALIASES: ALIASES_KAVA,
140
+ COINS: COINS_KAVA,
141
+ },
142
+ 8453: {
143
+ NAME: 'base',
144
+ ALIASES: ALIASES_BASE,
145
+ COINS: COINS_BASE,
146
+ },
147
+ 42161: {
148
+ NAME: 'arbitrum',
149
+ ALIASES: ALIASES_ARBITRUM,
150
+ COINS: COINS_ARBITRUM,
151
+ EXCLUDED_PROTOCOLS_1INCH: "",
152
+ },
153
+ 42220: {
154
+ NAME: 'celo',
155
+ ALIASES: ALIASES_CELO,
156
+ COINS: COINS_CELO,
157
+ },
158
+ 43114: {
159
+ NAME: 'avalanche',
160
+ ALIASES: ALIASES_AVALANCHE,
161
+ COINS: COINS_AVALANCHE,
162
+ },
163
+ 1313161554: {
164
+ NAME: 'aurora',
165
+ ALIASES: ALIASES_AURORA,
166
+ COINS: COINS_AURORA,
167
+ },
168
+ }
169
+
170
+
171
+ class Llamalend implements ILlamalend {
172
+ address: string;
173
+ provider: ethers.BrowserProvider | ethers.JsonRpcProvider;
174
+ multicallProvider: MulticallProvider;
175
+ signer: ethers.Signer | null;
176
+ signerAddress: string;
177
+ chainId: IChainId;
178
+ contracts: { [index: string]: ICurveContract };
179
+ feeData: { gasPrice?: number, maxFeePerGas?: number, maxPriorityFeePerGas?: number };
180
+ constantOptions: { gasLimit: number };
181
+ options: { gasPrice?: number | bigint, maxFeePerGas?: number | bigint, maxPriorityFeePerGas?: number | bigint };
182
+ L1WeightedGasPrice?: number;
183
+ constants: {
184
+ ONE_WAY_MARKETS: IDict<IOneWayMarket>,
185
+ DECIMALS: IDict<number>;
186
+ NETWORK_NAME: INetworkName;
187
+ ALIASES: Record<string, string>;
188
+ COINS: Record<string, string>;
189
+ ZERO_ADDRESS: string,
190
+ EXCLUDED_PROTOCOLS_1INCH: string,
191
+ LLAMMAS: IDict<ILlamma>,
192
+ FACTORY: string,
193
+ PEG_KEEPERS: string[],
194
+ WETH: string,
195
+ };
196
+
197
+ constructor() {
198
+ this.address = '00000'//COINS.lending.toLowerCase();
199
+ // @ts-ignore
200
+ this.provider = null;
201
+ this.signer = null;
202
+ this.signerAddress = "";
203
+ this.chainId = 1;
204
+ // @ts-ignore
205
+ this.multicallProvider = null;
206
+ this.contracts = {};
207
+ this.feeData = {}
208
+ this.constantOptions = { gasLimit: 12000000 }
209
+ this.options = {};
210
+ this.constants = {
211
+ ONE_WAY_MARKETS: {},
212
+ LLAMMAS,
213
+ COINS: {},
214
+ DECIMALS: {},
215
+ NETWORK_NAME: 'ethereum',
216
+ ALIASES: {},
217
+ ZERO_ADDRESS: '0x000',
218
+ EXCLUDED_PROTOCOLS_1INCH: "",
219
+ FACTORY: "0xC9332fdCB1C491Dcc683bAe86Fe3cb70360738BC".toLowerCase(),
220
+ PEG_KEEPERS: [
221
+ '0x9201da0d97caaaff53f01b2fb56767c7072de340'.toLowerCase(),
222
+ '0xfb726f57d251ab5c731e5c64ed4f5f94351ef9f3'.toLowerCase(),
223
+ '0x3fa20eaa107de08b38a8734063d605d5842fe09c'.toLowerCase(),
224
+ '0x0a05ff644878b908ef8eb29542aa88c07d9797d3'.toLowerCase(),
225
+ '0x503E1Bf274e7a6c64152395aE8eB57ec391F91F8'.toLowerCase(),
226
+ ],
227
+ WETH: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".toLowerCase(),
228
+ };
229
+ }
230
+
231
+ async init(
232
+ providerType: 'JsonRpc' | 'Web3' | 'Infura' | 'Alchemy',
233
+ providerSettings: { url?: string, privateKey?: string, batchMaxCount? : number } | { externalProvider: ethers.Eip1193Provider } | { network?: Networkish, apiKey?: string },
234
+ options: { gasPrice?: number, maxFeePerGas?: number, maxPriorityFeePerGas?: number, chainId?: number } = {} // gasPrice in Gwei
235
+ ): Promise<void> {
236
+ // @ts-ignore
237
+ this.provider = null;
238
+ // @ts-ignore
239
+ this.signer = null;
240
+ this.signerAddress = "";
241
+ this.chainId = 1;
242
+ // @ts-ignore
243
+ this.multicallProvider = null;
244
+ this.contracts = {};
245
+ this.feeData = {}
246
+ this.constantOptions = { gasLimit: 12000000 }
247
+ this.options = {};
248
+ this.constants = {
249
+ ONE_WAY_MARKETS: {},
250
+ LLAMMAS,
251
+ COINS: {},
252
+ DECIMALS: {},
253
+ NETWORK_NAME: 'ethereum',
254
+ ALIASES: {},
255
+ ZERO_ADDRESS: ethers.ZeroAddress,
256
+ EXCLUDED_PROTOCOLS_1INCH: "",
257
+ FACTORY: "0xC9332fdCB1C491Dcc683bAe86Fe3cb70360738BC".toLowerCase(),
258
+ PEG_KEEPERS: [
259
+ '0x9201da0d97caaaff53f01b2fb56767c7072de340'.toLowerCase(),
260
+ '0xfb726f57d251ab5c731e5c64ed4f5f94351ef9f3'.toLowerCase(),
261
+ '0x3fa20eaa107de08b38a8734063d605d5842fe09c'.toLowerCase(),
262
+ '0x0a05ff644878b908ef8eb29542aa88c07d9797d3'.toLowerCase(),
263
+ '0x503E1Bf274e7a6c64152395aE8eB57ec391F91F8'.toLowerCase(),
264
+ ],
265
+ WETH: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".toLowerCase(),
266
+ };
267
+
268
+ // JsonRpc provider
269
+ if (providerType.toLowerCase() === 'JsonRpc'.toLowerCase()) {
270
+ providerSettings = providerSettings as { url: string, privateKey: string, batchMaxCount? : number };
271
+
272
+ let jsonRpcApiProviderOptions;
273
+ if ( providerSettings.batchMaxCount ) {
274
+ jsonRpcApiProviderOptions = {
275
+ batchMaxCount: providerSettings.batchMaxCount,
276
+ };
277
+ }
278
+
279
+ if (providerSettings.url) {
280
+ this.provider = new ethers.JsonRpcProvider(providerSettings.url, undefined, jsonRpcApiProviderOptions);
281
+ } else {
282
+ this.provider = new ethers.JsonRpcProvider('http://localhost:8545/', undefined, jsonRpcApiProviderOptions);
283
+ }
284
+
285
+ if (providerSettings.privateKey) {
286
+ this.signer = new ethers.Wallet(providerSettings.privateKey, this.provider);
287
+ } else if (!providerSettings.url?.startsWith("https://rpc.gnosischain.com")) {
288
+ try {
289
+ this.signer = await this.provider.getSigner();
290
+ } catch (e) {
291
+ this.signer = null;
292
+ }
293
+ }
294
+ // Web3 provider
295
+ } else if (providerType.toLowerCase() === 'Web3'.toLowerCase()) {
296
+ providerSettings = providerSettings as { externalProvider: ethers.Eip1193Provider };
297
+ this.provider = new ethers.BrowserProvider(providerSettings.externalProvider);
298
+ this.signer = await this.provider.getSigner();
299
+ // Infura provider
300
+ } else if (providerType.toLowerCase() === 'Infura'.toLowerCase()) {
301
+ providerSettings = providerSettings as { network?: Networkish, apiKey?: string };
302
+ this.provider = new ethers.InfuraProvider(providerSettings.network, providerSettings.apiKey);
303
+ this.signer = null;
304
+ // Alchemy provider
305
+ } else if (providerType.toLowerCase() === 'Alchemy'.toLowerCase()) {
306
+ providerSettings = providerSettings as { network?: Networkish, apiKey?: string };
307
+ this.provider = new ethers.AlchemyProvider(providerSettings.network, providerSettings.apiKey);
308
+ this.signer = null;
309
+ } else {
310
+ throw Error('Wrong providerType');
311
+ }
312
+
313
+ const network = await this.provider.getNetwork();
314
+ this.chainId = Number(network.chainId) === 133 || Number(network.chainId) === 31337 ? 1 : Number(network.chainId) as IChainId;
315
+ console.log("CURVE-LENDING-JS IS CONNECTED TO NETWORK:", { name: network.name.toUpperCase(), chainId: Number(this.chainId) });
316
+
317
+ if(this.chainId === 42161) {
318
+ this.constantOptions = { gasLimit: 1125899906842624 } // https://arbiscan.io/chart/gaslimit
319
+ }
320
+
321
+ this.constants.NETWORK_NAME = NETWORK_CONSTANTS[this.chainId].NAME;
322
+ this.constants.ALIASES = NETWORK_CONSTANTS[this.chainId].ALIASES;
323
+ this.constants.COINS = NETWORK_CONSTANTS[this.chainId].COINS;
324
+ this.constants.EXCLUDED_PROTOCOLS_1INCH = NETWORK_CONSTANTS[this.chainId].EXCLUDED_PROTOCOLS_1INCH;
325
+ this.setContract(this.constants.ALIASES.crv, ERC20ABI);
326
+ this.setContract(this.constants.ALIASES.crvUSD, ERC20ABI);
327
+ this.setContract(this.constants.ALIASES.st_crvUSD, ERC4626ABI);
328
+ this.constants.DECIMALS[this.constants.ALIASES.crv] = 18;
329
+ this.constants.DECIMALS[this.constants.ALIASES.crvUSD] = 18;
330
+ this.constants.DECIMALS[this.constants.ALIASES.st_crvUSD] = 18;
331
+
332
+ this.multicallProvider = new MulticallProvider(this.chainId, this.provider);
333
+
334
+ if (this.signer) {
335
+ try {
336
+ this.signerAddress = await this.signer.getAddress();
337
+ } catch (err) {
338
+ this.signer = null;
339
+ }
340
+ } else {
341
+ this.signerAddress = '';
342
+ }
343
+
344
+ this.feeData = { gasPrice: options.gasPrice, maxFeePerGas: options.maxFeePerGas, maxPriorityFeePerGas: options.maxPriorityFeePerGas };
345
+ await this.updateFeeData();
346
+
347
+ // oneWayMarkets contracts
348
+ this.setContract(this.constants.ALIASES['one_way_factory'], OneWayLendingFactoryABI);
349
+ this.setContract(this.constants.ALIASES['gauge_controller'], GaugeControllerABI);
350
+ this.setContract(this.constants.ALIASES['leverage_zap'], LeverageZapABI);
351
+ if (this.chainId === 1) {
352
+ this.setContract(this.constants.ALIASES.minter, MinterABI);
353
+ this.setContract(this.constants.ALIASES.gauge_factory, GaugeFactoryMainnetABI);
354
+ } else {
355
+ this.constants.ALIASES.minter = this.constants.ALIASES.gauge_factory;
356
+ this.setContract(this.constants.ALIASES.gauge_factory, GaugeFactorySidechainABI);
357
+ }
358
+
359
+ // crvUSD contracts
360
+ if(this.chainId === 1) {
361
+ this.setContract(this.constants.COINS.crvusd.toLowerCase(), ERC20ABI);
362
+ for (const llamma of Object.values(this.constants.LLAMMAS)) {
363
+ this.setContract(llamma.amm_address, llammaABI);
364
+ this.setContract(llamma.controller_address, controllerABI);
365
+ const monetary_policy_address = await this.contracts[llamma.controller_address].contract.monetary_policy(this.constantOptions);
366
+ llamma.monetary_policy_address = monetary_policy_address.toLowerCase();
367
+ this.setContract(llamma.monetary_policy_address, llamma.monetary_policy_abi);
368
+ if (llamma.collateral_address === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") {
369
+ this.setContract(this.constants.WETH, ERC20ABI);
370
+ } else {
371
+ this.setContract(llamma.collateral_address, ERC20ABI);
372
+ }
373
+ this.setContract(llamma.leverage_zap, LeverageZapCrvUSDABI);
374
+ this.setContract(llamma.deleverage_zap, DeleverageZapABI);
375
+ if (llamma.health_calculator_zap) this.setContract(llamma.health_calculator_zap, HealthCalculatorZapABI);
376
+ }
377
+ for (const pegKeeper of this.constants.PEG_KEEPERS) {
378
+ this.setContract(pegKeeper, PegKeeper);
379
+ }
380
+ }
381
+
382
+ // TODO Put it in a separate method
383
+ // Fetch new llammas
384
+ this.setContract(this.constants.FACTORY, FactoryABI);
385
+ const factoryContract = this.contracts[this.constants.FACTORY].contract;
386
+ const factoryMulticallContract = this.contracts[this.constants.FACTORY].multicallContract;
387
+
388
+ const N1 = Object.keys(this.constants.LLAMMAS).length;
389
+ const N2 = await factoryContract.n_collaterals(this.constantOptions);
390
+ let calls = [];
391
+ for (let i = N1; i < N2; i++) {
392
+ calls.push(
393
+ factoryMulticallContract.collaterals(i),
394
+ factoryMulticallContract.amms(i),
395
+ factoryMulticallContract.controllers(i)
396
+ );
397
+ }
398
+ const res: string[] = (await this.multicallProvider.all(calls) as string[]).map((c) => c.toLowerCase());
399
+ const collaterals = res.filter((a, i) => i % 3 == 0) as string[];
400
+ const amms = res.filter((a, i) => i % 3 == 1) as string[];
401
+ const controllers = res.filter((a, i) => i % 3 == 2) as string[];
402
+
403
+ if (collaterals.length > 0) {
404
+ for (const collateral of collaterals) this.setContract(collateral, ERC20ABI);
405
+
406
+ calls = [];
407
+ for (const collateral of collaterals) {
408
+ calls.push(
409
+ this.contracts[collateral].multicallContract.symbol(),
410
+ this.contracts[collateral].multicallContract.decimals()
411
+ )
412
+ }
413
+ const res = (await this.multicallProvider.all(calls)).map((x) => {
414
+ if (typeof x === "string") return x.toLowerCase();
415
+ return x;
416
+ });
417
+
418
+ for (let i = 0; i < collaterals.length; i++) {
419
+ const is_eth = collaterals[i] === this.constants.WETH;
420
+ const [collateral_symbol, collateral_decimals] = res.splice(0, 2) as [string, number];
421
+ this.setContract(amms[i], llammaABI);
422
+ this.setContract(controllers[i], controllerABI);
423
+ const monetary_policy_address = (await this.contracts[controllers[i]].contract.monetary_policy(this.constantOptions)).toLowerCase();
424
+ this.setContract(monetary_policy_address, MonetaryPolicy2ABI);
425
+ const _llammaId: string = is_eth ? "eth" : collateral_symbol.toLowerCase();
426
+ let llammaId = _llammaId
427
+ let j = 2;
428
+ while (llammaId in this.constants.LLAMMAS) llammaId = _llammaId + j++;
429
+ this.constants.LLAMMAS[llammaId] = {
430
+ amm_address: amms[i],
431
+ controller_address: controllers[i],
432
+ monetary_policy_address,
433
+ collateral_address: is_eth ? "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" : collaterals[i],
434
+ leverage_zap: "0x0000000000000000000000000000000000000000",
435
+ deleverage_zap: "0x0000000000000000000000000000000000000000",
436
+ collateral_symbol: is_eth ? "ETH" : collateral_symbol,
437
+ collateral_decimals,
438
+ min_bands: 4,
439
+ max_bands: 50,
440
+ default_bands: 10,
441
+ A: 100,
442
+ monetary_policy_abi: MonetaryPolicy2ABI,
443
+ }
444
+ }
445
+ }
446
+
447
+ this.constants.DECIMALS = extractDecimals(this.constants.LLAMMAS);
448
+ this.constants.DECIMALS[this.address] = 18;
449
+
450
+ if(L2Networks.includes(this.chainId)) {
451
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
452
+ const lendingInstance = this;
453
+ lendingInstance.setContract(lendingInstance.constants.ALIASES.gas_oracle, gasOracleABI);
454
+ lendingInstance.setContract(lendingInstance.constants.ALIASES.gas_oracle_blob, gasOracleBlobABI);
455
+
456
+ // @ts-ignore
457
+ if(AbstractProvider.prototype.originalEstimate) {
458
+ // @ts-ignore
459
+ AbstractProvider.prototype.estimateGas = AbstractProvider.prototype.originalEstimate;
460
+ }
461
+
462
+ const originalEstimate = AbstractProvider.prototype.estimateGas;
463
+
464
+ const oldEstimate = async function(arg: any) {
465
+ // @ts-ignore
466
+ const originalEstimateFunc = originalEstimate.bind(this);
467
+
468
+ const gas = await originalEstimateFunc(arg);
469
+
470
+ return gas;
471
+ }
472
+
473
+ //Override
474
+ const newEstimate = async function(arg: any) {
475
+ // @ts-ignore
476
+ const L2EstimateGas = originalEstimate.bind(this);
477
+
478
+ const L1GasUsed = await lendingInstance.contracts[lendingInstance.constants.ALIASES.gas_oracle_blob].contract.getL1GasUsed(arg.data);
479
+ const L1Fee = await lendingInstance.contracts[lendingInstance.constants.ALIASES.gas_oracle_blob].contract.getL1Fee(arg.data);
480
+
481
+ lendingInstance.L1WeightedGasPrice = Number(L1Fee)/Number(L1GasUsed);
482
+
483
+ const L2GasUsed = await L2EstimateGas(arg);
484
+
485
+ return [L2GasUsed,L1GasUsed];
486
+ }
487
+
488
+ // @ts-ignore
489
+ AbstractProvider.prototype.estimateGas = newEstimate;
490
+ // @ts-ignore
491
+ AbstractProvider.prototype.originalEstimate = oldEstimate;
492
+ } else {
493
+ // @ts-ignore
494
+ if(AbstractProvider.prototype.originalEstimate) {
495
+ // @ts-ignore
496
+ AbstractProvider.prototype.estimateGas = AbstractProvider.prototype.originalEstimate;
497
+ }
498
+ }
499
+ }
500
+
501
+ setContract(address: string, abi: any): void {
502
+ if (address === this.constants.ZERO_ADDRESS || address === undefined) return;
503
+ this.contracts[address] = {
504
+ contract: new Contract(address, abi, this.signer || this.provider),
505
+ multicallContract: new MulticallContract(address, abi),
506
+ address: address,
507
+ abi: abi,
508
+ }
509
+ }
510
+
511
+ setCustomFeeData(customFeeData: { gasPrice?: number, maxFeePerGas?: number, maxPriorityFeePerGas?: number }): void {
512
+ this.feeData = { ...this.feeData, ...customFeeData };
513
+ }
514
+
515
+ getLendMarketList = () => Object.keys(this.constants.ONE_WAY_MARKETS);
516
+
517
+ getMintMarketList = () => Object.keys(this.constants.LLAMMAS);
518
+
519
+ getFactoryMarketData = async () => {
520
+ const factory = this.contracts[this.constants.ALIASES['one_way_factory']];
521
+ const factoryContract = this.contracts[this.constants.ALIASES['one_way_factory']].contract;
522
+ const markets_count = await factoryContract.market_count();
523
+ const callsMap = ['names', 'amms', 'controllers', 'borrowed_tokens', 'collateral_tokens', 'monetary_policies', 'vaults', 'gauges']
524
+
525
+ const calls: Call[] = [];
526
+ for (let i = 0; i < markets_count; i++) {
527
+ callsMap.forEach((item) => {
528
+ calls.push(createCall(factory,item, [i]))
529
+ })
530
+ }
531
+ const res = (await this.multicallProvider.all(calls) as string[]).map((addr) => addr.toLowerCase());
532
+
533
+ return handleMultiCallResponse(callsMap, res)
534
+ }
535
+
536
+ getFactoryMarketDataByAPI = async () => {
537
+ const apiData = (await _getMarketsData(this.constants.NETWORK_NAME)).lendingVaultData;
538
+
539
+ const result: Record<string, string[]> = {
540
+ names: [],
541
+ amms: [],
542
+ controllers: [],
543
+ borrowed_tokens: [],
544
+ collateral_tokens: [],
545
+ monetary_policies: [],
546
+ vaults: [],
547
+ gauges: [],
548
+ };
549
+
550
+ apiData.forEach((market: IMarketDataAPI) => {
551
+ result.names.push(market.name);
552
+ result.amms.push(market.ammAddress.toLowerCase());
553
+ result.controllers.push(market.controllerAddress.toLowerCase());
554
+ result.borrowed_tokens.push(market.assets.borrowed.address.toLowerCase());
555
+ result.collateral_tokens.push(market.assets.collateral.address.toLowerCase());
556
+ result.monetary_policies.push(market.monetaryPolicyAddress.toLowerCase());
557
+ result.vaults.push(market.address.toLowerCase());
558
+ result.gauges.push(market.gaugeAddress?.toLowerCase() || this.constants.ZERO_ADDRESS);
559
+ });
560
+
561
+ return result;
562
+ }
563
+
564
+ getCoins = async (collateral_tokens: string[], borrowed_tokens: string[], useApi = false): Promise<IDict<ICoin>> => {
565
+ const coins = new Set([...collateral_tokens, ...borrowed_tokens]);
566
+ const COINS_DATA: IDict<ICoin> = {};
567
+
568
+ if (useApi) {
569
+ const apiData = (await _getMarketsData(this.constants.NETWORK_NAME)).lendingVaultData;
570
+ apiData.forEach((market) => {
571
+ const borrowedCoin = market.assets.borrowed;
572
+ const collateralCoin = market.assets.collateral;
573
+
574
+ if (coins.has(borrowedCoin.address)) {
575
+ this.setContract(borrowedCoin.address, ERC20ABI);
576
+ COINS_DATA[borrowedCoin.address] = {
577
+ address: borrowedCoin.address,
578
+ decimals: borrowedCoin.decimals,
579
+ name: borrowedCoin.symbol,
580
+ symbol: borrowedCoin.symbol,
581
+ };
582
+ }
583
+
584
+ if (coins.has(collateralCoin.address)) {
585
+ this.setContract(collateralCoin.address, ERC20ABI);
586
+ COINS_DATA[collateralCoin.address] = {
587
+ address: collateralCoin.address,
588
+ decimals: collateralCoin.decimals,
589
+ name: collateralCoin.symbol,
590
+ symbol: collateralCoin.symbol,
591
+ };
592
+ }
593
+ });
594
+ } else {
595
+ const calls: Call[] = [];
596
+ const callsMap = ['name', 'decimals', 'symbol'];
597
+
598
+ coins.forEach((coin: string) => {
599
+ this.setContract(coin, ERC20ABI);
600
+ callsMap.forEach((item) => {
601
+ calls.push(createCall(this.contracts[coin], item, []));
602
+ });
603
+ });
604
+
605
+ const res = await this.multicallProvider.all(calls);
606
+ const { name, decimals, symbol } = handleMultiCallResponse(callsMap, res);
607
+
608
+ Array.from(coins).forEach((coin: string, index: number) => {
609
+ COINS_DATA[coin] = {
610
+ address: coin,
611
+ decimals: Number(decimals[index]),
612
+ name: name[index],
613
+ symbol: symbol[index],
614
+ };
615
+ });
616
+ }
617
+
618
+ return COINS_DATA;
619
+ }
620
+
621
+
622
+ fetchStats = async (amms: string[], controllers: string[], vaults: string[], borrowed_tokens: string[], collateral_tokens: string[]) => {
623
+ cacheStats.clear();
624
+
625
+ const marketCount = controllers.length;
626
+
627
+ const calls: Call[] = [];
628
+
629
+ for (let i = 0; i < marketCount; i++) {
630
+ calls.push(createCall(this.contracts[controllers[i]], 'total_debt', []));
631
+ calls.push(createCall(this.contracts[vaults[i]], 'totalAssets', [controllers[i]]));
632
+ calls.push(createCall(this.contracts[borrowed_tokens[i]], 'balanceOf', [controllers[i]]));
633
+ calls.push(createCall(this.contracts[amms[i]], 'rate', []));
634
+ calls.push(createCall(this.contracts[borrowed_tokens[i]], 'balanceOf', [amms[i]]));
635
+ calls.push(createCall(this.contracts[amms[i]], 'admin_fees_x', []));
636
+ calls.push(createCall(this.contracts[amms[i]], 'admin_fees_y', []));
637
+ calls.push(createCall(this.contracts[collateral_tokens[i]], 'balanceOf', [amms[i]]));
638
+ }
639
+
640
+ const res = await this.multicallProvider.all(calls);
641
+
642
+ for (let i = 0; i < marketCount; i++) {
643
+ cacheStats.set(cacheKey(controllers[i], 'total_debt'), res[(i * 8) + 0]);
644
+ cacheStats.set(cacheKey(vaults[i], 'totalAssets', controllers[i]), res[(i * 8) + 1]);
645
+ cacheStats.set(cacheKey(borrowed_tokens[i], 'balanceOf', controllers[i]), res[(i * 8) + 2]);
646
+ cacheStats.set(cacheKey(amms[i], 'rate'), res[(i * 8) + 3]);
647
+ cacheStats.set(cacheKey(borrowed_tokens[i], 'balanceOf', amms[i]), res[(i * 8) + 4]);
648
+ cacheStats.set(cacheKey(amms[i], 'admin_fees_x'), res[(i * 8) + 5]);
649
+ cacheStats.set(cacheKey(amms[i], 'admin_fees_y'), res[(i * 8) + 6]);
650
+ cacheStats.set(cacheKey(collateral_tokens[i], 'balanceOf', amms[i]), res[(i * 8) + 7]);
651
+ }
652
+ };
653
+
654
+
655
+ fetchLendMarkets = async (useAPI = true) => {
656
+ if(useAPI) {
657
+ await this._fetchOneWayMarketsByAPI()
658
+ } else {
659
+ await this._fetchOneWayMarketsByBlockchain()
660
+ }
661
+ }
662
+
663
+ _fetchOneWayMarketsByBlockchain = async () => {
664
+ const {names, amms, controllers, borrowed_tokens, collateral_tokens, monetary_policies, vaults, gauges} = await this.getFactoryMarketData()
665
+ const COIN_DATA = await this.getCoins(collateral_tokens, borrowed_tokens);
666
+ for (const c in COIN_DATA) {
667
+ this.constants.DECIMALS[c] = COIN_DATA[c].decimals;
668
+ }
669
+
670
+ amms.forEach((amm: string, index: number) => {
671
+ this.setContract(amm, LlammaABI);
672
+ this.setContract(controllers[index], ControllerABI);
673
+ this.setContract(monetary_policies[index], MonetaryPolicyABI);
674
+ this.setContract(vaults[index], VaultABI);
675
+ this.setContract(gauges[index], this.chainId === 1 ? GaugeABI : SidechainGaugeABI);
676
+ COIN_DATA[vaults[index]] = {
677
+ address: vaults[index],
678
+ decimals: 18,
679
+ name: "Curve Vault for " + COIN_DATA[borrowed_tokens[index]].name,
680
+ symbol: "cv" + COIN_DATA[borrowed_tokens[index]].symbol,
681
+ };
682
+ COIN_DATA[gauges[index]] = {
683
+ address: gauges[index],
684
+ decimals: 18,
685
+ name: "Curve.fi " + COIN_DATA[borrowed_tokens[index]].name + " Gauge Deposit",
686
+ symbol: "cv" + COIN_DATA[borrowed_tokens[index]].symbol + "-gauge",
687
+ };
688
+ this.constants.DECIMALS[vaults[index]] = 18;
689
+ this.constants.DECIMALS[gauges[index]] = 18;
690
+ this.constants.ONE_WAY_MARKETS[`one-way-market-${index}`] = {
691
+ name: names[index],
692
+ addresses: {
693
+ amm: amms[index],
694
+ controller: controllers[index],
695
+ borrowed_token: borrowed_tokens[index],
696
+ collateral_token: collateral_tokens[index],
697
+ monetary_policy: monetary_policies[index],
698
+ vault: vaults[index],
699
+ gauge: gauges[index],
700
+ },
701
+ borrowed_token: COIN_DATA[borrowed_tokens[index]],
702
+ collateral_token: COIN_DATA[collateral_tokens[index]],
703
+ }
704
+ })
705
+
706
+ await this.fetchStats(amms, controllers, vaults, borrowed_tokens, collateral_tokens);
707
+ }
708
+
709
+ _fetchOneWayMarketsByAPI = async () => {
710
+ const {names, amms, controllers, borrowed_tokens, collateral_tokens, monetary_policies, vaults, gauges} = await this.getFactoryMarketDataByAPI()
711
+ const COIN_DATA = await this.getCoins(collateral_tokens, borrowed_tokens, true);
712
+ for (const c in COIN_DATA) {
713
+ this.constants.DECIMALS[c] = COIN_DATA[c].decimals;
714
+ }
715
+
716
+ amms.forEach((amm: string, index: number) => {
717
+ this.setContract(amms[index], LlammaABI);
718
+ this.setContract(controllers[index], ControllerABI);
719
+ this.setContract(monetary_policies[index], MonetaryPolicyABI);
720
+ this.setContract(vaults[index], VaultABI);
721
+ if(gauges[index]){
722
+ this.setContract(gauges[index], this.chainId === 1 ? GaugeABI : SidechainGaugeABI);
723
+ }
724
+ COIN_DATA[vaults[index]] = {
725
+ address: vaults[index],
726
+ decimals: 18,
727
+ name: "Curve Vault for " + COIN_DATA[borrowed_tokens[index]].name,
728
+ symbol: "cv" + COIN_DATA[borrowed_tokens[index]].symbol,
729
+ };
730
+ COIN_DATA[gauges[index]] = {
731
+ address: gauges[index],
732
+ decimals: 18,
733
+ name: "Curve.fi " + COIN_DATA[borrowed_tokens[index]].name + " Gauge Deposit",
734
+ symbol: "cv" + COIN_DATA[borrowed_tokens[index]].symbol + "-gauge",
735
+ };
736
+ this.constants.DECIMALS[vaults[index]] = 18;
737
+ this.constants.DECIMALS[gauges[index]] = 18;
738
+ this.constants.ONE_WAY_MARKETS[`one-way-market-${index}`] = {
739
+ name: names[index],
740
+ addresses: {
741
+ amm: amms[index],
742
+ controller: controllers[index],
743
+ borrowed_token: borrowed_tokens[index],
744
+ collateral_token: collateral_tokens[index],
745
+ monetary_policy: monetary_policies[index],
746
+ vault: vaults[index],
747
+ gauge: gauges[index],
748
+ },
749
+ borrowed_token: COIN_DATA[borrowed_tokens[index]],
750
+ collateral_token: COIN_DATA[collateral_tokens[index]],
751
+ }
752
+ })
753
+ }
754
+
755
+ formatUnits(value: BigNumberish, unit?: string | Numeric): string {
756
+ return ethers.formatUnits(value, unit);
757
+ }
758
+
759
+ parseUnits(value: string, unit?: string | Numeric): bigint {
760
+ return ethers.parseUnits(value, unit);
761
+ }
762
+
763
+ async updateFeeData(): Promise<void> {
764
+ const feeData = await this.provider.getFeeData();
765
+ if (feeData.maxFeePerGas === null || feeData.maxPriorityFeePerGas === null) {
766
+ delete this.options.maxFeePerGas;
767
+ delete this.options.maxPriorityFeePerGas;
768
+
769
+ this.options.gasPrice = this.feeData.gasPrice !== undefined ?
770
+ this.parseUnits(this.feeData.gasPrice.toString(), "gwei") :
771
+ (feeData.gasPrice || this.parseUnits("20", "gwei"));
772
+ } else {
773
+ delete this.options.gasPrice;
774
+
775
+ this.options.maxFeePerGas = this.feeData.maxFeePerGas !== undefined ?
776
+ this.parseUnits(this.feeData.maxFeePerGas.toString(), "gwei") :
777
+ feeData.maxFeePerGas;
778
+ this.options.maxPriorityFeePerGas = this.feeData.maxPriorityFeePerGas !== undefined ?
779
+ this.parseUnits(this.feeData.maxPriorityFeePerGas.toString(), "gwei") :
780
+ feeData.maxPriorityFeePerGas;
781
+ }
782
+ }
783
+ }
784
+
785
+ export const llamalend = new Llamalend();