@defillama/sdk 5.0.145 → 5.0.147

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.
@@ -3,9 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getTransactions = exports.getTokenTransfers = exports.isIndexer2Enabled = exports.isIndexerEnabled = exports.getLogs = exports.getTokens = exports.supportedChainSet2 = exports.supportedChainSet = void 0;
6
+ exports.isIndexer2Enabled = exports.isIndexerEnabled = exports.getTransactions = exports.getTokenTransfers = exports.getLogs = exports.getTokens = exports.supportedChainSet2 = exports.supportedChainSet = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
- const ethers_1 = require("ethers");
9
8
  const _1 = require(".");
10
9
  const general_1 = require("../general");
11
10
  const generalUtil_1 = require("../generalUtil");
@@ -14,74 +13,51 @@ const cache_1 = require("./cache");
14
13
  const debugLog_1 = require("./debugLog");
15
14
  const env_1 = require("./env");
16
15
  const logs_1 = require("./logs");
17
- const indexerURL = (0, env_1.getEnvValue)('LLAMA_INDEXER_ENDPOINT');
18
- const LLAMA_INDEXER_API_KEY = (0, env_1.getEnvValue)('LLAMA_INDEXER_API_KEY');
19
- const LLAMA_INDEXER_V2_ENDPOINT = (0, env_1.getEnvValue)('LLAMA_INDEXER_V2_ENDPOINT');
20
- const LLAMA_INDEXER_V2_API_KEY = (0, env_1.getEnvValue)('LLAMA_INDEXER_V2_API_KEY');
21
- const addressChunkSize = +(0, env_1.getEnvValue)('LLAMA_INDEXER_ADDRESS_CHUNK_SIZE');
16
+ const indexerURL = (0, env_1.getEnvValue)("LLAMA_INDEXER_ENDPOINT");
17
+ const LLAMA_INDEXER_API_KEY = (0, env_1.getEnvValue)("LLAMA_INDEXER_API_KEY");
18
+ const LLAMA_INDEXER_V2_ENDPOINT = (0, env_1.getEnvValue)("LLAMA_INDEXER_V2_ENDPOINT");
19
+ const LLAMA_INDEXER_V2_API_KEY = (0, env_1.getEnvValue)("LLAMA_INDEXER_V2_API_KEY");
20
+ const addressChunkSize = +(0, env_1.getEnvValue)("LLAMA_INDEXER_ADDRESS_CHUNK_SIZE") || 100;
21
+ // v1 chains (still used for balances endpoint) -------------------------------------------------
22
22
  const indexerChainIdChainMapping = {
23
- 1: 'ethereum',
24
- 10: 'optimism',
25
- 56: 'bsc',
26
- 100: 'xdai',
27
- 137: 'polygon',
28
- 204: 'op_bnb',
29
- 250: 'fantom',
30
- 324: 'era',
31
- 1101: 'polygon_zkevm',
32
- 8453: 'base',
33
- 34443: 'mode',
34
- 42170: 'arbitrum_nova',
35
- 42161: 'arbitrum',
36
- 43114: 'avax',
37
- 59144: 'linea',
38
- 81457: 'blast',
39
- 534352: 'scroll',
40
- 146: 'sonic',
23
+ 1: "ethereum",
24
+ 10: "optimism",
25
+ 56: "bsc",
26
+ 100: "xdai",
27
+ 137: "polygon",
28
+ 204: "op_bnb",
29
+ 250: "fantom",
30
+ 324: "era",
31
+ 1101: "polygon_zkevm",
32
+ 8453: "base",
33
+ 34443: "mode",
34
+ 42170: "arbitrum_nova",
35
+ 42161: "arbitrum",
36
+ 43114: "avax",
37
+ 59144: "linea",
38
+ 81457: "blast",
39
+ 534352: "scroll",
40
+ 146: "sonic",
41
41
  };
42
+ // v2 chains (superset of v1)
42
43
  const indexer2ChainIdChainMapping = {
43
44
  ...indexerChainIdChainMapping,
44
- 130: 'unichain',
45
- 1868: 'soneium',
46
- 80094: 'berachain',
47
- 999: 'hyperliquid'
45
+ 130: "unichain",
46
+ 1868: "soneium",
47
+ 80094: "berachain",
48
+ 999: "hyperliquid",
48
49
  };
49
- const state = { chainIndexStatus: {} };
50
- const cacheTime = 1 * 60 * 1000; // 1 minutes - we cache sync status for 1 minute
51
- async function getChainIndexStatus(version = 'v1') {
52
- checkIndexerConfig(version);
53
- if (state.timestamp && (Date.now() - state.timestamp) < cacheTime)
54
- return state.chainIndexStatus;
55
- state.timestamp = Date.now();
56
- state.chainIndexStatus = _getState();
57
- return state.chainIndexStatus;
58
- async function _getState() {
59
- const { data: { syncStatus } } = await axiosInstances[version].get(`/sync`);
60
- const syncInfo = {};
61
- syncStatus.forEach((d) => {
62
- const chain = indexerConfigs[version].chainMapping[d.chain];
63
- if (chain) {
64
- syncInfo[chain] = {
65
- block: d.lastIndexedBlock,
66
- timestamp: +new Date(d.lastIndexedDate)
67
- };
68
- }
69
- });
70
- state.chainIndexStatus = syncInfo;
71
- return syncInfo;
72
- }
73
- }
74
50
  const indexerConfigs = {
75
51
  v1: {
76
52
  endpoint: indexerURL,
77
53
  apiKey: LLAMA_INDEXER_API_KEY,
78
- chainMapping: indexerChainIdChainMapping
54
+ chainMapping: indexerChainIdChainMapping,
79
55
  },
80
56
  v2: {
81
57
  endpoint: LLAMA_INDEXER_V2_ENDPOINT,
82
58
  apiKey: LLAMA_INDEXER_V2_API_KEY,
83
- chainMapping: indexer2ChainIdChainMapping
84
- }
59
+ chainMapping: indexer2ChainIdChainMapping,
60
+ },
85
61
  };
86
62
  const axiosInstances = {
87
63
  v1: axios_1.default.create({
@@ -91,53 +67,83 @@ const axiosInstances = {
91
67
  v2: axios_1.default.create({
92
68
  headers: { "x-api-key": indexerConfigs.v2.apiKey },
93
69
  baseURL: indexerConfigs.v2.endpoint,
94
- })
70
+ }),
95
71
  };
96
72
  function checkIndexerConfig(version) {
97
73
  const config = indexerConfigs[version];
98
74
  if (!config.endpoint || !config.apiKey)
99
75
  throw new Error(`Llama Indexer ${version} URL/api key is not set`);
100
76
  }
101
- function getChainId(chain, version = 'v1') {
77
+ function getChainId(chain, version = "v2") {
102
78
  var _a;
103
- const chainId = (_a = Object.entries(indexerConfigs[version].chainMapping)
104
- .find(([_, chainName]) => chainName === chain)) === null || _a === void 0 ? void 0 : _a[0];
79
+ const chainId = (_a = Object.entries(indexerConfigs[version].chainMapping).find(([, chainName]) => chainName === chain)) === null || _a === void 0 ? void 0 : _a[0];
105
80
  if (!chainId)
106
- throw new Error('Chain not supported');
81
+ throw new Error("Chain not supported");
107
82
  return +chainId;
108
83
  }
109
- function getSupportedChains(version = 'v1') {
84
+ function getSupportedChains(version = "v2") {
110
85
  return new Set(Object.values(indexerConfigs[version].chainMapping));
111
86
  }
112
- exports.supportedChainSet = getSupportedChains('v1');
113
- exports.supportedChainSet2 = getSupportedChains('v2');
87
+ exports.supportedChainSet = getSupportedChains("v1"); // legacy
88
+ exports.supportedChainSet2 = getSupportedChains("v2");
114
89
  const chainToIDMapping = {};
115
90
  Object.entries(indexerConfigs.v1.chainMapping).forEach(([id, chain]) => {
116
- chainToIDMapping[chain] = id;
91
+ chainToIDMapping[chain] = +id;
117
92
  });
118
93
  const chainToIDMapping2 = {};
119
94
  Object.entries(indexerConfigs.v2.chainMapping).forEach(([id, chain]) => {
120
- chainToIDMapping2[chain] = id;
95
+ chainToIDMapping2[chain] = +id;
121
96
  });
97
+ const state = {
98
+ chainIndexStatus: {},
99
+ };
100
+ const cacheTime = 1 * 60 * 1000; // 1 min
101
+ async function getChainIndexStatus(version = "v2") {
102
+ checkIndexerConfig(version);
103
+ if (state.timestamp && Date.now() - state.timestamp < cacheTime)
104
+ return state.chainIndexStatus;
105
+ state.timestamp = Date.now();
106
+ state.chainIndexStatus = (async () => {
107
+ const { data: { syncStatus }, } = await axiosInstances[version].get(`/sync`);
108
+ const info = {};
109
+ syncStatus.forEach((d) => {
110
+ const chain = indexerConfigs[version].chainMapping[d.chain];
111
+ if (chain) {
112
+ info[chain] = {
113
+ block: d.lastIndexedBlock,
114
+ timestamp: +new Date(d.lastIndexedDate),
115
+ };
116
+ }
117
+ });
118
+ return (state.chainIndexStatus = info);
119
+ })();
120
+ return state.chainIndexStatus;
121
+ }
122
+ // ---------------------------------------------------------------------------
123
+ // Utilities / Enums / Types
124
+ // ---------------------------------------------------------------------------
122
125
  var TokenTypes;
123
126
  (function (TokenTypes) {
124
127
  TokenTypes["ERC20"] = "erc20";
125
128
  TokenTypes["ERC721"] = "erc721";
126
129
  })(TokenTypes || (TokenTypes = {}));
130
+ // ---------------------------------------------------------------------------
131
+ // Token helpers (still via v1 endpoint)
132
+ // ---------------------------------------------------------------------------
127
133
  async function getTokens(address, { onlyWhitelisted = true, skipCacheRead = false, skipCache = false, chain, tokenType, } = {}) {
128
134
  var _a, _b;
129
- checkIndexerConfig('v1');
135
+ checkIndexerConfig("v1");
130
136
  if (!address)
131
- throw new Error('Address is required either as a string or an array of strings');
137
+ throw new Error("Address is required either as a string or an array of strings");
132
138
  if (Array.isArray(address) && !address.length)
133
- throw new Error('Address array cannot be empty');
139
+ throw new Error("Address array cannot be empty");
134
140
  if (Array.isArray(address))
135
- address = address.join(',');
141
+ address = address.join(",");
136
142
  address = address.toLowerCase();
137
143
  let chainId;
138
144
  if (chain)
139
145
  chainId = chainToIDMapping[chain];
140
- const project = 'llama-indexer-cache';
146
+ const project = "llama-indexer-cache";
141
147
  const key = onlyWhitelisted ? address : `${address}/all`;
142
148
  const file = `${project}/${key}`;
143
149
  const timeNow = Math.floor(Date.now() / 1e3);
@@ -145,28 +151,29 @@ async function getTokens(address, { onlyWhitelisted = true, skipCacheRead = fals
145
151
  let cache = {};
146
152
  if (!skipCacheRead && !skipCache) {
147
153
  cache = (_a = (await (0, cache_1.readCache)(file))) !== null && _a !== void 0 ? _a : {};
148
- if (cache.timestamp && (timeNow - cache.timestamp) < THREE_DAYS)
154
+ if (cache.timestamp && timeNow - cache.timestamp < THREE_DAYS)
149
155
  return cache.tokens;
150
156
  }
151
- (0, debugLog_1.debugLog)('[Indexer] Pulling tokens for ' + address);
157
+ (0, debugLog_1.debugLog)("[Indexer] Pulling tokens for " + address);
152
158
  const tokens = (_b = cache.tokens) !== null && _b !== void 0 ? _b : {};
153
- const { data: { balances } } = await axiosInstances.v1(`/balances`, {
159
+ const { data: { balances }, } = await axiosInstances.v1(`/balances`, {
154
160
  params: {
155
161
  addresses: address,
156
162
  chainId,
157
163
  type: tokenType,
158
164
  },
159
165
  });
160
- balances.filter((b) => +b.total_amount > 0).forEach((b) => {
166
+ balances
167
+ .filter((b) => +b.total_amount > 0)
168
+ .forEach((b) => {
161
169
  const chain = indexerConfigs.v1.chainMapping[b.chain];
162
- if (!chain) {
170
+ if (!chain)
163
171
  return;
164
- }
165
172
  if (!tokens[chain])
166
173
  tokens[chain] = [];
167
174
  tokens[chain].push(b.address);
168
175
  });
169
- const tokenCache = { timestamp: timeNow, tokens, };
176
+ const tokenCache = { timestamp: timeNow, tokens };
170
177
  Object.entries(tokens).forEach(([chain, values]) => {
171
178
  values.push(general_1.ETHER_ADDRESS);
172
179
  tokens[chain] = (0, generalUtil_1.getUniqueAddresses)(values);
@@ -176,239 +183,187 @@ async function getTokens(address, { onlyWhitelisted = true, skipCacheRead = fals
176
183
  return tokens;
177
184
  }
178
185
  exports.getTokens = getTokens;
179
- async function getIndexerVersionForBlock(chain, blockNumber) {
180
- var _a, _b;
181
- if (!isIndexer2Enabled(chain)) {
182
- if (!isIndexerEnabled(chain)) {
183
- throw new Error(`Indexer not enabled for chain ${chain}`);
184
- }
185
- return 'v1';
186
- }
187
- const syncStatus = await getChainIndexStatus('v2');
188
- const lastIndexedBlock = (_b = (_a = syncStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
189
- if (lastIndexedBlock >= blockNumber) {
190
- return 'v2';
191
- }
192
- return 'v1';
193
- }
194
- async function executeWithFallback(chain, blockNumber, v2Fn, v1Fn) {
195
- const version = await getIndexerVersionForBlock(chain, blockNumber);
196
- try {
197
- return version === 'v2' ? await v2Fn() : await v1Fn();
198
- }
199
- catch (error) {
200
- if (version === 'v2') {
201
- console.log(`Fallback to v1 for chain ${chain} due to error:`, error);
202
- return v1Fn();
203
- }
204
- throw error;
205
- }
206
- }
207
- async function getLogs({ chain = 'ethereum', topic, topics, fromBlock, toBlock, all = true, limit = 1000, offset = 0, target, targets, eventAbi, entireLog = false, flatten = true, extraTopics, fromTimestamp, toTimestamp, debugMode = false, noTarget = false, parseLog = true, }) {
186
+ // ---------------------------------------------------------------------------
187
+ // LOGS (v2 only)
188
+ // ---------------------------------------------------------------------------
189
+ async function getLogs(options) {
208
190
  var _a, _b, _c;
191
+ let { all = true, limit = 1000, offset: initialOffset = 0, target, targets = [], flatten = true, debugMode = false, noTarget = false, maxBlockRange, processor, } = options;
192
+ const { chain, fromBlock, toBlock, topic, topic1, topic2, topic3, transformLog, } = await (0, logs_1.getLogParams)(options, true);
209
193
  if (!debugMode)
210
194
  debugMode = debugLog_1.DEBUG_LEVEL2;
211
- const version = await getIndexerVersionForBlock(chain, toBlock !== null && toBlock !== void 0 ? toBlock : 0);
212
- checkIndexerConfig(version);
213
- const chainId = getChainId(chain, version);
214
- let topic1;
215
- let topic2;
216
- let topic3;
217
- if (((topics === null || topics === void 0 ? void 0 : topics.length) && topics.length > 1)) {
218
- if (topics === null || topics === void 0 ? void 0 : topics.length) {
219
- topic1 = topics[1];
220
- topic2 = topics[2];
221
- topic3 = topics[3];
222
- }
195
+ const blockRange = toBlock - fromBlock;
196
+ const effectiveMaxBlockRange = maxBlockRange !== null && maxBlockRange !== void 0 ? maxBlockRange : (noTarget ? 10000 : Infinity);
197
+ if (noTarget && blockRange > 500000) {
198
+ throw new Error("When noTarget is true, block range must be less than 500k blocks. Please narrow down your block range.");
223
199
  }
224
- else if (extraTopics === null || extraTopics === void 0 ? void 0 : extraTopics.length) {
225
- topic1 = extraTopics[0];
226
- topic2 = extraTopics[1];
227
- topic3 = extraTopics[2];
200
+ // We now only support v2 for logs ---------------------------------------------------------
201
+ checkIndexerConfig("v2");
202
+ const chainId = getChainId(chain, "v2");
203
+ // Ensure the indexer is synced far enough
204
+ const chainIndexStatus = await getChainIndexStatus("v2");
205
+ const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
206
+ if (lastIndexedBlock < toBlock) {
207
+ throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}`);
228
208
  }
229
- if (topics === null || topics === void 0 ? void 0 : topics.length)
230
- topic = topics[0];
231
- if (!eventAbi)
232
- entireLog = true;
233
- if (!noTarget && !target && !(targets === null || targets === void 0 ? void 0 : targets.length))
234
- throw new Error('target|targets is required or set the flag "noTarget" to true');
235
- if (!fromBlock && !fromTimestamp)
236
- throw new Error('fromBlock or fromTimestamp is required');
237
- if (!toBlock && !toTimestamp)
238
- throw new Error('toBlock or toTimestamp is required');
239
- if (!fromBlock)
240
- fromBlock = await (0, blocks_1.getBlockNumber)(chain, fromTimestamp);
241
- if (!toBlock)
242
- toBlock = await (0, blocks_1.getBlockNumber)(chain, toTimestamp);
243
- if (!fromBlock || !toBlock)
244
- throw new Error('fromBlock and toBlock must be > 0');
245
- if (version === 'v1') {
246
- const chainIndexStatus = await getChainIndexStatus('v1');
247
- const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
248
- if (lastIndexedBlock < toBlock)
249
- throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}`);
209
+ // Re‑curse if the requested range is too large ------------------------------------------------
210
+ if (blockRange > effectiveMaxBlockRange) {
211
+ const results = [];
212
+ for (let currentFromBlock = fromBlock; currentFromBlock < toBlock; currentFromBlock += effectiveMaxBlockRange) {
213
+ const currentToBlock = Math.min(currentFromBlock + effectiveMaxBlockRange - 1, toBlock);
214
+ const chunk = await getLogs({ ...options, fromBlock: currentFromBlock, toBlock: currentToBlock });
215
+ results.push(chunk);
216
+ }
217
+ if (flatten || !(targets === null || targets === void 0 ? void 0 : targets.length))
218
+ return results.flat();
219
+ return targets.map((_, i) => results.map((r) => r[i]).flat());
250
220
  }
221
+ // -------------------------------------------------------------------------
222
+ // Prepare address filters
223
+ // -------------------------------------------------------------------------
251
224
  let address = target;
252
- if (typeof target === 'string')
225
+ if (typeof target === "string")
253
226
  targets = [target];
254
- if (Array.isArray(targets))
255
- address = targets.join(',');
256
- const hasAddressFilter = address && address.length;
227
+ if (Array.isArray(targets) && targets.length)
228
+ address = targets.join(",");
229
+ const hasAddressFilter = !!(address === null || address === void 0 ? void 0 : address.length);
257
230
  if (address)
258
231
  address = address.toLowerCase();
259
- // Create an Interface object
260
- let iface;
261
- if (eventAbi)
262
- iface = new ethers_1.Interface([eventAbi]);
263
- if (topic)
264
- topic = (0, logs_1.toFilterTopic)(topic);
265
- else if (eventAbi)
266
- topic = (0, logs_1.toFilterTopic)(eventAbi);
267
- let hasMore = true;
268
- let logs = [];
232
+ const addressSet = new Set((address !== null && address !== void 0 ? address : "").split(",").filter(Boolean));
233
+ const addressChunks = (0, _1.sliceIntoChunks)((_c = address === null || address === void 0 ? void 0 : address.split(",")) !== null && _c !== void 0 ? _c : [], addressChunkSize);
234
+ if (noTarget && addressChunks.length === 0)
235
+ addressChunks.push(undefined);
236
+ // -------------------------------------------------------------------------
237
+ // Pull logs
238
+ // -------------------------------------------------------------------------
239
+ const rawLogs = [];
269
240
  const debugTimeKey = `Indexer-getLogs-${chain}-${topic}-${address}_${Math.random()}`;
270
241
  if (debugMode) {
271
- (0, debugLog_1.debugLog)('[Indexer] Pulling logs ' + debugTimeKey);
242
+ (0, debugLog_1.debugLog)("[Indexer] Pulling logs " + debugTimeKey);
272
243
  console.time(debugTimeKey);
273
244
  }
274
- const addressSet = new Set(address === null || address === void 0 ? void 0 : address.split(','));
275
- const addressChunks = (0, _1.sliceIntoChunks)((_c = address === null || address === void 0 ? void 0 : address.split(',')) !== null && _c !== void 0 ? _c : [], addressChunkSize);
276
- // to ensure that we have at least one chunk to process if no target is provided
277
- if (noTarget && addressChunks.length === 0)
278
- addressChunks.push(undefined);
279
245
  for (const chunk of addressChunks) {
280
- if (Array.isArray(chunk) && chunk.length === 0) {
281
- throw new Error('Address chunk cannot be empty');
282
- }
246
+ if (Array.isArray(chunk) && chunk.length === 0)
247
+ throw new Error("Address chunk cannot be empty");
248
+ let chunkOffset = initialOffset;
283
249
  let logCount = 0;
250
+ let hasMore = true;
284
251
  do {
285
252
  const params = {
286
- addresses: hasAddressFilter ? chunk.join(',') : undefined,
253
+ addresses: hasAddressFilter ? chunk === null || chunk === void 0 ? void 0 : chunk.join(",") : undefined,
287
254
  chainId,
288
255
  topic0: topic,
289
256
  from_block: fromBlock,
290
257
  to_block: toBlock,
291
- topic1, topic2, topic3,
292
- limit, offset,
258
+ topic1,
259
+ topic2,
260
+ topic3,
261
+ limit,
262
+ offset: chunkOffset,
293
263
  noTarget,
264
+ eventAbi: options.eventAbi,
265
+ parseLog: options.parseLog,
266
+ onlyArgs: options.onlyArgs,
294
267
  };
295
- const { data: { logs: _logs, totalCount } } = await executeWithFallback(chain, toBlock, () => axiosInstances.v2(`/logs`, { params }), () => axiosInstances.v1(`/logs`, { params }));
296
- // getLogs uses 'address' field to return log source, so we add the field here to make it compatible
297
- _logs.forEach((l) => {
298
- l.address = l.source;
299
- const iswhitelisted = !addressSet.size || addressSet.has(l.source.toLowerCase());
300
- if (iswhitelisted) {
301
- logs.push(l);
268
+ const { data: { logs: _logs, totalCount }, } = await axiosInstances.v2(`/logs`, { params });
269
+ const filtered = _logs.filter((l) => {
270
+ const isWhitelisted = !addressSet.size || addressSet.has(l.source.toLowerCase());
271
+ if (!isWhitelisted)
272
+ return false;
273
+ if (l.block_number) {
274
+ l.blockNumber = parseInt(l.block_number);
302
275
  }
276
+ return true;
303
277
  });
278
+ const transformed = filtered.map(transformLog);
279
+ if (processor)
280
+ await processor(transformed);
281
+ rawLogs.push(...transformed);
304
282
  logCount += _logs.length;
305
- offset += limit;
306
- // If we have all the logs, or we have reached the limit, or there are no logs, we stop
283
+ chunkOffset += limit;
307
284
  if (_logs.length < limit || totalCount <= logCount || _logs.length === 0)
308
285
  hasMore = false;
309
286
  } while (all && hasMore);
310
287
  }
311
288
  if (debugMode) {
312
289
  console.timeEnd(debugTimeKey);
313
- (0, debugLog_1.debugLog)('Logs pulled ' + chain, address, logs.length);
290
+ (0, debugLog_1.debugLog)("Logs pulled " + chain, address, rawLogs.length);
314
291
  }
315
- const mappedLogs = [];
316
- let addressIndexMap = {};
292
+ // -------------------------------------------------------------------------
293
+ // Map / transform result
294
+ // -------------------------------------------------------------------------
317
295
  const splitByAddress = (targets === null || targets === void 0 ? void 0 : targets.length) && !flatten;
318
296
  if (splitByAddress) {
319
- for (let i = 0; i < targets.length; i++) {
320
- addressIndexMap[targets[i].toLowerCase()] = i;
321
- mappedLogs.push([]);
322
- }
297
+ const mapped = targets.map(() => []);
298
+ const indexMap = {};
299
+ targets.forEach((t, i) => (indexMap[t.toLowerCase()] = i));
300
+ rawLogs.forEach((log) => {
301
+ const idx = indexMap[log.source.toLowerCase()];
302
+ if (idx === undefined)
303
+ return; // ignore unknown sources
304
+ mapped[idx].push(log);
305
+ });
306
+ return mapped;
323
307
  }
324
- logs = logs.map((log) => {
325
- const source = log.source.toLowerCase();
326
- log.logIndex = log.log_index;
327
- log.index = log.log_index;
328
- log.transactionHash = log.transaction_hash;
329
- log.blockNumber = log.block_number;
330
- log.topics = [log.topic0, log.topic1, log.topic2, log.topic3,].filter(t => t !== '').map(i => ethers_1.ethers.zeroPadValue(i, 32));
331
- const deleteKeys = ['chain', 'block_number', 'log_index', 'topic0', 'topic1', 'topic2', 'topic3', 'decodedArgs', 'transaction_hash',];
332
- deleteKeys.forEach(k => delete log[k]);
333
- const parsedLog = iface === null || iface === void 0 ? void 0 : iface.parseLog({ data: log.data, topics: log.topics, });
334
- log.args = parsedLog === null || parsedLog === void 0 ? void 0 : parsedLog.args;
335
- log = !entireLog ? log.args : log;
336
- if (entireLog && parseLog)
337
- log.parsedLog = parsedLog;
338
- if (splitByAddress) {
339
- const index = addressIndexMap[source];
340
- mappedLogs[index].push(log);
341
- }
342
- return log;
343
- });
344
- if (splitByAddress)
345
- return mappedLogs;
346
- return logs;
308
+ return rawLogs;
347
309
  }
348
310
  exports.getLogs = getLogs;
349
- function isIndexerEnabled(chain) {
350
- if (!indexerConfigs.v1.endpoint)
351
- return false;
352
- if (chain && !exports.supportedChainSet.has(chain))
353
- return false;
354
- return true;
355
- }
356
- exports.isIndexerEnabled = isIndexerEnabled;
357
- function isIndexer2Enabled(chain) {
358
- if (!indexerConfigs.v2.endpoint)
359
- return false;
360
- if (chain && !exports.supportedChainSet2.has(chain))
361
- return false;
362
- return true;
363
- }
364
- exports.isIndexer2Enabled = isIndexer2Enabled;
365
- async function getTokenTransfers({ chain = 'ethereum', fromAddressFilter, fromBlock, toBlock, all = true, limit = 1000, offset = 0, target, targets = [], flatten = true, fromTimestamp, toTimestamp, debugMode = false, transferType = 'in', token, tokens, }) {
311
+ // ---------------------------------------------------------------------------
312
+ // TOKEN TRANSFERS (v2 only)
313
+ // ---------------------------------------------------------------------------
314
+ async function getTokenTransfers({ chain = "ethereum", fromAddressFilter, fromBlock, toBlock, all = true, limit = 1000, offset = 0, target, targets = [], flatten = true, fromTimestamp, toTimestamp, debugMode = false, transferType = "in", token, tokens, }) {
366
315
  var _a, _b;
367
316
  if (!debugMode)
368
317
  debugMode = debugLog_1.DEBUG_LEVEL2;
369
- const version = await getIndexerVersionForBlock(chain, toBlock !== null && toBlock !== void 0 ? toBlock : 0);
370
- checkIndexerConfig(version);
371
- const chainId = getChainId(chain, version);
372
- const fromFilterEnabled = fromAddressFilter && fromAddressFilter.length;
373
- if (fromAddressFilter && typeof fromAddressFilter === 'string')
318
+ checkIndexerConfig("v2");
319
+ const chainId = getChainId(chain, "v2");
320
+ // -----------------------------------------------------------------------
321
+ // Input validation & pre‑processing
322
+ // -----------------------------------------------------------------------
323
+ const fromFilterEnabled = !!(fromAddressFilter === null || fromAddressFilter === void 0 ? void 0 : fromAddressFilter.length);
324
+ if (typeof fromAddressFilter === "string")
374
325
  fromAddressFilter = [fromAddressFilter];
375
326
  const fromFilterSet = new Set((fromAddressFilter !== null && fromAddressFilter !== void 0 ? fromAddressFilter : []).map((a) => a.toLowerCase()));
376
327
  if (!fromBlock && !fromTimestamp)
377
- throw new Error('fromBlock or fromTimestamp is required');
328
+ throw new Error("fromBlock or fromTimestamp is required");
378
329
  if (!toBlock && !toTimestamp)
379
- throw new Error('toBlock or toTimestamp is required');
330
+ throw new Error("toBlock or toTimestamp is required");
380
331
  if (!fromBlock)
381
332
  fromBlock = await (0, blocks_1.getBlockNumber)(chain, fromTimestamp);
382
333
  if (!toBlock)
383
334
  toBlock = await (0, blocks_1.getBlockNumber)(chain, toTimestamp);
384
335
  if (!fromBlock || !toBlock)
385
- throw new Error('fromBlock and toBlock must be > 0');
336
+ throw new Error("fromBlock and toBlock must be > 0");
386
337
  if (token)
387
338
  tokens = [token];
388
339
  if (tokens) {
389
- if (typeof tokens === 'string')
340
+ if (typeof tokens === "string")
390
341
  tokens = [tokens];
391
- tokens = tokens.join(',').toLowerCase();
342
+ tokens = tokens.join(",").toLowerCase();
392
343
  }
393
344
  if (target)
394
345
  targets = [target];
395
346
  if (!targets.length)
396
- throw new Error('target|targets is required');
397
- targets = targets.map(t => t.toLowerCase());
398
- let addresses = targets.join(',');
399
- if (version === 'v1') {
400
- const chainIndexStatus = await getChainIndexStatus('v1');
401
- const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
402
- if (lastIndexedBlock < toBlock)
403
- throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}`);
347
+ throw new Error("target|targets is required");
348
+ targets = targets.map((t) => t.toLowerCase());
349
+ const addresses = targets.join(",");
350
+ // Ensure v2 indexer is up‑to‑date
351
+ const chainIndexStatus = await getChainIndexStatus("v2");
352
+ const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
353
+ if (lastIndexedBlock < toBlock) {
354
+ throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}`);
404
355
  }
405
- let hasMore = true;
406
- let logs = [];
356
+ // -----------------------------------------------------------------------
357
+ // Fetch transfers
358
+ // -----------------------------------------------------------------------
359
+ const rawTransfers = [];
407
360
  const debugTimeKey = `Indexer-tokenTransfers-${chain}-${addresses}_${Math.random()}`;
408
361
  if (debugMode) {
409
- (0, debugLog_1.debugLog)('[Indexer] Pulling token transfers ' + debugTimeKey);
362
+ (0, debugLog_1.debugLog)("[Indexer] Pulling token transfers " + debugTimeKey);
410
363
  console.time(debugTimeKey);
411
364
  }
365
+ let currentOffset = offset;
366
+ let hasMore = true;
412
367
  do {
413
368
  const params = {
414
369
  addresses,
@@ -416,161 +371,149 @@ async function getTokenTransfers({ chain = 'ethereum', fromAddressFilter, fromBl
416
371
  from_block: fromBlock,
417
372
  to_block: toBlock,
418
373
  limit,
419
- offset,
374
+ offset: currentOffset,
420
375
  tokens,
421
376
  from_address: false,
422
377
  to_address: false,
423
378
  };
424
379
  switch (transferType) {
425
- case 'in':
380
+ case "in":
426
381
  params.to_address = true;
427
382
  break;
428
- case 'out':
383
+ case "out":
429
384
  params.from_address = true;
430
385
  break;
431
- case 'all':
386
+ case "all":
432
387
  params.from_address = true;
433
388
  params.to_address = true;
434
389
  break;
435
390
  default:
436
- throw new Error('Invalid transferType');
391
+ throw new Error("Invalid transferType");
437
392
  }
438
- const { data: { transfers: _logs, totalCount } } = await executeWithFallback(chain, toBlock, () => axiosInstances.v2(`/token-transfers`, { params }), () => axiosInstances.v1(`/token-transfers`, { params }));
439
- logs.push(..._logs);
440
- offset += limit;
441
- // If we have all the logs, or we have reached the limit, or there are no logs, we stop
442
- if (_logs.length < limit || totalCount <= logs.length || _logs.length === 0)
393
+ const { data: { transfers: _logs, totalCount }, } = await axiosInstances.v2(`/token-transfers`, { params });
394
+ rawTransfers.push(..._logs);
395
+ currentOffset += limit;
396
+ if (_logs.length < limit || totalCount <= rawTransfers.length || _logs.length === 0)
443
397
  hasMore = false;
444
398
  } while (all && hasMore);
445
- logs = logs.filter((l) => {
399
+ const filteredTransfers = rawTransfers.filter((l) => {
446
400
  if (!fromFilterEnabled)
447
401
  return true;
448
- return fromFilterSet.has(l === null || l === void 0 ? void 0 : l.from_address.toLowerCase());
402
+ return fromFilterSet.has(l.from_address.toLowerCase());
449
403
  });
450
404
  if (debugMode) {
451
405
  console.timeEnd(debugTimeKey);
452
- (0, debugLog_1.debugLog)('Token Transfers pulled ' + chain, addresses, logs.length);
406
+ (0, debugLog_1.debugLog)("Token Transfers pulled " + chain, addresses, filteredTransfers.length);
453
407
  }
454
- const mappedLogs = [];
455
- let addressIndexMap = {};
408
+ // -----------------------------------------------------------------------
409
+ // Split result if requested
410
+ // -----------------------------------------------------------------------
456
411
  const splitByAddress = (targets === null || targets === void 0 ? void 0 : targets.length) && !flatten;
457
412
  if (splitByAddress) {
458
- for (let i = 0; i < targets.length; i++) {
459
- addressIndexMap[targets[i]] = i;
460
- mappedLogs.push([]);
461
- }
462
- }
463
- if (splitByAddress) {
464
- logs.forEach((log) => {
465
- const sourceField = transferType === 'in' ? 'to_address' : 'from_address';
466
- const source = log[sourceField].toLowerCase();
467
- const index = addressIndexMap[source];
468
- mappedLogs[index].push(log);
413
+ const mapped = targets.map(() => []);
414
+ const indexMap = {};
415
+ targets.forEach((t, i) => (indexMap[t] = i));
416
+ filteredTransfers.forEach((log) => {
417
+ const sourceField = transferType === "in" ? "to_address" : "from_address";
418
+ const idx = indexMap[log[sourceField].toLowerCase()];
419
+ if (idx !== undefined)
420
+ mapped[idx].push(log);
469
421
  });
470
- return mappedLogs;
422
+ return mapped;
471
423
  }
472
- return logs;
424
+ return filteredTransfers;
473
425
  }
474
426
  exports.getTokenTransfers = getTokenTransfers;
475
- async function getTransactions({ chain = 'ethereum', addresses, transaction_hashes, from_block, to_block, all = true, limit = 1000, offset = 0, debugMode = false, transactionType = 'from' }) {
427
+ // ---------------------------------------------------------------------------
428
+ // TRANSACTIONS (v2 only)
429
+ // ---------------------------------------------------------------------------
430
+ async function getTransactions({ chain = "ethereum", addresses, transaction_hashes, from_block, to_block, all = true, limit = 1000, offset = 0, debugMode = false, transactionType = "from", }) {
476
431
  var _a, _b;
477
432
  if (!debugMode)
478
433
  debugMode = debugLog_1.DEBUG_LEVEL2;
479
- checkIndexerConfig('v2');
480
- const chainId = getChainId(chain, 'v2');
481
- if ((!addresses || addresses.length === 0) && (!transaction_hashes || transaction_hashes.length === 0)) {
434
+ checkIndexerConfig("v2");
435
+ const chainId = getChainId(chain, "v2");
436
+ if ((!addresses || addresses.length === 0) && (!transaction_hashes || transaction_hashes.length === 0))
482
437
  throw new Error("You must provide at least 'addresses' or 'transaction_hashes'");
483
- }
484
- if (!from_block || !to_block) {
438
+ if (!from_block || !to_block)
485
439
  throw new Error("'from_block' and 'to_block' are required to search for transactions");
486
- }
487
- const chainIndexStatus = await getChainIndexStatus('v2');
440
+ const chainIndexStatus = await getChainIndexStatus("v2");
488
441
  const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
489
442
  if (to_block > lastIndexedBlock) {
490
443
  throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${to_block}`);
491
444
  }
492
- const apiParams = { chainId };
445
+ const params = { chainId };
493
446
  if (addresses) {
494
- if (Array.isArray(addresses)) {
495
- apiParams.addresses = addresses.map((a) => a.toLowerCase()).join(',');
496
- }
497
- else {
498
- apiParams.addresses = addresses.toLowerCase();
499
- }
447
+ params.addresses = Array.isArray(addresses)
448
+ ? addresses.map((a) => a.toLowerCase()).join(",")
449
+ : addresses.toLowerCase();
500
450
  }
501
451
  if (transaction_hashes) {
502
- if (Array.isArray(transaction_hashes)) {
503
- apiParams.transaction_hashes = transaction_hashes.map((h) => h.toLowerCase()).join(',');
504
- }
505
- else {
506
- apiParams.transaction_hashes = transaction_hashes.toLowerCase();
507
- }
452
+ params.transaction_hashes = Array.isArray(transaction_hashes)
453
+ ? transaction_hashes.map((h) => h.toLowerCase()).join(",")
454
+ : transaction_hashes.toLowerCase();
508
455
  }
509
- apiParams.from_block = from_block;
510
- apiParams.to_block = to_block;
456
+ params.from_block = from_block;
457
+ params.to_block = to_block;
511
458
  if (offset)
512
- apiParams.offset = offset;
513
- let from_address = false;
514
- let to_address = false;
515
- switch (transactionType) {
516
- case 'from':
517
- from_address = true;
518
- break;
519
- case 'to':
520
- to_address = true;
521
- break;
522
- case 'all':
523
- default:
524
- from_address = true;
525
- to_address = true;
526
- break;
527
- }
528
- apiParams.from_address = from_address;
529
- apiParams.to_address = to_address;
530
- if (all) {
531
- apiParams.limit = 'all';
532
- }
533
- else {
534
- if (limit !== 'all' && limit !== 0) {
535
- apiParams.limit = limit;
536
- }
537
- }
459
+ params.offset = offset;
460
+ params.from_address = transactionType !== "to";
461
+ params.to_address = transactionType !== "from";
462
+ if (all)
463
+ params.limit = "all";
464
+ else if (limit !== "all" && limit !== 0)
465
+ params.limit = limit;
538
466
  const debugTimeKey = `Indexer-getTransactions-${chain}-${addresses || transaction_hashes}-${from_block}-${to_block}_${Math.random()}`;
539
467
  if (debugMode) {
540
- (0, debugLog_1.debugLog)('[Indexer] Pulling transactions ' + debugTimeKey);
468
+ (0, debugLog_1.debugLog)("[Indexer] Pulling transactions " + debugTimeKey);
541
469
  console.time(debugTimeKey);
542
470
  }
543
- const { data: { transactions } } = await axiosInstances.v2(`/transactions`, {
544
- params: apiParams,
545
- });
471
+ const { data: { transactions } } = await axiosInstances.v2(`/transactions`, { params });
546
472
  if (debugMode) {
547
473
  console.timeEnd(debugTimeKey);
548
- (0, debugLog_1.debugLog)('Transactions pulled ' + chain, addresses || transaction_hashes, (transactions === null || transactions === void 0 ? void 0 : transactions.length) || 0);
474
+ (0, debugLog_1.debugLog)("Transactions pulled " + chain, addresses || transaction_hashes, (transactions === null || transactions === void 0 ? void 0 : transactions.length) || 0);
549
475
  }
550
476
  if (!(transactions === null || transactions === void 0 ? void 0 : transactions.length))
551
477
  return null;
552
- return transactions.map((transaction) => ({
553
- hash: transaction.hash,
554
- blockNumber: parseInt(transaction.block_number),
555
- transactionIndex: parseInt(transaction.transaction_index),
556
- from: transaction.from_address,
557
- to: transaction.to_address,
558
- value: transaction.value,
559
- gasPrice: transaction.gas_price,
560
- gas: transaction.gas,
561
- input: transaction.input,
562
- nonce: parseInt(transaction.nonce),
563
- data: transaction.input,
564
- type: transaction.transaction_type,
565
- maxFeePerGas: transaction.max_fee_per_gas,
566
- maxPriorityFeePerGas: transaction.max_priority_fee_per_gas,
567
- baseFeePerGas: transaction.base_fee_per_gas,
568
- effectiveGasPrice: transaction.effective_gas_price,
569
- gasUsed: transaction.gas_used,
570
- cumulativeGasUsed: transaction.cumulative_gas_used,
571
- status: transaction.status === 'success' ? 1 : 0,
572
- contractCreated: transaction.contract_created || undefined,
573
- timestamp: transaction.timestamp,
478
+ return transactions.map((t) => ({
479
+ hash: t.hash,
480
+ blockNumber: +t.block_number,
481
+ transactionIndex: +t.transaction_index,
482
+ from: t.from_address,
483
+ to: t.to_address,
484
+ value: t.value,
485
+ gasPrice: t.gas_price,
486
+ gas: t.gas,
487
+ input: t.input,
488
+ nonce: +t.nonce,
489
+ data: t.input,
490
+ type: t.transaction_type,
491
+ maxFeePerGas: t.max_fee_per_gas,
492
+ maxPriorityFeePerGas: t.max_priority_fee_per_gas,
493
+ baseFeePerGas: t.base_fee_per_gas,
494
+ effectiveGasPrice: t.effective_gas_price,
495
+ gasUsed: t.gas_used,
496
+ cumulativeGasUsed: t.cumulative_gas_used,
497
+ status: t.status === "success" ? 1 : 0,
498
+ contractCreated: t.contract_created || undefined,
499
+ timestamp: t.timestamp,
574
500
  }));
575
501
  }
576
502
  exports.getTransactions = getTransactions;
503
+ // ---------------------------------------------------------------------------
504
+ // Public helpers
505
+ // ---------------------------------------------------------------------------
506
+ function isIndexerEnabled(chain) {
507
+ // Legacy function kept for backward‑compat: return true if v2 is enabled
508
+ if (!indexerConfigs.v2.endpoint)
509
+ return false;
510
+ if (chain && !exports.supportedChainSet2.has(chain))
511
+ return false;
512
+ return true;
513
+ }
514
+ exports.isIndexerEnabled = isIndexerEnabled;
515
+ function isIndexer2Enabled(chain) {
516
+ // Alias – retained so external calls do not break
517
+ return isIndexerEnabled(chain);
518
+ }
519
+ exports.isIndexer2Enabled = isIndexer2Enabled;