@defillama/sdk 5.0.146 → 5.0.148

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,188 @@ 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 allLogs = [];
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
+ // we need to the { raw, transformed } structure to allow for post‑processing like clustering based on `source` field
279
+ const transformed = filtered.map((log) => ({ raw: log, transformed: transformLog(log) }));
280
+ if (processor)
281
+ await processor(transformed.map((i) => i.transformed));
282
+ allLogs.push(...transformed);
304
283
  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
284
+ chunkOffset += limit;
307
285
  if (_logs.length < limit || totalCount <= logCount || _logs.length === 0)
308
286
  hasMore = false;
309
287
  } while (all && hasMore);
310
288
  }
311
289
  if (debugMode) {
312
290
  console.timeEnd(debugTimeKey);
313
- (0, debugLog_1.debugLog)('Logs pulled ' + chain, address, logs.length);
291
+ (0, debugLog_1.debugLog)("Logs pulled " + chain, address, allLogs.length);
314
292
  }
315
- const mappedLogs = [];
316
- let addressIndexMap = {};
293
+ // -------------------------------------------------------------------------
294
+ // Map / transform result
295
+ // -------------------------------------------------------------------------
317
296
  const splitByAddress = (targets === null || targets === void 0 ? void 0 : targets.length) && !flatten;
318
297
  if (splitByAddress) {
319
- for (let i = 0; i < targets.length; i++) {
320
- addressIndexMap[targets[i].toLowerCase()] = i;
321
- mappedLogs.push([]);
322
- }
298
+ const mapped = targets.map(() => []);
299
+ const indexMap = {};
300
+ targets.forEach((t, i) => (indexMap[t.toLowerCase()] = i));
301
+ allLogs.forEach(({ raw, transformed }) => {
302
+ const idx = indexMap[raw.source.toLowerCase()];
303
+ if (idx === undefined)
304
+ return; // ignore unknown sources
305
+ mapped[idx].push(transformed);
306
+ });
307
+ return mapped;
323
308
  }
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;
309
+ return allLogs.map(i => i.transformed);
347
310
  }
348
311
  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, }) {
312
+ // ---------------------------------------------------------------------------
313
+ // TOKEN TRANSFERS (v2 only)
314
+ // ---------------------------------------------------------------------------
315
+ 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
316
  var _a, _b;
367
317
  if (!debugMode)
368
318
  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')
319
+ checkIndexerConfig("v2");
320
+ const chainId = getChainId(chain, "v2");
321
+ // -----------------------------------------------------------------------
322
+ // Input validation & pre‑processing
323
+ // -----------------------------------------------------------------------
324
+ const fromFilterEnabled = !!(fromAddressFilter === null || fromAddressFilter === void 0 ? void 0 : fromAddressFilter.length);
325
+ if (typeof fromAddressFilter === "string")
374
326
  fromAddressFilter = [fromAddressFilter];
375
327
  const fromFilterSet = new Set((fromAddressFilter !== null && fromAddressFilter !== void 0 ? fromAddressFilter : []).map((a) => a.toLowerCase()));
376
328
  if (!fromBlock && !fromTimestamp)
377
- throw new Error('fromBlock or fromTimestamp is required');
329
+ throw new Error("fromBlock or fromTimestamp is required");
378
330
  if (!toBlock && !toTimestamp)
379
- throw new Error('toBlock or toTimestamp is required');
331
+ throw new Error("toBlock or toTimestamp is required");
380
332
  if (!fromBlock)
381
333
  fromBlock = await (0, blocks_1.getBlockNumber)(chain, fromTimestamp);
382
334
  if (!toBlock)
383
335
  toBlock = await (0, blocks_1.getBlockNumber)(chain, toTimestamp);
384
336
  if (!fromBlock || !toBlock)
385
- throw new Error('fromBlock and toBlock must be > 0');
337
+ throw new Error("fromBlock and toBlock must be > 0");
386
338
  if (token)
387
339
  tokens = [token];
388
340
  if (tokens) {
389
- if (typeof tokens === 'string')
341
+ if (typeof tokens === "string")
390
342
  tokens = [tokens];
391
- tokens = tokens.join(',').toLowerCase();
343
+ tokens = tokens.join(",").toLowerCase();
392
344
  }
393
345
  if (target)
394
346
  targets = [target];
395
347
  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}`);
348
+ throw new Error("target|targets is required");
349
+ targets = targets.map((t) => t.toLowerCase());
350
+ const addresses = targets.join(",");
351
+ // Ensure v2 indexer is up‑to‑date
352
+ const chainIndexStatus = await getChainIndexStatus("v2");
353
+ const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
354
+ if (lastIndexedBlock < toBlock) {
355
+ throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}`);
404
356
  }
405
- let hasMore = true;
406
- let logs = [];
357
+ // -----------------------------------------------------------------------
358
+ // Fetch transfers
359
+ // -----------------------------------------------------------------------
360
+ const rawTransfers = [];
407
361
  const debugTimeKey = `Indexer-tokenTransfers-${chain}-${addresses}_${Math.random()}`;
408
362
  if (debugMode) {
409
- (0, debugLog_1.debugLog)('[Indexer] Pulling token transfers ' + debugTimeKey);
363
+ (0, debugLog_1.debugLog)("[Indexer] Pulling token transfers " + debugTimeKey);
410
364
  console.time(debugTimeKey);
411
365
  }
366
+ let currentOffset = offset;
367
+ let hasMore = true;
412
368
  do {
413
369
  const params = {
414
370
  addresses,
@@ -416,161 +372,149 @@ async function getTokenTransfers({ chain = 'ethereum', fromAddressFilter, fromBl
416
372
  from_block: fromBlock,
417
373
  to_block: toBlock,
418
374
  limit,
419
- offset,
375
+ offset: currentOffset,
420
376
  tokens,
421
377
  from_address: false,
422
378
  to_address: false,
423
379
  };
424
380
  switch (transferType) {
425
- case 'in':
381
+ case "in":
426
382
  params.to_address = true;
427
383
  break;
428
- case 'out':
384
+ case "out":
429
385
  params.from_address = true;
430
386
  break;
431
- case 'all':
387
+ case "all":
432
388
  params.from_address = true;
433
389
  params.to_address = true;
434
390
  break;
435
391
  default:
436
- throw new Error('Invalid transferType');
392
+ throw new Error("Invalid transferType");
437
393
  }
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)
394
+ const { data: { transfers: _logs, totalCount }, } = await axiosInstances.v2(`/token-transfers`, { params });
395
+ rawTransfers.push(..._logs);
396
+ currentOffset += limit;
397
+ if (_logs.length < limit || totalCount <= rawTransfers.length || _logs.length === 0)
443
398
  hasMore = false;
444
399
  } while (all && hasMore);
445
- logs = logs.filter((l) => {
400
+ const filteredTransfers = rawTransfers.filter((l) => {
446
401
  if (!fromFilterEnabled)
447
402
  return true;
448
- return fromFilterSet.has(l === null || l === void 0 ? void 0 : l.from_address.toLowerCase());
403
+ return fromFilterSet.has(l.from_address.toLowerCase());
449
404
  });
450
405
  if (debugMode) {
451
406
  console.timeEnd(debugTimeKey);
452
- (0, debugLog_1.debugLog)('Token Transfers pulled ' + chain, addresses, logs.length);
407
+ (0, debugLog_1.debugLog)("Token Transfers pulled " + chain, addresses, filteredTransfers.length);
453
408
  }
454
- const mappedLogs = [];
455
- let addressIndexMap = {};
409
+ // -----------------------------------------------------------------------
410
+ // Split result if requested
411
+ // -----------------------------------------------------------------------
456
412
  const splitByAddress = (targets === null || targets === void 0 ? void 0 : targets.length) && !flatten;
457
413
  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);
414
+ const mapped = targets.map(() => []);
415
+ const indexMap = {};
416
+ targets.forEach((t, i) => (indexMap[t.toLowerCase()] = i));
417
+ filteredTransfers.forEach((log) => {
418
+ const sourceField = transferType === "in" ? "to_address" : "from_address";
419
+ const idx = indexMap[log[sourceField].toLowerCase()];
420
+ if (idx !== undefined)
421
+ mapped[idx].push(log);
469
422
  });
470
- return mappedLogs;
423
+ return mapped;
471
424
  }
472
- return logs;
425
+ return filteredTransfers;
473
426
  }
474
427
  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' }) {
428
+ // ---------------------------------------------------------------------------
429
+ // TRANSACTIONS (v2 only)
430
+ // ---------------------------------------------------------------------------
431
+ async function getTransactions({ chain = "ethereum", addresses, transaction_hashes, from_block, to_block, all = true, limit = 1000, offset = 0, debugMode = false, transactionType = "from", }) {
476
432
  var _a, _b;
477
433
  if (!debugMode)
478
434
  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)) {
435
+ checkIndexerConfig("v2");
436
+ const chainId = getChainId(chain, "v2");
437
+ if ((!addresses || addresses.length === 0) && (!transaction_hashes || transaction_hashes.length === 0))
482
438
  throw new Error("You must provide at least 'addresses' or 'transaction_hashes'");
483
- }
484
- if (!from_block || !to_block) {
439
+ if (!from_block || !to_block)
485
440
  throw new Error("'from_block' and 'to_block' are required to search for transactions");
486
- }
487
- const chainIndexStatus = await getChainIndexStatus('v2');
441
+ const chainIndexStatus = await getChainIndexStatus("v2");
488
442
  const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
489
443
  if (to_block > lastIndexedBlock) {
490
444
  throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${to_block}`);
491
445
  }
492
- const apiParams = { chainId };
446
+ const params = { chainId };
493
447
  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
- }
448
+ params.addresses = Array.isArray(addresses)
449
+ ? addresses.map((a) => a.toLowerCase()).join(",")
450
+ : addresses.toLowerCase();
500
451
  }
501
452
  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
- }
453
+ params.transaction_hashes = Array.isArray(transaction_hashes)
454
+ ? transaction_hashes.map((h) => h.toLowerCase()).join(",")
455
+ : transaction_hashes.toLowerCase();
508
456
  }
509
- apiParams.from_block = from_block;
510
- apiParams.to_block = to_block;
457
+ params.from_block = from_block;
458
+ params.to_block = to_block;
511
459
  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
- }
460
+ params.offset = offset;
461
+ params.from_address = transactionType !== "to";
462
+ params.to_address = transactionType !== "from";
463
+ if (all)
464
+ params.limit = "all";
465
+ else if (limit !== "all" && limit !== 0)
466
+ params.limit = limit;
538
467
  const debugTimeKey = `Indexer-getTransactions-${chain}-${addresses || transaction_hashes}-${from_block}-${to_block}_${Math.random()}`;
539
468
  if (debugMode) {
540
- (0, debugLog_1.debugLog)('[Indexer] Pulling transactions ' + debugTimeKey);
469
+ (0, debugLog_1.debugLog)("[Indexer] Pulling transactions " + debugTimeKey);
541
470
  console.time(debugTimeKey);
542
471
  }
543
- const { data: { transactions } } = await axiosInstances.v2(`/transactions`, {
544
- params: apiParams,
545
- });
472
+ const { data: { transactions } } = await axiosInstances.v2(`/transactions`, { params });
546
473
  if (debugMode) {
547
474
  console.timeEnd(debugTimeKey);
548
- (0, debugLog_1.debugLog)('Transactions pulled ' + chain, addresses || transaction_hashes, (transactions === null || transactions === void 0 ? void 0 : transactions.length) || 0);
475
+ (0, debugLog_1.debugLog)("Transactions pulled " + chain, addresses || transaction_hashes, (transactions === null || transactions === void 0 ? void 0 : transactions.length) || 0);
549
476
  }
550
477
  if (!(transactions === null || transactions === void 0 ? void 0 : transactions.length))
551
478
  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,
479
+ return transactions.map((t) => ({
480
+ hash: t.hash,
481
+ blockNumber: +t.block_number,
482
+ transactionIndex: +t.transaction_index,
483
+ from: t.from_address,
484
+ to: t.to_address,
485
+ value: t.value,
486
+ gasPrice: t.gas_price,
487
+ gas: t.gas,
488
+ input: t.input,
489
+ nonce: +t.nonce,
490
+ data: t.input,
491
+ type: t.transaction_type,
492
+ maxFeePerGas: t.max_fee_per_gas,
493
+ maxPriorityFeePerGas: t.max_priority_fee_per_gas,
494
+ baseFeePerGas: t.base_fee_per_gas,
495
+ effectiveGasPrice: t.effective_gas_price,
496
+ gasUsed: t.gas_used,
497
+ cumulativeGasUsed: t.cumulative_gas_used,
498
+ status: t.status === "success" ? 1 : 0,
499
+ contractCreated: t.contract_created || undefined,
500
+ timestamp: t.timestamp,
574
501
  }));
575
502
  }
576
503
  exports.getTransactions = getTransactions;
504
+ // ---------------------------------------------------------------------------
505
+ // Public helpers
506
+ // ---------------------------------------------------------------------------
507
+ function isIndexerEnabled(chain) {
508
+ // Legacy function kept for backward‑compat: return true if v2 is enabled
509
+ if (!indexerConfigs.v2.endpoint)
510
+ return false;
511
+ if (chain && !exports.supportedChainSet2.has(chain))
512
+ return false;
513
+ return true;
514
+ }
515
+ exports.isIndexerEnabled = isIndexerEnabled;
516
+ function isIndexer2Enabled(chain) {
517
+ // Alias – retained so external calls do not break
518
+ return isIndexerEnabled(chain);
519
+ }
520
+ exports.isIndexer2Enabled = isIndexer2Enabled;