@defillama/sdk 5.0.14 → 5.0.18

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