Skip to content

Commit ccf8b44

Browse files
Merge pull request #454 from curvefi/feat/inject-pool-data
feat: allow pool data injection
2 parents a015ec1 + 69a7318 commit ccf8b44

File tree

4 files changed

+94
-21
lines changed

4 files changed

+94
-21
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@curvefi/api",
3-
"version": "2.66.30",
3+
"version": "2.66.31",
44
"description": "JavaScript library for curve.finance",
55
"main": "lib/index.js",
66
"author": "Macket",

src/cached.ts

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,91 @@
1-
import memoize from "memoizee";
21
import {IDict, IExtendedPoolDataFromApi, INetworkName, IPoolType} from "./interfaces.js";
32
import {createCrvApyDict, createUsdPricesDict, uncached_getAllPoolsFromApi} from './external-api.js'
43

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+
const crvApy = createCrvApyDict(poolLists)
69+
return {poolsDict, poolLists, usdPrices, crvApy};
70+
};
71+
572
/**
673
* This function is used to cache the data fetched from the API and the data derived from it.
774
* Note: do not expose this function to the outside world, instead encapsulate it in a function that returns the data you need.
875
*/
9-
const _getCachedData = memoize(
10-
async (network: INetworkName, isLiteChain: boolean) => {
11-
const poolsDict = await uncached_getAllPoolsFromApi(network, isLiteChain);
12-
const poolLists = Object.values(poolsDict)
13-
const usdPrices = createUsdPricesDict(poolLists);
14-
const crvApy = createCrvApyDict(poolLists)
15-
return { poolsDict, poolLists, usdPrices, crvApy };
16-
},
17-
{
18-
promise: true,
19-
maxAge: 5 * 60 * 1000, // 5m
20-
primitive: true,
21-
}
22-
)
76+
const _getCachedData = memoize(async (network: INetworkName, isLiteChain: boolean) =>
77+
createCache(await uncached_getAllPoolsFromApi(network, isLiteChain)), {maxAge: 1000 * 60 * 5 /* 5 minutes */})
2378

2479
export const _getPoolsFromApi =
2580
async (network: INetworkName, poolType: IPoolType, isLiteChain = false): Promise<IExtendedPoolDataFromApi> => {
2681
const {poolsDict} = await _getCachedData(network, isLiteChain);
2782
return poolsDict[poolType]
2883
}
2984

85+
export const _setPoolsFromApi =
86+
(network: INetworkName, isLiteChain: boolean, data: Record<IPoolType, IExtendedPoolDataFromApi>): void =>
87+
_getCachedData.set(createCache(data), network, isLiteChain)
88+
3089
export const _getAllPoolsFromApi = async (network: INetworkName, isLiteChain: boolean): Promise<IExtendedPoolDataFromApi[]> => {
3190
const {poolLists} = await _getCachedData(network, isLiteChain);
3291
return poolLists

src/curve.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,17 @@ import {getFactoryPoolData} from "./factory/factory.js";
1616
import {getFactoryPoolsDataFromApi} from "./factory/factory-api.js";
1717
import {getCryptoFactoryPoolData} from "./factory/factory-crypto.js";
1818
import {getTricryptoFactoryPoolData} from "./factory/factory-tricrypto.js";
19-
import {Abi, IChainId, ICurve, IDict, IFactoryPoolType, INetworkConstants, IPoolData} from "./interfaces";
19+
import {
20+
IPoolData,
21+
IDict,
22+
ICurve,
23+
IChainId,
24+
IFactoryPoolType,
25+
Abi,
26+
INetworkConstants,
27+
IPoolType,
28+
IExtendedPoolDataFromApi,
29+
} from "./interfaces";
2030
import ERC20Abi from './constants/abis/ERC20.json' with {type: 'json'};
2131
import cERC20Abi from './constants/abis/cERC20.json' with {type: 'json'};
2232
import yERC20Abi from './constants/abis/yERC20.json' with {type: 'json'};
@@ -61,7 +71,7 @@ import {_getHiddenPools} from "./external-api.js";
6171
import {L2Networks} from "./constants/L2Networks.js";
6272
import {getTwocryptoFactoryPoolData} from "./factory/factory-twocrypto.js";
6373
import {getNetworkConstants} from "./utils.js";
64-
74+
import {_setPoolsFromApi} from "./cached";
6575

6676
export const OLD_CHAINS = [1, 10, 56, 100, 137, 250, 1284, 2222, 8453, 42161, 42220, 43114, 1313161554]; // these chains have non-ng pools
6777

@@ -149,7 +159,7 @@ export class Curve implements ICurve {
149159
async init(
150160
providerType: 'JsonRpc' | 'Web3' | 'Infura' | 'Alchemy' | 'NoRPC',
151161
providerSettings: { url?: string, privateKey?: string, batchMaxCount? : number } | { externalProvider: ethers.Eip1193Provider } | { network?: Networkish, apiKey?: string } | 'NoRPC',
152-
options: { gasPrice?: number, maxFeePerGas?: number, maxPriorityFeePerGas?: number, chainId?: number } = {} // gasPrice in Gwei
162+
options: { gasPrice?: number, maxFeePerGas?: number, maxPriorityFeePerGas?: number, chainId?: number, poolsData?: Record<IPoolType, IExtendedPoolDataFromApi> } = {} // gasPrice in Gwei
153163
): Promise<void> {
154164
this.provider = null!;
155165
this.signer = null;
@@ -294,6 +304,10 @@ export class Curve implements ICurve {
294304
}
295305

296306
this.feeData = { gasPrice: options.gasPrice, maxFeePerGas: options.maxFeePerGas, maxPriorityFeePerGas: options.maxPriorityFeePerGas };
307+
if (options.poolsData) {
308+
_setPoolsFromApi(this.constants.NETWORK_NAME, this.isLiteChain, options.poolsData);
309+
}
310+
297311
await this.updateFeeData();
298312

299313
for (const pool of Object.values({...this.constants.POOLS_DATA, ...this.constants.LLAMMAS_DATA})) {

0 commit comments

Comments
 (0)