@curvefi/llamalend-api 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curvefi/llamalend-api",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "JavaScript library for Curve Lending",
5
5
  "main": "lib/index.js",
6
6
  "author": "Macket",
@@ -17,26 +17,31 @@
17
17
  "build": "rm -rf lib && tsc -p tsconfig.build.json",
18
18
  "lint": "eslint src --ext .ts"
19
19
  },
20
+ "engines": {
21
+ "node": "^22.0.0"
22
+ },
20
23
  "type": "module",
21
24
  "devDependencies": {
22
- "@types/chai": "^4.2.18",
23
- "@types/memoizee": "^0.4.7",
24
- "@types/mocha": "^8.2.2",
25
- "@types/node": "^14.14.37",
26
- "@typescript-eslint/eslint-plugin": "^4.33.0",
27
- "@typescript-eslint/parser": "^4.20.0",
25
+ "@eslint/eslintrc": "^3.3.1",
26
+ "@eslint/js": "^9.23.0",
27
+ "@types/chai": "^5.2.1",
28
+ "@types/memoizee": "^0.4.12",
29
+ "@types/mocha": "^10.0.10",
30
+ "@types/node": "^22.13.13",
31
+ "@typescript-eslint/eslint-plugin": "^8.28.0",
32
+ "@typescript-eslint/parser": "^8.28.0",
28
33
  "babel-eslint": "^10.1.0",
29
- "chai": "^4.3.4",
30
- "eslint": "^7.32.0",
31
- "mocha": "^8.4.0",
32
- "typescript": "^4.5.2",
33
- "vue-eslint-parser": "^7.6.0"
34
+ "chai": "^5.2.0",
35
+ "eslint": "^9.23.0",
36
+ "globals": "^16.0.0",
37
+ "mocha": "^11.1.0",
38
+ "typescript": "^5.8.2",
39
+ "vue-eslint-parser": "^10.1.1"
34
40
  },
35
41
  "dependencies": {
36
- "axios": "^0.21.1",
37
- "bignumber.js": "^9.0.1",
38
42
  "@curvefi/ethcall": "6.0.13",
39
- "ethers": "^6.10.0",
40
- "memoizee": "^0.4.15"
43
+ "bignumber.js": "^9.1.2",
44
+ "ethers": "^6.13.5",
45
+ "memoizee": "^0.4.17"
41
46
  }
42
47
  }
@@ -1,4 +1,5 @@
1
1
  class Cache {
2
+ // eslint-disable-next-line no-use-before-define
2
3
  private static instance: Cache;
3
4
  readonly cache: Map<string, any>;
4
5
 
@@ -1,24 +1,21 @@
1
- import axios from "axios";
2
- import { ethers } from "ethers";
1
+ import {ethers} from "ethers";
3
2
  import memoize from "memoizee";
4
- import BigNumber from 'bignumber.js';
5
- import { llamalend } from "./llamalend.js";
3
+ import {llamalend} from "./llamalend.js";
6
4
  import {
5
+ IDict,
7
6
  IExtendedPoolDataFromApi,
7
+ IMarketData,
8
8
  INetworkName,
9
9
  IPoolFactory,
10
- I1inchSwapData,
11
- IDict,
12
- IMarketData,
13
10
  IQuoteOdos,
14
11
  } from "./interfaces";
15
12
 
16
13
 
17
14
  const _getPoolsFromApi = memoize(
18
15
  async (network: INetworkName, poolFactory: IPoolFactory ): Promise<IExtendedPoolDataFromApi> => {
19
- const url = `https://api.curve.fi/api/getPools/${network}/${poolFactory}`;
20
- const response = await axios.get(url, { validateStatus: () => true });
21
- return response.data.data ?? { poolData: [], tvl: 0, tvlAll: 0 };
16
+ const response = await fetch(`https://api.curve.fi/api/getPools/${network}/${poolFactory}`);
17
+ const { data } = await response.json() as { data?: IExtendedPoolDataFromApi, success: boolean };
18
+ return data ?? { poolData: [], tvl: 0, tvlAll: 0 };
22
19
  },
23
20
  {
24
21
  promise: true,
@@ -117,14 +114,16 @@ export const _getUsdPricesFromApi = async (): Promise<IDict<number>> => {
117
114
  return priceDictByMaxTvl
118
115
  }
119
116
 
117
+ type UserCollateral = { total_deposit_precise: string, total_deposit_from_user: number, total_deposit_usd_value: number }
120
118
  export const _getUserCollateral = memoize(
121
- async (network: INetworkName, controller: string, user: string): Promise<Record<string, any>> => {
119
+ async (network: INetworkName, controller: string, user: string): Promise<UserCollateral> => {
122
120
  const url = `https://prices.curve.fi/v1/lending/collateral_events/${network}/${controller}/${user}`;
123
- const response = await axios.get(url, { validateStatus: () => true });
121
+ const response = await fetch(url);
122
+ const data = await response.json() as UserCollateral;
124
123
  return {
125
- total_deposit_precise: response.data.total_deposit_precise,
126
- total_deposit_from_user: response.data.total_deposit_from_user,
127
- total_deposit_usd_value: response.data.total_deposit_usd_value,
124
+ total_deposit_precise: data.total_deposit_precise,
125
+ total_deposit_from_user: data.total_deposit_from_user,
126
+ total_deposit_usd_value: data.total_deposit_usd_value,
128
127
  }
129
128
  },
130
129
  {
@@ -136,8 +135,9 @@ export const _getUserCollateral = memoize(
136
135
  export const _getUserCollateralCrvUsd = memoize(
137
136
  async (network: INetworkName, controller: string, user: string): Promise<string> => {
138
137
  const url = `https://prices.curve.fi/v1/crvusd/collateral_events/${network}/${controller}/${user}`;
139
- const response = await axios.get(url, { validateStatus: () => true });
140
- return response.data.total_deposit;
138
+ const response = await fetch(url);
139
+ const { total_deposit } = await response.json() as { total_deposit: string };
140
+ return total_deposit;
141
141
  },
142
142
  {
143
143
  promise: true,
@@ -148,16 +148,11 @@ export const _getUserCollateralCrvUsd = memoize(
148
148
  export const _getMarketsData = memoize(
149
149
  async (network: INetworkName): Promise<IMarketData> => {
150
150
  const url = `https://api.curve.fi/api/getLendingVaults/${network}/oneway`;
151
- const response = await axios.get(
152
- url,
153
- {
154
- headers: {"accept": "application/json"},
155
- validateStatus: () => true,
156
- });
151
+ const response = await fetch(url, { headers: {"accept": "application/json"} });
157
152
  if (response.status !== 200) {
158
153
  throw Error(`Fetch error: ${response.status} ${response.statusText}`);
159
154
  }
160
- return response.data.data;
155
+ return await response.json() as IMarketData;
161
156
  },
162
157
  {
163
158
  promise: true,
@@ -177,17 +172,12 @@ export const _getQuoteOdos = async (fromToken: string, toToken: string, _amount:
177
172
  `&to_address=${ethers.getAddress(toToken)}&amount=${_amount.toString()}&slippage=${slippage}&pathVizImage=${pathVizImage}` +
178
173
  `&caller_address=${ethers.getAddress(llamalend.constants.ALIASES.leverage_zap)}&blacklist=${ethers.getAddress(blacklist)}`;
179
174
 
180
- const response = await axios.get(
181
- url,
182
- {
183
- headers: {"accept": "application/json"},
184
- validateStatus: () => true,
185
- });
175
+ const response = await fetch(url, { headers: {"accept": "application/json"} });
186
176
  if (response.status !== 200) {
187
177
  throw Error(`Odos quote error - ${response.status} ${response.statusText}`);
188
178
  }
189
-
190
- return { ...response.data, slippage };
179
+ const data = await response.json() as Omit<IQuoteOdos, 'slippage'>;
180
+ return { ...data, slippage };
191
181
  }
192
182
 
193
183
  export const _getExpectedOdos = async (fromToken: string, toToken: string, _amount: bigint, blacklist: string) => {
@@ -198,125 +188,12 @@ export const _assembleTxOdos = memoize(
198
188
  async (pathId: string): Promise<string> => {
199
189
  const url = `https://prices.curve.fi/odos/assemble?user=${ethers.getAddress(llamalend.constants.ALIASES.leverage_zap)}&path_id=${pathId}`;
200
190
 
201
- const response = await axios.get(
202
- url,
203
- {
204
- headers: {'Content-Type': 'application/json'},
205
- validateStatus: () => true,
206
- });
191
+ const response = await fetch(url, { headers: {'Content-Type': 'application/json'} });
207
192
  if (response.status !== 200) {
208
193
  throw Error(`Odos assemble error - ${response.status} ${response.statusText}`);
209
194
  }
210
-
211
- return response.data['transaction']['data'];
212
- },
213
- {
214
- promise: true,
215
- maxAge: 10 * 1000, // 10s
216
- }
217
- )
218
-
219
- export const _getSpotPriceOdos = memoize(
220
- async (fromToken: string, toToken: string): Promise<string | undefined> => {
221
- fromToken = ethers.getAddress(fromToken);
222
- toToken = ethers.getAddress(toToken);
223
- const url = `https://prices.curve.fi/odos/prices?chain_id=${llamalend.chainId}&tokens=${fromToken},${toToken}`;
224
- const response = await axios.get(
225
- url,
226
- {
227
- headers: {"accept": "application/json"},
228
- validateStatus: () => true,
229
- });
230
- if (response.status !== 200) {
231
- throw Error(`Odos spot prices error - ${response.status} ${response.statusText}`);
232
- }
233
-
234
- const pricesFromOdos = response.data.tokenPrices;
235
- const pricesFromApi: IDict<string> = {};
236
- for (const coin of [fromToken, toToken]) {
237
- if (pricesFromOdos[coin] !== 0) continue;
238
- const _pricesFromApi = await _getUsdPricesFromApi();
239
- pricesFromApi[coin] = String(_pricesFromApi[coin] || 0);
240
- }
241
- const prices = { ...pricesFromOdos, ...pricesFromApi };
242
- if (BigNumber(prices[fromToken]).eq(0) || BigNumber(prices[toToken]).eq( 0)) return undefined;
243
-
244
- return (new BigNumber(prices[toToken])).div(prices[fromToken]).toString()
245
- },
246
- {
247
- promise: true,
248
- maxAge: 10 * 1000, // 10s
249
- }
250
- )
251
-
252
- // --- 1INCH ---
253
-
254
- export const _getExpected1inch = memoize(
255
- async (fromToken: string, toToken: string, _amount: bigint): Promise<string> => {
256
- if (_amount === BigInt(0)) return "0.0";
257
- const url = `https://prices.curve.fi/1inch/swap/v6.0/${llamalend.chainId}/quote?src=${fromToken}&dst=${toToken}&amount=${_amount}&excludedProtocols=${llamalend.constants.EXCLUDED_PROTOCOLS_1INCH}&includeTokensInfo=true&includeProtocols=true`;
258
- const response = await axios.get(
259
- url,
260
- {
261
- headers: {"accept": "application/json"},
262
- validateStatus: () => true,
263
- });
264
- if (response.status !== 200) {
265
- throw Error(`1inch error: ${response.status} ${response.statusText}`);
266
- }
267
- return response.data.dstAmount;
268
-
269
- },
270
- {
271
- promise: true,
272
- maxAge: 10 * 1000, // 10s
273
- }
274
- )
275
-
276
- export const _getSwapData1inch = memoize(
277
- async (fromToken: string, toToken: string, _amount: bigint, slippage: number): Promise<I1inchSwapData> => {
278
- if (_amount === BigInt(0)) throw Error("Amount must be > 0");
279
- const url = `https://prices.curve.fi/1inch/swap/v6.0/${llamalend.chainId}/swap?src=${fromToken}&dst=${toToken}&amount=${_amount}&from_=${llamalend.constants.ALIASES.leverage_zap}&slippage=${slippage}&excludedProtocols=${llamalend.constants.EXCLUDED_PROTOCOLS_1INCH}&includeTokensInfo=true&includeProtocols=true&disableEstimate=true`;
280
- const response = await axios.get(
281
- url,
282
- {
283
- headers: {"accept": "application/json"},
284
- validateStatus: () => true,
285
- });
286
- if (response.status !== 200) {
287
- throw Error(`1inch error: ${response.status} ${response.statusText}`);
288
- }
289
- return response.data;
290
-
291
- },
292
- {
293
- promise: true,
294
- maxAge: 10 * 1000, // 10s
295
- }
296
- )
297
-
298
- export const _getSpotPrice1inch = memoize(
299
- async (fromToken: string, toToken: string): Promise<string | undefined> => {
300
- const url = `https://prices.curve.fi/1inch/price/v1.1/${llamalend.chainId}?tokens=${fromToken},${toToken}&currency=USD`;
301
- const response = await axios.get(
302
- url,
303
- {
304
- headers: {"accept": "application/json"},
305
- validateStatus: () => true,
306
- });
307
- if (response.status !== 200) {
308
- throw Error(`1inch error: ${response.status} ${response.statusText}`);
309
- }
310
- const pricesFromApi: IDict<string> = {};
311
- for (const coin in response.data) {
312
- if (response.data[coin] !== "0") continue;
313
- const _pricesFromApi = await _getUsdPricesFromApi();
314
- pricesFromApi[coin] = String(_pricesFromApi[coin] || 0);
315
- }
316
- const prices = { ...response.data, ...pricesFromApi };
317
- if (prices[fromToken] === '0' || prices[toToken] === '0') return undefined;
318
-
319
- return (new BigNumber(prices[toToken])).div(prices[fromToken]).toString()
195
+ const { transaction } = await response.json() as { transaction: { data: string } };
196
+ return transaction.data;
320
197
  },
321
198
  {
322
199
  promise: true,
package/src/interfaces.ts CHANGED
@@ -123,25 +123,6 @@ export interface IReward {
123
123
  apy: number
124
124
  }
125
125
 
126
- export type T1inchRouteStep = {
127
- name: string,
128
- part: number,
129
- fromTokenAddress: string,
130
- toTokenAddress: string,
131
- }[]
132
-
133
- export interface I1inchRoute {
134
- part: number,
135
- hops: T1inchRouteStep[],
136
- }
137
-
138
- export interface I1inchSwapData {
139
- tx: { data: string },
140
- dstAmount: string,
141
- protocols: I1inchRoute[],
142
- slippage: number,
143
- }
144
-
145
126
  interface Rates {
146
127
  borrowApr: number;
147
128
  borrowApy: number;
@@ -700,7 +700,7 @@ export class LendMarketTemplate {
700
700
  return [baseApyBN.times(100).toNumber(), boostedApyBN.times(100).toNumber()]
701
701
  }
702
702
 
703
- private async vaultCrvApr(useApi = true): Promise<[baseApy: number, boostedApy: number]> {
703
+ private async vaultCrvApr(): Promise<[baseApy: number, boostedApy: number]> {
704
704
  if (this.vaultRewardsOnly()) throw Error(`${this.name} has Rewards-Only Gauge. Use stats.rewardsApy instead`);
705
705
 
706
706
  // const isDisabledChain = [1313161554].includes(llamalend.chainId); // Disable Aurora
@@ -765,7 +765,7 @@ export class LendMarketTemplate {
765
765
  return await this._vaultClaimCrv(false) as string;
766
766
  }
767
767
 
768
- private vaultRewardTokens = memoize(async (useApi = true): Promise<{token: string, symbol: string, decimals: number}[]> => {
768
+ private vaultRewardTokens = memoize(async (): Promise<{token: string, symbol: string, decimals: number}[]> => {
769
769
  if (this.addresses.gauge === llamalend.constants.ZERO_ADDRESS) return []
770
770
 
771
771
  // if (useApi) {
@@ -828,7 +828,7 @@ export class LendMarketTemplate {
828
828
  // }
829
829
 
830
830
  const apy: IReward[] = [];
831
- const rewardTokens = await this.vaultRewardTokens(false);
831
+ const rewardTokens = await this.vaultRewardTokens();
832
832
  for (const rewardToken of rewardTokens) {
833
833
  const gaugeContract = llamalend.contracts[this.addresses.gauge].multicallContract;
834
834
  const lpTokenContract = llamalend.contracts[this.addresses.vault].multicallContract;
@@ -1863,7 +1863,7 @@ export class LendMarketTemplate {
1863
1863
 
1864
1864
  const _debt = parseUnits(debt);
1865
1865
  const contract = llamalend.contracts[this.addresses.controller].contract;
1866
- const [_, n1] = await this.userBands(address);
1866
+ const [, n1] = await this.userBands(address);
1867
1867
  const { borrowed } = await this.userState(address);
1868
1868
  const n = (BN(borrowed).gt(0)) ? MAX_ACTIVE_BAND : n1 - 1; // In liquidation mode it doesn't matter if active band moves
1869
1869
  const gas = await contract.repay.estimateGas(_debt, address, n, llamalend.constantOptions);
@@ -2859,7 +2859,7 @@ export class LendMarketTemplate {
2859
2859
  try {
2860
2860
  _n1 = await llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N);
2861
2861
  _n2 = _n1 + (_N - BigInt(1));
2862
- } catch (e) {
2862
+ } catch {
2863
2863
  console.log("Full repayment");
2864
2864
  }
2865
2865
 
package/src/llamalend.ts CHANGED
@@ -288,7 +288,7 @@ class Llamalend implements ILlamalend {
288
288
  } else if (!providerSettings.url?.startsWith("https://rpc.gnosischain.com")) {
289
289
  try {
290
290
  this.signer = await this.provider.getSigner();
291
- } catch (e) {
291
+ } catch {
292
292
  this.signer = null;
293
293
  }
294
294
  }
@@ -335,7 +335,7 @@ class Llamalend implements ILlamalend {
335
335
  if (this.signer) {
336
336
  try {
337
337
  this.signerAddress = await this.signer.getAddress();
338
- } catch (err) {
338
+ } catch {
339
339
  this.signer = null;
340
340
  }
341
341
  } else {
@@ -1057,7 +1057,7 @@ export class MintMarketTemplate {
1057
1057
 
1058
1058
  const _debt = parseUnits(debt);
1059
1059
  const contract = llamalend.contracts[this.controller].contract;
1060
- const [_, n1] = await this.userBands(address);
1060
+ const [, n1] = await this.userBands(address);
1061
1061
  const { stablecoin } = await this.userState(address);
1062
1062
  const n = (BN(stablecoin).gt(0)) ? MAX_ACTIVE_BAND : n1 - 1; // In liquidation mode it doesn't matter if active band moves
1063
1063
  const gas = await contract.repay.estimateGas(_debt, address, n, isEth(this.collateral), llamalend.constantOptions);
@@ -1693,7 +1693,7 @@ export class MintMarketTemplate {
1693
1693
  try {
1694
1694
  _n1 = await llamalend.contracts[this.deleverageZap].contract.calculate_debt_n1(_collateral, routeIdx, address);
1695
1695
  _n2 = _n1 + BigInt(N - 1);
1696
- } catch (e) {
1696
+ } catch {
1697
1697
  console.log("Full repayment");
1698
1698
  }
1699
1699
 
package/src/utils.ts CHANGED
@@ -1,4 +1,3 @@
1
- import axios from "axios";
2
1
  import { ethers, BigNumberish, Numeric } from "ethers";
3
2
  import { Call } from "@curvefi/ethcall";
4
3
  import BigNumber from 'bignumber.js';
@@ -359,10 +358,11 @@ export const _getUsdRate = async (assetId: string): Promise<number> => {
359
358
  const url = [nativeTokenName, 'ethereum', 'bitcoin', 'link', 'curve-dao-token', 'stasis-eurs'].includes(assetId.toLowerCase()) ?
360
359
  `https://api.coingecko.com/api/v3/simple/price?ids=${assetId}&vs_currencies=usd` :
361
360
  `https://api.coingecko.com/api/v3/simple/token_price/${chainName}?contract_addresses=${assetId}&vs_currencies=usd`
362
- const response = await axios.get(url);
361
+ const response = await fetch(url);
362
+ const data = await response.json() as Record<string, { usd: number }>;
363
363
  try {
364
- _usdRatesCache[assetId] = {'rate': response.data[assetId]['usd'] ?? 0, 'time': Date.now()};
365
- } catch (err) { // TODO pay attention!
364
+ _usdRatesCache[assetId] = {'rate': data[assetId]['usd'] ?? 0, 'time': Date.now()};
365
+ } catch { // TODO pay attention!
366
366
  _usdRatesCache[assetId] = {'rate': 0, 'time': Date.now()};
367
367
  }
368
368
  }
@@ -457,8 +457,8 @@ export const getLsdApy = memoize(async(name: 'wstETH' | 'sfrxETH'): Promise<{
457
457
  baseApy: number,
458
458
  apyMean30d: number,
459
459
  }> => {
460
- const response = await axios.get('https://yields.llama.fi/pools');
461
- const {data} = response as { data: { chain: string, project: string, symbol: string, apy: number, apyBase: number, apyMean30d: number }[] };
460
+ const response = await fetch('https://yields.llama.fi/pools');
461
+ const {data} = await response.json() as { data: { chain: string, project: string, symbol: string, apy: number, apyBase: number, apyMean30d: number }[] };
462
462
 
463
463
  const params = {
464
464
  'wstETH': {
package/tsconfig.json CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  /* Basic Options */
6
6
  // "incremental": true, /* Enable incremental compilation */
7
- "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
7
+ "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8
8
  "module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9
9
  "lib": ["ES2020"], /* Specify library files to be included in the compilation. */
10
10
  // "allowJs": true, /* Allow javascript files to be compiled. */
package/.eslintrc.json DELETED
@@ -1,40 +0,0 @@
1
- {
2
- "env": {
3
- "browser": true,
4
- "es6": true
5
- },
6
- "extends": "eslint:recommended",
7
- "parser": "vue-eslint-parser",
8
- "parserOptions": {
9
- "parser": "babel-eslint",
10
- "sourceType": "module",
11
- "allowImportExportEverywhere": false
12
- },
13
- "rules": {
14
- "func-names": 0,
15
- "no-nested-ternary": 0,
16
- "max-len": 0,
17
- "arrow-parens": ["error", "always"],
18
- "no-underscore-dangle": 0,
19
- "comma-dangle": ["error", {
20
- "arrays": "always-multiline",
21
- "objects": "always-multiline",
22
- "imports": "always-multiline",
23
- "exports": "always-multiline",
24
- "functions": "never"
25
- }],
26
- "no-use-before-define": ["error", "nofunc"],
27
- "no-empty": ["error", { "allowEmptyCatch": true }],
28
- "no-mixed-operators": ["error", { "allowSamePrecedence": true }],
29
- "indent": ["error", 4, { "flatTernaryExpressions": true, "SwitchCase": 1 }]
30
- },
31
- "overrides": [{
32
- "files": ["**/*.ts"],
33
- "parser": "@typescript-eslint/parser",
34
- "plugins": ["@typescript-eslint"],
35
- "extends": ["plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended"],
36
- "rules": {
37
- "@typescript-eslint/ban-ts-comment": "off"
38
- }
39
- }]
40
- }