@defillama/sdk 5.0.13 → 5.0.16

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/build/types.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { Chain } from "./general";
2
1
  export type Address = string;
3
2
  export type StringNumber = string;
4
3
  export type Block = number | string;
@@ -20,6 +19,7 @@ export type CallOptions = {
20
19
  skipCache?: boolean;
21
20
  logArray?: LogArray;
22
21
  };
22
+ export type Chain = string;
23
23
  export type MulticallOptions = {
24
24
  abi: string | any;
25
25
  calls: CallsParams[] | (string | number)[];
@@ -0,0 +1,34 @@
1
+ import { AbstractProvider, AddressLike, Block, BlockTag, FallbackProvider, Filter, FilterByBlockHash, Log, TransactionRequest } from "ethers";
2
+ import { Chain } from "../types";
3
+ type ProviderWithURL = {
4
+ url: string;
5
+ provider: AbstractProvider;
6
+ };
7
+ export declare class LlamaProvider extends FallbackProvider {
8
+ isCustomLlamaProvider: boolean;
9
+ rpcs: ProviderWithURL[];
10
+ archivalRPCs: ProviderWithURL[];
11
+ _isReady: Promise<void>;
12
+ chainName: string;
13
+ chainId: number;
14
+ constructor({ providers, chainId, chainName, archivalRPCs, }: {
15
+ providers: ProviderWithURL[];
16
+ archivalRPCs: ProviderWithURL[];
17
+ chainId: number;
18
+ chainName: string;
19
+ }, fallbackConfig?: any);
20
+ getNetwork(): any;
21
+ _removeProvider(rpc: string): void;
22
+ _ready(): Promise<void>;
23
+ call(_tx: TransactionRequest): Promise<string>;
24
+ getBalance(address: AddressLike, blockTag?: BlockTag): Promise<bigint>;
25
+ getBlock(block: BlockTag, prefetchTxs?: boolean | undefined): Promise<Block | null>;
26
+ getLogs(_filter: Filter | FilterByBlockHash): Promise<Log[]>;
27
+ _performAction(method: string, params: any, attempts?: number): Promise<any>;
28
+ }
29
+ export declare const providers: {
30
+ [chain: string]: AbstractProvider;
31
+ };
32
+ export declare function setProvider(chain: Chain, provider: AbstractProvider): void;
33
+ export declare function getProvider(chain?: Chain, _getArchivalNode?: boolean): AbstractProvider;
34
+ export {};
@@ -0,0 +1,274 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getProvider = exports.setProvider = exports.providers = exports.LlamaProvider = void 0;
7
+ const ethers_1 = require("ethers");
8
+ const providers_json_1 = __importDefault(require("../providers.json"));
9
+ const env_1 = require("./env");
10
+ const debugLog_1 = require("./debugLog");
11
+ const axios_1 = __importDefault(require("axios"));
12
+ class LlamaProvider extends ethers_1.FallbackProvider {
13
+ constructor({ providers, chainId, chainName, archivalRPCs, }, fallbackConfig) {
14
+ const networkish = { name: chainName, chainId };
15
+ super(providers.map(i => i.provider), networkish, fallbackConfig);
16
+ this.isCustomLlamaProvider = true;
17
+ this.rpcs = providers;
18
+ this.archivalRPCs = archivalRPCs;
19
+ this.chainName = chainName;
20
+ this.chainId = chainId;
21
+ this._isReady = this._ready();
22
+ }
23
+ getNetwork() {
24
+ return { chainId: this.chainId, name: this.chainName };
25
+ }
26
+ _removeProvider(rpc) {
27
+ this.rpcs = this.rpcs.filter(i => i.url !== rpc);
28
+ this.archivalRPCs = this.archivalRPCs.filter(i => i.url !== rpc);
29
+ }
30
+ async _ready() {
31
+ const outOfSyncLimit = 1000;
32
+ const _this = this;
33
+ const currentBlocks = {};
34
+ await Promise.all(this.rpcs.map(checkRPCWorks));
35
+ if (this.archivalRPCs.length) {
36
+ await Promise.all(this.archivalRPCs.map(checkRPCWorks));
37
+ }
38
+ const highestBlock = Object.values(currentBlocks).reduce((highest, current) => Math.max(highest, current), 0);
39
+ Object.entries(currentBlocks).forEach(([url, block]) => {
40
+ if (highestBlock - block > outOfSyncLimit) {
41
+ (0, debugLog_1.debugLog)(`RPC ${url} is out of sync by ${highestBlock - block} blocks`);
42
+ _this.rpcs = _this.rpcs.filter(i => i.url !== url);
43
+ _this.archivalRPCs = _this.archivalRPCs.filter(i => i.url !== url);
44
+ }
45
+ });
46
+ async function checkRPCWorks({ url, provider }) {
47
+ try {
48
+ const block = await httpRPC.getBlockNumber(url);
49
+ currentBlocks[url] = block;
50
+ }
51
+ catch (e) {
52
+ // debugLog(`${_this.chainName} skipping RPC ${url} is not working, error: ${(e as any).message}`)
53
+ _this._removeProvider(url);
54
+ }
55
+ }
56
+ }
57
+ async call(_tx) {
58
+ var _a;
59
+ return this._performAction('call', [{ to: _tx.to, data: _tx.data }, (_a = _tx.blockTag) !== null && _a !== void 0 ? _a : 'latest']);
60
+ }
61
+ async getBalance(address, blockTag = 'latest') {
62
+ return this._performAction('getBalance', [address, blockTag]);
63
+ }
64
+ async getBlock(block, prefetchTxs) {
65
+ return this._performAction('getBlock', [block, false]);
66
+ }
67
+ async getLogs(_filter) {
68
+ return this._performAction('getLogs', [_filter], 3);
69
+ }
70
+ async _performAction(method, params, attempts = 7) {
71
+ await this._isReady;
72
+ let runners = [...this.rpcs];
73
+ if (method === 'getLogs') {
74
+ runners = [...this.archivalRPCs];
75
+ this.rpcs.filter(i => !runners.map(i => i.url).includes(i.url)).forEach(i => runners.push(i)); // ensure that there are no duplicates
76
+ }
77
+ let primaryRunner = runners[0];
78
+ let errors = [];
79
+ runners = runners.slice(1).sort(() => Math.random() - 0.5); // randomize order of runners
80
+ const isArchivalRequest = ['call', 'getBalance'].includes(method) && params[1] !== 'latest';
81
+ let noPlayingAround = primaryRunner.url.includes('llama.fi') || method === 'getLogs' || isArchivalRequest;
82
+ if (noPlayingAround) {
83
+ // if primary runner is llama.fi or alchemy, then try it first
84
+ runners.unshift(primaryRunner);
85
+ }
86
+ else {
87
+ // add it as second runner
88
+ runners.splice(1, 0, primaryRunner);
89
+ }
90
+ runners = runners.slice(0, attempts);
91
+ for (const runner of runners) {
92
+ try {
93
+ const result = await httpRPC[method](runner.url, params);
94
+ return result;
95
+ }
96
+ catch (e) {
97
+ // console.log('failed', runner.url, (e as any).message)
98
+ errors.push({ host: runner.url, error: e });
99
+ }
100
+ }
101
+ throw { llamaRPCError: true, errors };
102
+ }
103
+ }
104
+ exports.LlamaProvider = LlamaProvider;
105
+ function createProvider(name, rpcString, chainId = 400069, archivalRPCList = []) {
106
+ chainId = (0, env_1.getChainId)(name, chainId);
107
+ const networkish = { name, chainId };
108
+ const rpcList = rpcString.split(',');
109
+ if ((0, env_1.getEnvValue)('HISTORICAL')) {
110
+ if (chainId === 1) {
111
+ (0, debugLog_1.debugLog)("RPC providers set to historical, only the first RPC provider will be used");
112
+ }
113
+ return getProviderObject(rpcList[0], name);
114
+ }
115
+ else {
116
+ try {
117
+ const fallbackConfig = { cacheTimeout: 5 * 1000, quorum: 1, eventQuorum: 1, };
118
+ const getProviderWithURL = (url) => ({ url, provider: getProviderObject(url, name) });
119
+ const providers = rpcList.map(getProviderWithURL);
120
+ const archivalRPCs = archivalRPCList.map(getProviderWithURL);
121
+ return new LlamaProvider({ providers, archivalRPCs, chainId, chainName: name, }, fallbackConfig);
122
+ // return new FallbackProvider(providers, networkish, fallbackConfig)
123
+ }
124
+ catch (e) {
125
+ // debugLog(`Error creating provider for ${name} with RPCs: ${rpcList.join(', ')}`)
126
+ // we dont throw errors for chains not present in providers.json, these can be non-evm chains like solana
127
+ if (providers_json_1.default[name])
128
+ throw e;
129
+ return null;
130
+ }
131
+ }
132
+ function getProviderObject(url, chain) {
133
+ /**
134
+ * Options for configuring a [[JsonRpcApiProvider]]. Much of this
135
+ * is targetted towards sub-classes, which often will not expose
136
+ * any of these options to their consumers.
137
+ *
138
+ * **``polling``** - use the polling strategy is used immediately
139
+ * for events; otherwise, attempt to use filters and fall back onto
140
+ * polling (default: ``false``)
141
+ *
142
+ * **``staticNetwork``** - do not request chain ID on requests to
143
+ * validate the underlying chain has not changed (default: ``null``)
144
+ *
145
+ * This should **ONLY** be used if it is **certain** that the network
146
+ * cannot change, such as when using INFURA (since the URL dictates the
147
+ * network). If the network is assumed static and it does change, this
148
+ * can have tragic consequences. For example, this **CANNOT** be used
149
+ * with MetaMask, since the used can select a new network from the
150
+ * drop-down at any time.
151
+ *
152
+ * **``batchStallTime``** - how long (ms) to aggregate requests into a
153
+ * single batch. ``0`` indicates batching will only encompass the current
154
+ * event loop. If ``batchMaxCount = 1``, this is ignored. (default: ``10``)
155
+ *
156
+ * **``batchMaxSize``** - target maximum size (bytes) to allow per batch
157
+ * request (default: 1Mb)
158
+ *
159
+ * **``batchMaxCount``** - maximum number of requests to allow in a batch.
160
+ * If ``batchMaxCount = 1``, then batching is disabled. (default: ``100``)
161
+ *
162
+ * **``cacheTimeout``** - passed as [[AbstractProviderOptions]].
163
+ * **``cacheTimeout``** - how long to cache a low-level ``_perform``
164
+ * for, based on input parameters. This reduces the number of calls
165
+ * to getChainId and getBlockNumber, but may break test chains which
166
+ * can perform operations (internally) synchronously. Use ``-1`` to
167
+ * disable, ``0`` will only buffer within the same event loop and
168
+ * any other value is in ms. (default: ``250``)
169
+ */
170
+ // const batchMaxSize = 10 * (1024 * 1024) // 10Mb
171
+ // some rpcs throw error if batchMaxCount is set higher than 100
172
+ const batchMaxCount = (0, env_1.getBatchMaxCount)(chain);
173
+ const jsonRpcApiProviderOptions = { staticNetwork: true, batchMaxCount: 1, };
174
+ if (url.startsWith('wss://')) {
175
+ delete jsonRpcApiProviderOptions.batchMaxCount;
176
+ return new ethers_1.WebSocketProvider(url, networkish, jsonRpcApiProviderOptions);
177
+ }
178
+ return new ethers_1.JsonRpcProvider(url, networkish, jsonRpcApiProviderOptions);
179
+ }
180
+ }
181
+ exports.providers = {};
182
+ function setProvider(chain, provider) {
183
+ exports.providers[chain] = provider;
184
+ }
185
+ exports.setProvider = setProvider;
186
+ function getProvider(chain = "ethereum", _getArchivalNode = false) {
187
+ var _a, _b;
188
+ if (exports.providers[chain])
189
+ return exports.providers[chain];
190
+ // use RPC from env variable if set else use RPC from providers.json
191
+ let rpcList = (0, env_1.getChainRPCs)(chain, (_a = providers_json_1.default[chain]) === null || _a === void 0 ? void 0 : _a.rpc);
192
+ let archivalRPCList = (0, env_1.getArchivalRPCs)(chain);
193
+ if (!rpcList) {
194
+ // @ts-ignore (throwing error here would alter function behavior and have side effects)
195
+ return null;
196
+ }
197
+ if (!exports.providers[chain])
198
+ exports.providers[chain] = createProvider(chain, rpcList, (_b = providers_json_1.default[chain]) === null || _b === void 0 ? void 0 : _b.chainId, archivalRPCList);
199
+ return exports.providers[chain];
200
+ }
201
+ exports.getProvider = getProvider;
202
+ function toHex(n) {
203
+ if (typeof n === 'string')
204
+ return n;
205
+ return '0x' + n.toString(16);
206
+ }
207
+ const httpRPC = {
208
+ getBlockNumber: async (rpc) => {
209
+ const { data } = await axios_1.default.post(rpc, {
210
+ jsonrpc: '2.0', id: 1, params: [],
211
+ method: 'eth_blockNumber',
212
+ }, {
213
+ timeout: 3000
214
+ });
215
+ if (data.error)
216
+ throw data.error;
217
+ return parseInt(data.result);
218
+ },
219
+ getBlock: async (rpc, params) => {
220
+ params[0] = toHex(params[0]);
221
+ const { data: { result, error } } = await axios_1.default.post(rpc, {
222
+ jsonrpc: '2.0', id: 1, params,
223
+ method: 'eth_getBlockByNumber',
224
+ }, {
225
+ timeout: 3000
226
+ });
227
+ if (error)
228
+ throw error;
229
+ result.number = parseInt(result.number);
230
+ result.timestamp = parseInt(result.timestamp);
231
+ return result;
232
+ },
233
+ getBalance: async (rpc, params) => {
234
+ params[1] = toHex(params[1]);
235
+ const { data: { result, error } } = await axios_1.default.post(rpc, {
236
+ jsonrpc: '2.0', id: 1, params,
237
+ method: 'eth_getBalance',
238
+ }, {
239
+ timeout: 5000
240
+ });
241
+ if (error)
242
+ throw error;
243
+ return BigInt(result).toString();
244
+ },
245
+ call: async (rpc, params) => {
246
+ params[1] = toHex(params[1]);
247
+ const { data } = await axios_1.default.post(rpc, {
248
+ jsonrpc: '2.0', id: 1, params,
249
+ method: 'eth_call',
250
+ }, {
251
+ timeout: 5000
252
+ });
253
+ if (data.error)
254
+ throw data.error;
255
+ return data.result;
256
+ },
257
+ getLogs: async (rpc, params) => {
258
+ params[0].fromBlock = toHex(params[0].fromBlock);
259
+ params[0].toBlock = toHex(params[0].toBlock);
260
+ const { data: { result, error } } = await axios_1.default.post(rpc, {
261
+ jsonrpc: '2.0', id: 1, params,
262
+ method: 'eth_getLogs',
263
+ });
264
+ if (error)
265
+ throw error;
266
+ result.forEach((i) => {
267
+ i.blockNumber = parseInt(i.blockNumber);
268
+ i.transactionIndex = parseInt(i.transactionIndex);
269
+ i.logIndex = parseInt(i.logIndex);
270
+ i.index = i.logIndex;
271
+ });
272
+ return result;
273
+ },
274
+ };
package/build/util/env.js CHANGED
@@ -32,8 +32,10 @@ function getMaxParallelRequests(chain) {
32
32
  }
33
33
  exports.getMaxParallelRequests = getMaxParallelRequests;
34
34
  function getParallelGetLogsLimit(chain, providerUrl) {
35
- const defaultLimit = getEnvValue('GET_LOGS_CONCURRENCY_LIMIT', '5');
36
- const defaultAlchemyLimit = getEnvValue('ALCHEMY_GET_LOGS_CONCURRENCY_LIMIT', '4');
35
+ let defaultLimit = getEnvValue('GET_LOGS_CONCURRENCY_LIMIT', '25');
36
+ if (chain === 'fantom')
37
+ defaultLimit = '10';
38
+ const defaultAlchemyLimit = getEnvValue('ALCHEMY_GET_LOGS_CONCURRENCY_LIMIT', '25');
37
39
  if (providerUrl && providerUrl.includes('alchemy.com'))
38
40
  return +(defaultAlchemyLimit);
39
41
  return +(getEnvValue(`${chain}_RPC_GET_LOGS_CONCURRENCY_LIMIT`, defaultLimit));
@@ -76,9 +78,8 @@ function getBatchMaxCount(chain) {
76
78
  if (getEnvValue(key))
77
79
  return +getEnvValue(key);
78
80
  switch (chain) {
79
- case 'cronos': return 5;
80
81
  case 'zkfair': return 1;
81
- default: return +getEnvValue('BATCH_MAX_COUNT', '99');
82
+ default: return +getEnvValue('BATCH_MAX_COUNT', '5');
82
83
  }
83
84
  }
84
85
  exports.getBatchMaxCount = getBatchMaxCount;
@@ -91,10 +92,13 @@ function getArchivalRPCs(chain) {
91
92
  exports.getArchivalRPCs = getArchivalRPCs;
92
93
  function getChainRPCs(chain, defaultList = []) {
93
94
  const key = chain + '_RPC';
94
- if (getEnvValue(key))
95
- return getEnvValue(key).split(',').concat(defaultList).join(',');
96
- if (defaultList.length === 0)
97
- return undefined;
98
- return defaultList.join(',');
95
+ const envValue = getEnvValue(key);
96
+ if (defaultList.length) {
97
+ const listString = defaultList.join(',');
98
+ if (envValue)
99
+ return envValue + ',' + listString;
100
+ return listString;
101
+ }
102
+ return envValue || undefined;
99
103
  }
100
104
  exports.getChainRPCs = getChainRPCs;
@@ -6,26 +6,73 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const providers_json_1 = __importDefault(require("../providers.json"));
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const axios_1 = __importDefault(require("axios"));
9
+ const providerList = providers_json_1.default;
9
10
  async function main() {
10
- let { data: chainData } = await (0, axios_1.default)('https://chainid.network/chains.json');
11
- const existingChainIds = new Set(Object.values(providers_json_1.default).map(i => i.chainId));
12
- const existingChainNames = new Set(Object.keys(providers_json_1.default).map(i => i.toLowerCase()));
11
+ let { data: chainData } = await (0, axios_1.default)('https://chainlist.org/rpcs.json');
12
+ const providerIDMap = {};
13
+ Object.values(providerList).forEach((i) => providerIDMap[i.chainId] = i.rpc);
13
14
  chainData = chainData
14
15
  .filter((i) => i.rpc.length)
15
16
  .filter((i) => !i.status || i.status === 'active')
16
- .filter((i) => i.shortName)
17
- .filter((i) => !existingChainIds.has(i.chainId))
18
- .filter((i) => !existingChainNames.has(i.shortName.toLowerCase()));
19
- chainData.forEach((i) => {
20
- i.rpc = i.rpc.filter((j) => !/wss:/.test(j));
17
+ .filter((i) => i.shortName);
18
+ for (const i of chainData) {
19
+ i.rpc = await filterForWorkingRPCs(i.rpc.map((j) => j.url), i.name, i.chainId);
20
+ if (!i.rpc.length)
21
+ continue;
22
+ if (providerIDMap[i.chainId]) {
23
+ providerIDMap[i.chainId].push(...i.rpc);
24
+ }
25
+ else if (providerList[i.shortName]) {
26
+ providerList[i.shortName].rpc.push(...i.rpc);
27
+ }
28
+ else {
29
+ providerList[i.shortName.toLowerCase()] = {
30
+ rpc: i.rpc,
31
+ chainId: i.chainId
32
+ };
33
+ }
34
+ }
35
+ Object.values(providerList).forEach((i) => {
36
+ i.rpc = Array.from(new Set(filterRPCs(i.rpc)));
21
37
  });
22
- const newList = { ...providers_json_1.default };
23
- chainData.forEach((i) => {
24
- newList[i.shortName.toLowerCase()] = {
25
- rpc: i.rpc,
26
- chainId: i.chainId
27
- };
38
+ fs_1.default.writeFileSync(__dirname + '/../providers.json', JSON.stringify(providerList));
39
+ }
40
+ function filterRPCs(rpc) {
41
+ return rpc.filter((i) => {
42
+ if (i.endsWith('/demo'))
43
+ return false; // remove demo rpc
44
+ if (i.includes('$'))
45
+ return false; // remove anything where api key is injected
46
+ if (i.startsWith('wss://') || i.startsWith('ws://'))
47
+ return false; // remove websocket rpcs
48
+ return true;
49
+ }).map((i) => {
50
+ if (i.endsWith('/'))
51
+ return i.slice(0, -1);
52
+ return i;
28
53
  });
29
- fs_1.default.writeFileSync(__dirname + '/../providers.json', JSON.stringify(newList));
54
+ }
55
+ async function filterForWorkingRPCs(rpc, chain, chainId) {
56
+ if (rpc.length < 6)
57
+ return rpc;
58
+ rpc = filterRPCs(rpc);
59
+ const promises = await Promise.all(rpc.map(async (i) => {
60
+ try {
61
+ const { data } = await axios_1.default.post(i, {
62
+ jsonrpc: '2.0',
63
+ method: 'eth_blockNumber',
64
+ params: [],
65
+ id: 1
66
+ }, {
67
+ timeout: 3000
68
+ });
69
+ if (data.result)
70
+ return i;
71
+ }
72
+ catch (e) {
73
+ // console.log((e as any).message, i, chain)
74
+ }
75
+ }));
76
+ return rpc.filter((_i, index) => promises[index]);
30
77
  }
31
78
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defillama/sdk",
3
- "version": "5.0.13",
3
+ "version": "5.0.16",
4
4
  "description": "SDK for calculating TVL",
5
5
  "main": "build/index.js",
6
6
  "scripts": {