@curvefi/llamalend-api 1.0.32 → 1.0.34
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/README.md +2 -2
- package/lib/cached.d.ts +2 -0
- package/lib/cached.js +75 -0
- package/lib/external-api.d.ts +3 -2
- package/lib/external-api.js +83 -88
- package/lib/interfaces.d.ts +1 -0
- package/lib/mintMarkets/MintMarketTemplate.d.ts +1 -0
- package/lib/utils.js +2 -2
- package/package.json +7 -7
- package/src/cached.ts +82 -0
- package/src/external-api.ts +28 -37
- package/src/interfaces.ts +2 -0
- package/src/mintMarkets/MintMarketTemplate.ts +1 -0
- package/src/utils.ts +2 -2
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ import { useState, useMemo } from 'react'
|
|
|
34
34
|
import { providers } from 'ethers'
|
|
35
35
|
import Onboard from 'bnc-onboard'
|
|
36
36
|
import type { Wallet } from 'bnc-onboard/dist/src/interfaces'
|
|
37
|
-
import llamalend from '@curvefi/
|
|
37
|
+
import llamalend from '@curvefi/llamalend-api'
|
|
38
38
|
...
|
|
39
39
|
|
|
40
40
|
const WalletProvider: FunctionComponent = ({ children }) => {
|
|
@@ -79,7 +79,7 @@ import { useState, useMemo, useEffect } from 'react'
|
|
|
79
79
|
import { providers } from 'ethers'
|
|
80
80
|
import Onboard from 'bnc-onboard'
|
|
81
81
|
import type { Wallet } from 'bnc-onboard/dist/src/interfaces'
|
|
82
|
-
import llamalend from '@curvefi/
|
|
82
|
+
import llamalend from '@curvefi/llamalend-api'
|
|
83
83
|
|
|
84
84
|
...
|
|
85
85
|
|
package/lib/cached.d.ts
ADDED
package/lib/cached.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { createUsdPricesDict, uncached_getAllPoolsFromApi } from './external-api.js';
|
|
11
|
+
/**
|
|
12
|
+
* Memoizes a function that returns a promise.
|
|
13
|
+
* Custom function instead of `memoizee` because we want to be able to set the cache manually based on server data.
|
|
14
|
+
* @param fn The function that returns a promise and will be memoized
|
|
15
|
+
* @param maxAge The maximum age of the cache in milliseconds
|
|
16
|
+
* @param createKey A function that creates a key for the cache based on the arguments passed to the function
|
|
17
|
+
* @returns A memoized `fn` function that includes a `set` method to set the cache manually
|
|
18
|
+
*/
|
|
19
|
+
const memoize = (fn, { maxAge, createKey = (list) => list.toString(), }) => {
|
|
20
|
+
const cache = {};
|
|
21
|
+
const timeouts = {};
|
|
22
|
+
const setCache = (key, promise) => {
|
|
23
|
+
if (promise) {
|
|
24
|
+
cache[key] = promise;
|
|
25
|
+
}
|
|
26
|
+
else if (key in cache) {
|
|
27
|
+
delete cache[key];
|
|
28
|
+
}
|
|
29
|
+
if (key in timeouts) {
|
|
30
|
+
clearTimeout(timeouts[key]);
|
|
31
|
+
delete timeouts[key];
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const scheduleCleanup = (key) => timeouts[key] = setTimeout(() => {
|
|
35
|
+
delete timeouts[key];
|
|
36
|
+
delete cache[key];
|
|
37
|
+
}, maxAge);
|
|
38
|
+
const cachedFn = (...args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
+
const key = createKey(args);
|
|
40
|
+
if (key in cache) {
|
|
41
|
+
return cache[key];
|
|
42
|
+
}
|
|
43
|
+
const promise = fn(...args);
|
|
44
|
+
setCache(key, promise);
|
|
45
|
+
try {
|
|
46
|
+
const result = yield promise;
|
|
47
|
+
scheduleCleanup(key);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
delete cache[key];
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
cachedFn.set = (result, ...args) => {
|
|
56
|
+
const key = createKey(args);
|
|
57
|
+
setCache(key, Promise.resolve(result));
|
|
58
|
+
scheduleCleanup(key);
|
|
59
|
+
};
|
|
60
|
+
return cachedFn;
|
|
61
|
+
};
|
|
62
|
+
const createCache = (poolsDict) => {
|
|
63
|
+
const poolLists = Object.values(poolsDict);
|
|
64
|
+
const usdPrices = createUsdPricesDict(poolLists);
|
|
65
|
+
return { poolsDict, poolLists, usdPrices };
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* This function is used to cache the data fetched from the API and the data derived from it.
|
|
69
|
+
* Note: do not expose this function to the outside world, instead encapsulate it in a function that returns the data you need.
|
|
70
|
+
*/
|
|
71
|
+
const _getCachedData = memoize((network) => __awaiter(void 0, void 0, void 0, function* () { return createCache(yield uncached_getAllPoolsFromApi(network)); }), { maxAge: 1000 * 60 * 5 /* 5 minutes */ });
|
|
72
|
+
export const _getUsdPricesFromApi = (network) => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
+
const { usdPrices } = yield _getCachedData(network);
|
|
74
|
+
return usdPrices;
|
|
75
|
+
});
|
package/lib/external-api.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import memoize from "memoizee";
|
|
2
2
|
import type { Llamalend } from "./llamalend.js";
|
|
3
|
-
import { IDict, IMarketData, INetworkName, IQuoteOdos } from "./interfaces";
|
|
4
|
-
export declare
|
|
3
|
+
import { IDict, IExtendedPoolDataFromApi, IMarketData, INetworkName, IQuoteOdos, IPoolType } from "./interfaces";
|
|
4
|
+
export declare const uncached_getAllPoolsFromApi: (network: INetworkName) => Promise<Record<IPoolType, IExtendedPoolDataFromApi>>;
|
|
5
|
+
export declare const createUsdPricesDict: (allTypesExtendedPoolData: IExtendedPoolDataFromApi[]) => IDict<number>;
|
|
5
6
|
type UserCollateral = {
|
|
6
7
|
total_deposit_precise: string;
|
|
7
8
|
total_deposit_from_user: number;
|
package/lib/external-api.js
CHANGED
|
@@ -9,105 +9,87 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { ethers } from "ethers";
|
|
11
11
|
import memoize from "memoizee";
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
promise: true,
|
|
18
|
-
maxAge: 5 * 60 * 1000, // 5m
|
|
12
|
+
const uncached_getPoolsFromApi = (network, poolType) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
var _a;
|
|
14
|
+
const api = "https://api.curve.finance/api";
|
|
15
|
+
const url = `${api}/getPools/${network}/${poolType}`;
|
|
16
|
+
return (_a = yield fetchData(url)) !== null && _a !== void 0 ? _a : { poolData: [], tvl: 0, tvlAll: 0 };
|
|
19
17
|
});
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
_getPoolsFromApi(network, "factory-crypto"),
|
|
27
|
-
_getPoolsFromApi(network, "factory-twocrypto"),
|
|
28
|
-
_getPoolsFromApi(network, "factory-tricrypto"),
|
|
29
|
-
_getPoolsFromApi(network, "factory-stable-ng"),
|
|
30
|
-
]);
|
|
18
|
+
const getPoolTypes = () => ["main", "crypto", "factory", "factory-crvusd", "factory-crypto", "factory-twocrypto", "factory-tricrypto", "factory-stable-ng"];
|
|
19
|
+
export const uncached_getAllPoolsFromApi = (network) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
+
return Object.fromEntries(yield Promise.all(getPoolTypes().map((poolType) => __awaiter(void 0, void 0, void 0, function* () {
|
|
21
|
+
const data = yield uncached_getPoolsFromApi(network, poolType);
|
|
22
|
+
return [poolType, data];
|
|
23
|
+
}))));
|
|
31
24
|
});
|
|
32
|
-
export
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
price: coin.usdPrice,
|
|
61
|
-
tvl: pool.usdTotal,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
priceDict[coin.address.toLowerCase()] = [];
|
|
66
|
-
priceDict[coin.address.toLowerCase()].push({
|
|
67
|
-
price: coin.usdPrice,
|
|
68
|
-
tvl: pool.usdTotal,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
25
|
+
export const createUsdPricesDict = (allTypesExtendedPoolData) => {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
const priceDict = {};
|
|
28
|
+
const priceDictByMaxTvl = {};
|
|
29
|
+
for (const extendedPoolData of allTypesExtendedPoolData) {
|
|
30
|
+
for (const pool of extendedPoolData.poolData) {
|
|
31
|
+
const lpTokenAddress = (_a = pool.lpTokenAddress) !== null && _a !== void 0 ? _a : pool.address;
|
|
32
|
+
const totalSupply = pool.totalSupply / (Math.pow(10, 18));
|
|
33
|
+
if (lpTokenAddress.toLowerCase() in priceDict) {
|
|
34
|
+
priceDict[lpTokenAddress.toLowerCase()].push({
|
|
35
|
+
price: pool.usdTotal && totalSupply ? pool.usdTotal / totalSupply : 0,
|
|
36
|
+
tvl: pool.usdTotal,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
priceDict[lpTokenAddress.toLowerCase()] = [];
|
|
41
|
+
priceDict[lpTokenAddress.toLowerCase()].push({
|
|
42
|
+
price: pool.usdTotal && totalSupply ? pool.usdTotal / totalSupply : 0,
|
|
43
|
+
tvl: pool.usdTotal,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
for (const coin of pool.coins) {
|
|
47
|
+
if (typeof coin.usdPrice === "number") {
|
|
48
|
+
if (coin.address.toLowerCase() in priceDict) {
|
|
49
|
+
priceDict[coin.address.toLowerCase()].push({
|
|
50
|
+
price: coin.usdPrice,
|
|
51
|
+
tvl: pool.usdTotal,
|
|
52
|
+
});
|
|
71
53
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
tvl: pool.usdTotal,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
priceDict[coin.tokenAddress.toLowerCase()] = [];
|
|
83
|
-
priceDict[coin.tokenAddress.toLowerCase()].push({
|
|
84
|
-
price: coin.tokenPrice,
|
|
85
|
-
tvl: pool.usdTotal,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
54
|
+
else {
|
|
55
|
+
priceDict[coin.address.toLowerCase()] = [];
|
|
56
|
+
priceDict[coin.address.toLowerCase()].push({
|
|
57
|
+
price: coin.usdPrice,
|
|
58
|
+
tvl: pool.usdTotal,
|
|
59
|
+
});
|
|
88
60
|
}
|
|
89
61
|
}
|
|
90
62
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
63
|
+
for (const coin of (_b = pool.gaugeRewards) !== null && _b !== void 0 ? _b : []) {
|
|
64
|
+
if (typeof coin.tokenPrice === "number") {
|
|
65
|
+
if (coin.tokenAddress.toLowerCase() in priceDict) {
|
|
66
|
+
priceDict[coin.tokenAddress.toLowerCase()].push({
|
|
67
|
+
price: coin.tokenPrice,
|
|
68
|
+
tvl: pool.usdTotal,
|
|
69
|
+
});
|
|
97
70
|
}
|
|
98
71
|
else {
|
|
99
|
-
|
|
72
|
+
priceDict[coin.tokenAddress.toLowerCase()] = [];
|
|
73
|
+
priceDict[coin.tokenAddress.toLowerCase()].push({
|
|
74
|
+
price: coin.tokenPrice,
|
|
75
|
+
tvl: pool.usdTotal,
|
|
76
|
+
});
|
|
100
77
|
}
|
|
101
|
-
}
|
|
102
|
-
priceDictByMaxTvl[address] = maxTvlItem.price;
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
priceDictByMaxTvl[address] = 0;
|
|
78
|
+
}
|
|
106
79
|
}
|
|
107
80
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
81
|
+
}
|
|
82
|
+
for (const address in priceDict) {
|
|
83
|
+
if (priceDict[address].length) {
|
|
84
|
+
const maxTvlItem = priceDict[address].reduce((prev, current) => +current.tvl > +prev.tvl ? current : prev);
|
|
85
|
+
priceDictByMaxTvl[address] = maxTvlItem.price;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
priceDictByMaxTvl[address] = 0;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return priceDictByMaxTvl;
|
|
92
|
+
};
|
|
111
93
|
export const _getUserCollateral = memoize((network, controller, user) => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
94
|
const url = `https://prices.curve.finance/v1/lending/collateral_events/${network}/${controller}/${user}`;
|
|
113
95
|
const response = yield fetch(url);
|
|
@@ -199,3 +181,16 @@ export const _getHiddenPools = memoize(() => __awaiter(void 0, void 0, void 0, f
|
|
|
199
181
|
promise: true,
|
|
200
182
|
maxAge: 5 * 60 * 1000, // 5m
|
|
201
183
|
});
|
|
184
|
+
function fetchJson(url) {
|
|
185
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
var _a;
|
|
187
|
+
const response = yield fetch(url);
|
|
188
|
+
return (_a = yield response.json()) !== null && _a !== void 0 ? _a : {};
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function fetchData(url) {
|
|
192
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
193
|
+
const { data } = yield fetchJson(url);
|
|
194
|
+
return data;
|
|
195
|
+
});
|
|
196
|
+
}
|
package/lib/interfaces.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface IDict<T> {
|
|
|
6
6
|
export type INetworkName = "ethereum" | "optimism" | "bsc" | "xdai" | "polygon" | 'sonic' | "x-layer" | "fantom" | "fraxtal" | "zksync" | "moonbeam" | "kava" | "mantle" | "base" | "arbitrum" | "celo" | "avalanche" | "aurora";
|
|
7
7
|
export type IChainId = 1 | 10 | 56 | 100 | 137 | 146 | 196 | 250 | 252 | 324 | 1284 | 2222 | 5000 | 8453 | 42161 | 42220 | 43114 | 1313161554;
|
|
8
8
|
export type IPoolFactory = "main" | "crypto" | "factory" | "factory-crvusd" | "factory-crypto" | "factory-twocrypto" | "factory-tricrypto" | "factory-stable-ng";
|
|
9
|
+
export type IPoolType = "main" | "crypto" | IPoolFactory;
|
|
9
10
|
export interface ICurveContract {
|
|
10
11
|
contract: Contract;
|
|
11
12
|
multicallContract: MulticallContract;
|
package/lib/utils.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { ethers } from "ethers";
|
|
11
11
|
import BigNumber from 'bignumber.js';
|
|
12
|
-
import { _getUsdPricesFromApi } from "./
|
|
12
|
+
import { _getUsdPricesFromApi } from "./cached.js";
|
|
13
13
|
import { L2Networks } from "./constants/L2Networks.js";
|
|
14
14
|
import memoize from "memoizee";
|
|
15
15
|
export const MAX_ALLOWANCE = BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935"); // 2**256 - 1
|
|
@@ -264,7 +264,7 @@ export const _getUsdRate = function (assetId) {
|
|
|
264
264
|
var _a, _c;
|
|
265
265
|
if (this.chainId === 1 && assetId.toLowerCase() === '0x8762db106b2c2a0bccb3a80d1ed41273552616e8')
|
|
266
266
|
return 0; // RSR
|
|
267
|
-
const pricesFromApi = yield _getUsdPricesFromApi
|
|
267
|
+
const pricesFromApi = yield _getUsdPricesFromApi(this.constants.NETWORK_NAME);
|
|
268
268
|
if (assetId.toLowerCase() in pricesFromApi)
|
|
269
269
|
return pricesFromApi[assetId.toLowerCase()];
|
|
270
270
|
if (assetId === 'USD' || (this.chainId === 137 && (assetId.toLowerCase() === this.constants.COINS.am3crv.toLowerCase())))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@curvefi/llamalend-api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.34",
|
|
4
4
|
"description": "JavaScript library for Curve Lending",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"author": "Macket",
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
"private": false,
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/curvefi/curve-
|
|
11
|
+
"url": "https://github.com/curvefi/curve-llamalend.js.git"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/curvefi/curve-
|
|
14
|
+
"url": "https://github.com/curvefi/curve-llamalend.js/issues"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "rm -rf lib && tsc --project tsconfig.build.json",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"vue-eslint-parser": "^10.1.3"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@curvefi/ethcall": "6.0.
|
|
45
|
-
"bignumber.js": "
|
|
46
|
-
"ethers": "
|
|
47
|
-
"memoizee": "
|
|
44
|
+
"@curvefi/ethcall": "6.0.16",
|
|
45
|
+
"bignumber.js": "9.3.1",
|
|
46
|
+
"ethers": "6.15.0",
|
|
47
|
+
"memoizee": "0.4.17"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/src/cached.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {IDict, IExtendedPoolDataFromApi, INetworkName, IPoolType} from "./interfaces.js";
|
|
2
|
+
import {createUsdPricesDict, uncached_getAllPoolsFromApi} from './external-api.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Memoizes a function that returns a promise.
|
|
6
|
+
* Custom function instead of `memoizee` because we want to be able to set the cache manually based on server data.
|
|
7
|
+
* @param fn The function that returns a promise and will be memoized
|
|
8
|
+
* @param maxAge The maximum age of the cache in milliseconds
|
|
9
|
+
* @param createKey A function that creates a key for the cache based on the arguments passed to the function
|
|
10
|
+
* @returns A memoized `fn` function that includes a `set` method to set the cache manually
|
|
11
|
+
*/
|
|
12
|
+
const memoize = <TResult, TParams extends any[], TFunc extends (...args: TParams) => Promise<TResult>>(fn: TFunc, {
|
|
13
|
+
maxAge,
|
|
14
|
+
createKey = (list) => list.toString(),
|
|
15
|
+
}: {
|
|
16
|
+
maxAge: number,
|
|
17
|
+
createKey?: (args: TParams) => string
|
|
18
|
+
}) => {
|
|
19
|
+
const cache: Record<string, Promise<TResult>> = {};
|
|
20
|
+
const timeouts: Record<string, ReturnType<typeof setTimeout>> = {};
|
|
21
|
+
|
|
22
|
+
const setCache = (key: string, promise?: Promise<TResult>) => {
|
|
23
|
+
if (promise) {
|
|
24
|
+
cache[key] = promise;
|
|
25
|
+
} else if (key in cache) {
|
|
26
|
+
delete cache[key];
|
|
27
|
+
}
|
|
28
|
+
if (key in timeouts) {
|
|
29
|
+
clearTimeout(timeouts[key]);
|
|
30
|
+
delete timeouts[key]
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const scheduleCleanup = (key: string) => timeouts[key] = setTimeout(() => {
|
|
35
|
+
delete timeouts[key];
|
|
36
|
+
delete cache[key];
|
|
37
|
+
}, maxAge);
|
|
38
|
+
|
|
39
|
+
const cachedFn = async (...args: TParams): Promise<TResult> => {
|
|
40
|
+
const key = createKey(args);
|
|
41
|
+
if (key in cache) {
|
|
42
|
+
return cache[key];
|
|
43
|
+
}
|
|
44
|
+
const promise = fn(...args);
|
|
45
|
+
setCache(key, promise);
|
|
46
|
+
try {
|
|
47
|
+
const result = await promise;
|
|
48
|
+
scheduleCleanup(key)
|
|
49
|
+
return result;
|
|
50
|
+
} catch (e) {
|
|
51
|
+
delete cache[key];
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
cachedFn.set = (result: TResult, ...args: TParams) => {
|
|
57
|
+
const key = createKey(args);
|
|
58
|
+
setCache(key, Promise.resolve(result));
|
|
59
|
+
scheduleCleanup(key);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return cachedFn as TFunc & { set: (result: TResult, ...args: TParams) => void };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const createCache = (poolsDict: Record<IPoolType, IExtendedPoolDataFromApi>) => {
|
|
66
|
+
const poolLists = Object.values(poolsDict)
|
|
67
|
+
const usdPrices = createUsdPricesDict(poolLists);
|
|
68
|
+
return {poolsDict, poolLists, usdPrices};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* This function is used to cache the data fetched from the API and the data derived from it.
|
|
73
|
+
* Note: do not expose this function to the outside world, instead encapsulate it in a function that returns the data you need.
|
|
74
|
+
*/
|
|
75
|
+
const _getCachedData = memoize(async (network: INetworkName) =>
|
|
76
|
+
createCache(await uncached_getAllPoolsFromApi(network)), {maxAge: 1000 * 60 * 5 /* 5 minutes */})
|
|
77
|
+
|
|
78
|
+
export const _getUsdPricesFromApi = async (network:INetworkName): Promise<IDict<number>> => {
|
|
79
|
+
const {usdPrices} = await _getCachedData(network);
|
|
80
|
+
return usdPrices
|
|
81
|
+
}
|
|
82
|
+
|
package/src/external-api.ts
CHANGED
|
@@ -6,40 +6,27 @@ import {
|
|
|
6
6
|
IExtendedPoolDataFromApi,
|
|
7
7
|
IMarketData,
|
|
8
8
|
INetworkName,
|
|
9
|
-
IPoolFactory,
|
|
10
9
|
IQuoteOdos,
|
|
11
10
|
IResponseApi,
|
|
11
|
+
IPoolType,
|
|
12
12
|
} from "./interfaces";
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const { data } = await response.json() as { data?: IExtendedPoolDataFromApi, success: boolean };
|
|
19
|
-
return data ?? { poolData: [], tvl: 0, tvlAll: 0 };
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
promise: true,
|
|
23
|
-
maxAge: 5 * 60 * 1000, // 5m
|
|
24
|
-
}
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
const _getAllPoolsFromApi = async (network: INetworkName): Promise<IExtendedPoolDataFromApi[]> => {
|
|
28
|
-
return await Promise.all([
|
|
29
|
-
_getPoolsFromApi(network, "main"),
|
|
30
|
-
_getPoolsFromApi(network, "crypto"),
|
|
31
|
-
_getPoolsFromApi(network, "factory"),
|
|
32
|
-
_getPoolsFromApi(network, "factory-crvusd"),
|
|
33
|
-
_getPoolsFromApi(network, "factory-crypto"),
|
|
34
|
-
_getPoolsFromApi(network, "factory-twocrypto"),
|
|
35
|
-
_getPoolsFromApi(network, "factory-tricrypto"),
|
|
36
|
-
_getPoolsFromApi(network, "factory-stable-ng"),
|
|
37
|
-
]);
|
|
14
|
+
const uncached_getPoolsFromApi = async (network: INetworkName, poolType: IPoolType): Promise<IExtendedPoolDataFromApi> => {
|
|
15
|
+
const api = "https://api.curve.finance/api";
|
|
16
|
+
const url = `${api}/getPools/${network}/${poolType}`;
|
|
17
|
+
return await fetchData(url) ?? { poolData: [], tvl: 0, tvlAll: 0 };
|
|
38
18
|
}
|
|
39
19
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
20
|
+
const getPoolTypes = () => ["main", "crypto", "factory", "factory-crvusd", "factory-crypto", "factory-twocrypto", "factory-tricrypto", "factory-stable-ng"] as const;
|
|
21
|
+
export const uncached_getAllPoolsFromApi = async (network: INetworkName): Promise<Record<IPoolType, IExtendedPoolDataFromApi>> =>
|
|
22
|
+
Object.fromEntries(
|
|
23
|
+
await Promise.all(getPoolTypes().map(async (poolType) => {
|
|
24
|
+
const data = await uncached_getPoolsFromApi(network, poolType);
|
|
25
|
+
return [poolType, data];
|
|
26
|
+
}))
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
export const createUsdPricesDict = (allTypesExtendedPoolData: IExtendedPoolDataFromApi[]): IDict<number> => {
|
|
43
30
|
const priceDict: IDict<Record<string, number>[]> = {};
|
|
44
31
|
const priceDictByMaxTvl: IDict<number> = {};
|
|
45
32
|
|
|
@@ -97,24 +84,18 @@ export async function _getUsdPricesFromApi(this: Llamalend): Promise<IDict<numbe
|
|
|
97
84
|
}
|
|
98
85
|
|
|
99
86
|
for(const address in priceDict) {
|
|
100
|
-
if(priceDict[address].length
|
|
101
|
-
const maxTvlItem = priceDict[address].reduce((prev, current) =>
|
|
102
|
-
if (+current.tvl > +prev.tvl) {
|
|
103
|
-
return current;
|
|
104
|
-
} else {
|
|
105
|
-
return prev;
|
|
106
|
-
}
|
|
107
|
-
});
|
|
87
|
+
if (priceDict[address].length) {
|
|
88
|
+
const maxTvlItem = priceDict[address].reduce((prev, current) => +current.tvl > +prev.tvl ? current : prev);
|
|
108
89
|
priceDictByMaxTvl[address] = maxTvlItem.price
|
|
109
90
|
} else {
|
|
110
91
|
priceDictByMaxTvl[address] = 0
|
|
111
92
|
}
|
|
112
|
-
|
|
113
93
|
}
|
|
114
94
|
|
|
115
95
|
return priceDictByMaxTvl
|
|
116
96
|
}
|
|
117
97
|
|
|
98
|
+
|
|
118
99
|
type UserCollateral = { total_deposit_precise: string, total_deposit_from_user: number, total_deposit_usd_value: number , total_borrowed: number, total_deposit_from_user_precise: number, total_deposit_from_user_usd_value: number}
|
|
119
100
|
export const _getUserCollateral = memoize(
|
|
120
101
|
async (network: INetworkName, controller: string, user: string): Promise<UserCollateral> => {
|
|
@@ -232,3 +213,13 @@ export const _getHiddenPools = memoize(
|
|
|
232
213
|
maxAge: 5 * 60 * 1000, // 5m
|
|
233
214
|
}
|
|
234
215
|
)
|
|
216
|
+
|
|
217
|
+
async function fetchJson(url: string): Promise<any> {
|
|
218
|
+
const response = await fetch(url);
|
|
219
|
+
return await response.json() ?? {};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function fetchData(url: string) {
|
|
223
|
+
const {data} = await fetchJson(url);
|
|
224
|
+
return data;
|
|
225
|
+
}
|
package/src/interfaces.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface IDict<T> {
|
|
|
8
8
|
export type INetworkName = "ethereum" | "optimism" | "bsc" | "xdai" | "polygon" | 'sonic' | "x-layer" | "fantom" | "fraxtal" | "zksync" | "moonbeam" | "kava" | "mantle" | "base" | "arbitrum" | "celo" | "avalanche" | "aurora";
|
|
9
9
|
export type IChainId = 1 | 10 | 56 | 100 | 137 | 146 | 196 | 250 | 252 | 324 | 1284 | 2222 | 5000 | 8453 | 42161 | 42220 | 43114 | 1313161554;
|
|
10
10
|
export type IPoolFactory = "main" | "crypto" | "factory" | "factory-crvusd" | "factory-crypto" | "factory-twocrypto" | "factory-tricrypto" | "factory-stable-ng";
|
|
11
|
+
export type IPoolType = "main" | "crypto" | IPoolFactory;
|
|
12
|
+
|
|
11
13
|
export interface ICurveContract {
|
|
12
14
|
contract: Contract,
|
|
13
15
|
multicallContract: MulticallContract,
|
package/src/utils.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ethers, BigNumberish, Numeric } from "ethers";
|
|
|
2
2
|
import { Call } from "@curvefi/ethcall";
|
|
3
3
|
import BigNumber from 'bignumber.js';
|
|
4
4
|
import { ICurveContract, IDict, TGas } from "./interfaces.js";
|
|
5
|
-
import { _getUsdPricesFromApi } from "./
|
|
5
|
+
import { _getUsdPricesFromApi } from "./cached.js";
|
|
6
6
|
import type { Llamalend } from "./llamalend.js";
|
|
7
7
|
import { JsonFragment } from "ethers/lib.esm";
|
|
8
8
|
import { L2Networks } from "./constants/L2Networks.js";
|
|
@@ -286,7 +286,7 @@ export const ensureAllowance = async function (this: Llamalend, coins: string[],
|
|
|
286
286
|
const _usdRatesCache: IDict<{ rate: number, time: number }> = {}
|
|
287
287
|
export const _getUsdRate = async function (this: Llamalend, assetId: string): Promise<number> {
|
|
288
288
|
if (this.chainId === 1 && assetId.toLowerCase() === '0x8762db106b2c2a0bccb3a80d1ed41273552616e8') return 0; // RSR
|
|
289
|
-
const pricesFromApi = await _getUsdPricesFromApi
|
|
289
|
+
const pricesFromApi = await _getUsdPricesFromApi(this.constants.NETWORK_NAME);
|
|
290
290
|
if (assetId.toLowerCase() in pricesFromApi) return pricesFromApi[assetId.toLowerCase()];
|
|
291
291
|
|
|
292
292
|
if (assetId === 'USD' || (this.chainId === 137 && (assetId.toLowerCase() === this.constants.COINS.am3crv.toLowerCase()))) return 1
|