@defillama/sdk 5.0.185 → 5.0.188
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/Balances.d.ts +1 -0
- package/build/Balances.js +2 -1
- package/build/ChainApi.d.ts +1 -0
- package/build/ChainApi.js +19 -5
- package/build/abi/multicall3.js +1 -0
- package/build/generalUtil.d.ts +1 -0
- package/build/generalUtil.js +5 -1
- package/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/providers.json +1 -1
- package/build/util/LlamaProvider.d.ts +10 -0
- package/build/util/LlamaProvider.js +23 -1
- package/build/util/coins.d.ts +1 -0
- package/build/util/common.js +2 -2
- package/build/util/env.js +2 -1
- package/build/util/indexer.d.ts +16 -0
- package/build/util/indexer.js +323 -78
- package/build/util/logs.decode.shared.d.ts +8 -0
- package/build/util/logs.decode.shared.js +288 -0
- package/build/util/logs.js +31 -19
- package/build/util/updateProviderList.js +61 -16
- package/package.json +5 -2
package/build/util/indexer.js
CHANGED
|
@@ -11,20 +11,27 @@ exports.getTransactions = getTransactions;
|
|
|
11
11
|
exports.isIndexerEnabled = isIndexerEnabled;
|
|
12
12
|
exports.isIndexer2Enabled = isIndexer2Enabled;
|
|
13
13
|
const axios_1 = __importDefault(require("axios"));
|
|
14
|
+
const http_1 = __importDefault(require("http"));
|
|
15
|
+
const https_1 = __importDefault(require("https"));
|
|
16
|
+
const stream_json_1 = require("stream-json");
|
|
17
|
+
const Pick_1 = require("stream-json/filters/Pick");
|
|
18
|
+
const StreamArray_1 = require("stream-json/streamers/StreamArray");
|
|
14
19
|
const _1 = require(".");
|
|
15
20
|
const general_1 = require("../general");
|
|
16
|
-
const common_1 = require("./common");
|
|
17
21
|
const blocks_1 = require("./blocks");
|
|
18
22
|
const cache_1 = require("./cache");
|
|
23
|
+
const common_1 = require("./common");
|
|
19
24
|
const debugLog_1 = require("./debugLog");
|
|
20
25
|
const env_1 = require("./env");
|
|
21
26
|
const logs_1 = require("./logs");
|
|
27
|
+
const logs_decode_shared_1 = require("./logs.decode.shared");
|
|
22
28
|
const indexerURL = (0, env_1.getEnvValue)("LLAMA_INDEXER_ENDPOINT");
|
|
23
29
|
const LLAMA_INDEXER_API_KEY = (0, env_1.getEnvValue)("LLAMA_INDEXER_API_KEY");
|
|
24
30
|
const LLAMA_INDEXER_V2_ENDPOINT = (0, env_1.getEnvValue)("LLAMA_INDEXER_V2_ENDPOINT");
|
|
25
31
|
const LLAMA_INDEXER_V2_API_KEY = (0, env_1.getEnvValue)("LLAMA_INDEXER_V2_API_KEY");
|
|
26
32
|
const addressChunkSize = +(0, env_1.getEnvValue)("LLAMA_INDEXER_ADDRESS_CHUNK_SIZE") || 100;
|
|
27
|
-
|
|
33
|
+
const INDEXER_REQUEST_TIMEOUT_MS = +(0, env_1.getEnvValue)("LLAMA_INDEXER_TIMEOUT_MS");
|
|
34
|
+
// v1 chains (still used for balances endpoint)
|
|
28
35
|
const indexerChainIdChainMapping = {
|
|
29
36
|
1: "ethereum",
|
|
30
37
|
10: "optimism",
|
|
@@ -52,6 +59,7 @@ const indexer2ChainIdChainMapping = {
|
|
|
52
59
|
1868: "soneium",
|
|
53
60
|
80094: "berachain",
|
|
54
61
|
999: "hyperliquid",
|
|
62
|
+
143: "monad",
|
|
55
63
|
};
|
|
56
64
|
const indexerConfigs = {
|
|
57
65
|
v1: {
|
|
@@ -65,14 +73,21 @@ const indexerConfigs = {
|
|
|
65
73
|
chainMapping: indexer2ChainIdChainMapping,
|
|
66
74
|
},
|
|
67
75
|
};
|
|
76
|
+
// http agents for streaming (keep-alive)
|
|
77
|
+
const httpAgent = new http_1.default.Agent({ keepAlive: true, maxSockets: 256, maxFreeSockets: 64 });
|
|
78
|
+
const httpsAgent = new https_1.default.Agent({ keepAlive: true, maxSockets: 256, maxFreeSockets: 64 });
|
|
68
79
|
const axiosInstances = {
|
|
69
80
|
v1: axios_1.default.create({
|
|
70
81
|
headers: { "x-api-key": indexerConfigs.v1.apiKey },
|
|
71
82
|
baseURL: indexerConfigs.v1.endpoint,
|
|
83
|
+
timeout: INDEXER_REQUEST_TIMEOUT_MS,
|
|
72
84
|
}),
|
|
73
85
|
v2: axios_1.default.create({
|
|
74
86
|
headers: { "x-api-key": indexerConfigs.v2.apiKey },
|
|
75
87
|
baseURL: indexerConfigs.v2.endpoint,
|
|
88
|
+
httpAgent,
|
|
89
|
+
httpsAgent,
|
|
90
|
+
timeout: INDEXER_REQUEST_TIMEOUT_MS,
|
|
76
91
|
}),
|
|
77
92
|
};
|
|
78
93
|
function checkIndexerConfig(version) {
|
|
@@ -96,14 +111,8 @@ const chainToIDMapping = {};
|
|
|
96
111
|
Object.entries(indexerConfigs.v1.chainMapping).forEach(([id, chain]) => {
|
|
97
112
|
chainToIDMapping[chain] = +id;
|
|
98
113
|
});
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
chainToIDMapping2[chain] = +id;
|
|
102
|
-
});
|
|
103
|
-
const state = {
|
|
104
|
-
chainIndexStatus: {},
|
|
105
|
-
};
|
|
106
|
-
const cacheTime = 1 * 60 * 1000; // 1 min
|
|
114
|
+
const state = { chainIndexStatus: {} };
|
|
115
|
+
const cacheTime = 1 * 60 * 1000; // 1 min
|
|
107
116
|
async function getChainIndexStatus(version = "v2") {
|
|
108
117
|
checkIndexerConfig(version);
|
|
109
118
|
if (state.timestamp && Date.now() - state.timestamp < cacheTime)
|
|
@@ -125,17 +134,11 @@ async function getChainIndexStatus(version = "v2") {
|
|
|
125
134
|
})();
|
|
126
135
|
return state.chainIndexStatus;
|
|
127
136
|
}
|
|
128
|
-
// ---------------------------------------------------------------------------
|
|
129
|
-
// Utilities / Enums / Types
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
137
|
var TokenTypes;
|
|
132
138
|
(function (TokenTypes) {
|
|
133
139
|
TokenTypes["ERC20"] = "erc20";
|
|
134
140
|
TokenTypes["ERC721"] = "erc721";
|
|
135
141
|
})(TokenTypes || (TokenTypes = {}));
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
// Token helpers (still via v1 endpoint)
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
142
|
async function getTokens(address, { onlyWhitelisted = true, skipCacheRead = false, skipCache = false, chain, tokenType, } = {}) {
|
|
140
143
|
var _a, _b;
|
|
141
144
|
checkIndexerConfig("v1");
|
|
@@ -190,38 +193,119 @@ async function getTokens(address, { onlyWhitelisted = true, skipCacheRead = fals
|
|
|
190
193
|
return tokens;
|
|
191
194
|
}
|
|
192
195
|
// ---------------------------------------------------------------------------
|
|
193
|
-
//
|
|
196
|
+
// Streaming support (disabled by default, opt-in via clientStreaming: true)
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
async function streamJsonArrayFromIndexer(opts) {
|
|
199
|
+
const { path, arrayKey = "logs", onItem, onChunkStats, shouldStop } = opts;
|
|
200
|
+
const controller = new AbortController();
|
|
201
|
+
let bytesReceived = 0;
|
|
202
|
+
let itemsProcessed = 0;
|
|
203
|
+
const res = await axiosInstances.v2.get(path, {
|
|
204
|
+
responseType: "stream",
|
|
205
|
+
timeout: 0,
|
|
206
|
+
maxContentLength: Infinity,
|
|
207
|
+
maxBodyLength: Infinity,
|
|
208
|
+
headers: { Accept: "application/json", "Accept-Encoding": "gzip" },
|
|
209
|
+
signal: controller.signal,
|
|
210
|
+
}).catch((e) => {
|
|
211
|
+
if (axios_1.default.isCancel(e) || (e === null || e === void 0 ? void 0 : e.code) === 'ERR_CANCELED')
|
|
212
|
+
return { data: null };
|
|
213
|
+
throw (0, common_1.formError)(e);
|
|
214
|
+
});
|
|
215
|
+
if (!(res === null || res === void 0 ? void 0 : res.data))
|
|
216
|
+
return;
|
|
217
|
+
await new Promise((resolve, reject) => {
|
|
218
|
+
const stream = res.data;
|
|
219
|
+
const isCancellationError = (err) => {
|
|
220
|
+
var _a;
|
|
221
|
+
return (err === null || err === void 0 ? void 0 : err.name) === "CanceledError" ||
|
|
222
|
+
((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes("aborted")) ||
|
|
223
|
+
axios_1.default.isCancel(err) ||
|
|
224
|
+
(err === null || err === void 0 ? void 0 : err.code) === 'ERR_CANCELED';
|
|
225
|
+
};
|
|
226
|
+
const handleError = (err) => isCancellationError(err) ? resolve() : reject(err);
|
|
227
|
+
stream.on('data', (chunk) => {
|
|
228
|
+
bytesReceived += chunk.length;
|
|
229
|
+
onChunkStats === null || onChunkStats === void 0 ? void 0 : onChunkStats({ chunkSize: chunk.length, bytesReceived, itemsProcessed });
|
|
230
|
+
});
|
|
231
|
+
const jsonParser = (0, stream_json_1.parser)();
|
|
232
|
+
const pickFilter = (0, Pick_1.pick)({ filter: arrayKey });
|
|
233
|
+
const arrayStream = (0, StreamArray_1.streamArray)();
|
|
234
|
+
arrayStream.on('data', (data) => {
|
|
235
|
+
if (shouldStop === null || shouldStop === void 0 ? void 0 : shouldStop()) {
|
|
236
|
+
controller.abort();
|
|
237
|
+
stream.destroy();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
onItem(data.value);
|
|
242
|
+
itemsProcessed++;
|
|
243
|
+
if (itemsProcessed % 100000 === 0) {
|
|
244
|
+
onChunkStats === null || onChunkStats === void 0 ? void 0 : onChunkStats({ chunkSize: 100000, bytesReceived, itemsProcessed });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch { }
|
|
248
|
+
});
|
|
249
|
+
const finish = () => {
|
|
250
|
+
const rem = itemsProcessed % 100000;
|
|
251
|
+
if (rem > 0) {
|
|
252
|
+
onChunkStats === null || onChunkStats === void 0 ? void 0 : onChunkStats({ chunkSize: rem, bytesReceived, itemsProcessed });
|
|
253
|
+
}
|
|
254
|
+
resolve();
|
|
255
|
+
};
|
|
256
|
+
arrayStream.on('end', finish);
|
|
257
|
+
arrayStream.on('close', finish);
|
|
258
|
+
arrayStream.on('error', handleError);
|
|
259
|
+
jsonParser.on('error', handleError);
|
|
260
|
+
pickFilter.on('error', handleError);
|
|
261
|
+
stream.on('error', handleError);
|
|
262
|
+
stream.pipe(jsonParser).pipe(pickFilter).pipe(arrayStream);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// LOGS (v2) – streaming opt-in, legacy default
|
|
194
267
|
// ---------------------------------------------------------------------------
|
|
195
268
|
async function getLogs(options) {
|
|
196
|
-
|
|
197
|
-
|
|
269
|
+
// Set collect=false by default when processor is provided AND collect is not explicitly set AND clientStreaming = true
|
|
270
|
+
// if you pass processor without collect=true, you won't get results back
|
|
271
|
+
var _a, _b, _c, _d, _e, _f;
|
|
272
|
+
let { all = true, limit = 1000, offset: initialOffset = 0, target, targets = [], flatten = true, debugMode = false, noTarget = false, maxBlockRange, processor, clientStreaming = false, // default OFF to keep retro-compat
|
|
273
|
+
decoderType = "viem", // default to viem for performance
|
|
274
|
+
onWireStats, onDecodeStats, } = options;
|
|
275
|
+
if (processor && typeof options.collect === "undefined" && clientStreaming) {
|
|
276
|
+
options.collect = false;
|
|
277
|
+
}
|
|
198
278
|
const { chain, fromBlock, toBlock, topic, topic1, topic2, topic3, transformLog, } = await (0, logs_1.getLogParams)(options, true);
|
|
279
|
+
const viemFastPath = decoderType === "viem" && options.eventAbi
|
|
280
|
+
? (0, logs_decode_shared_1.createViemFastPathBatchDecoder)(options.eventAbi)
|
|
281
|
+
: null;
|
|
199
282
|
if (!debugMode)
|
|
200
283
|
debugMode = debugLog_1.DEBUG_LEVEL2 && !!env_1.ENV_CONSTANTS.GET_LOGS_INDEXER;
|
|
284
|
+
// === Match logs.ts semantic regarding processor + multiple targets (flatten=false) ===
|
|
285
|
+
if (processor && (targets === null || targets === void 0 ? void 0 : targets.length) > 1 && !flatten)
|
|
286
|
+
throw new Error("processor is not supported with multiple targets when flatten=false");
|
|
201
287
|
const blockRange = toBlock - fromBlock;
|
|
202
288
|
const effectiveMaxBlockRange = maxBlockRange !== null && maxBlockRange !== void 0 ? maxBlockRange : (noTarget ? 10000 : Infinity);
|
|
203
289
|
if (noTarget && blockRange > 500000) {
|
|
204
290
|
throw new Error("When noTarget is true, block range must be less than 500k blocks. Please narrow down your block range.");
|
|
205
291
|
}
|
|
206
|
-
//
|
|
292
|
+
// Ensure the indexer is synced far enough
|
|
207
293
|
checkIndexerConfig("v2");
|
|
208
294
|
const chainId = getChainId(chain, "v2");
|
|
209
|
-
// Ensure the indexer is synced far enough
|
|
210
295
|
const chainIndexStatus = await getChainIndexStatus("v2");
|
|
211
296
|
const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
|
|
212
297
|
if (lastIndexedBlock < toBlock) {
|
|
213
298
|
const percentageMissing = ((toBlock - lastIndexedBlock) / (toBlock - fromBlock)) * 100;
|
|
214
|
-
if (percentageMissing > 50)
|
|
299
|
+
if (percentageMissing > 50)
|
|
215
300
|
throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}`);
|
|
216
301
|
if (env_1.ENV_CONSTANTS.GET_LOGS_INDEXER)
|
|
217
302
|
(0, debugLog_1.debugLog)(`Indexer only partially up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}, missing ${Number(percentageMissing).toFixed(2)}%. Pulling part of the logs through RPC calls.`);
|
|
218
|
-
|
|
219
|
-
const breakBlock = lastIndexedBlock - 50; // we add a small buffer to avoid missing logs due to reorgs
|
|
303
|
+
const breakBlock = lastIndexedBlock - 50; // small buffer
|
|
220
304
|
const indexerLogs = await getLogs({ ...options, fromBlock, toBlock: breakBlock });
|
|
221
305
|
const rpcLogs = await (0, logs_1.getLogs)({ ...options, fromBlock: breakBlock + 1, toBlock, skipIndexer: true });
|
|
222
306
|
return indexerLogs.concat(rpcLogs);
|
|
223
307
|
}
|
|
224
|
-
// Re
|
|
308
|
+
// Re-curse if the requested range is too large
|
|
225
309
|
if (blockRange > effectiveMaxBlockRange) {
|
|
226
310
|
const results = [];
|
|
227
311
|
for (let currentFromBlock = fromBlock; currentFromBlock < toBlock; currentFromBlock += effectiveMaxBlockRange) {
|
|
@@ -233,9 +317,6 @@ async function getLogs(options) {
|
|
|
233
317
|
return results.flat();
|
|
234
318
|
return targets.map((_, i) => results.map((r) => r[i]).flat());
|
|
235
319
|
}
|
|
236
|
-
// -------------------------------------------------------------------------
|
|
237
|
-
// Prepare address filters
|
|
238
|
-
// -------------------------------------------------------------------------
|
|
239
320
|
let address = target;
|
|
240
321
|
if (typeof target === "string")
|
|
241
322
|
targets = [target];
|
|
@@ -248,10 +329,177 @@ async function getLogs(options) {
|
|
|
248
329
|
const addressChunks = (0, _1.sliceIntoChunks)((_c = address === null || address === void 0 ? void 0 : address.split(",")) !== null && _c !== void 0 ? _c : [], addressChunkSize);
|
|
249
330
|
if (noTarget && addressChunks.length === 0)
|
|
250
331
|
addressChunks.push(undefined);
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
332
|
+
// Safely push large arrays without stack overflow (avoid push.apply argument limits (~125k))
|
|
333
|
+
const safePush = (target, source) => {
|
|
334
|
+
if (source.length === 0)
|
|
335
|
+
return;
|
|
336
|
+
// For very large arrays, split into chunks
|
|
337
|
+
if (source.length > 100000) {
|
|
338
|
+
const CHUNK_SIZE = 50000; // Safe chunk size well below push.apply limit
|
|
339
|
+
for (let i = 0; i < source.length; i += CHUNK_SIZE) {
|
|
340
|
+
const chunk = source.slice(i, i + CHUNK_SIZE);
|
|
341
|
+
Array.prototype.push.apply(target, chunk);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
Array.prototype.push.apply(target, source);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
// -----------------------------------------------------------------------
|
|
349
|
+
// Streaming (opt-in) — NO server-side parsing; mirror legacy semantics
|
|
350
|
+
// -----------------------------------------------------------------------
|
|
351
|
+
if (clientStreaming) {
|
|
352
|
+
const useAll = options.all === true;
|
|
353
|
+
const effectiveLimit = useAll ? Number.POSITIVE_INFINITY : ((_d = options.limit) !== null && _d !== void 0 ? _d : limit);
|
|
354
|
+
const shouldLimit = !useAll && Number.isFinite(effectiveLimit);
|
|
355
|
+
const effectiveOffset = useAll ? 0 : ((_f = (_e = options.offset) !== null && _e !== void 0 ? _e : initialOffset) !== null && _f !== void 0 ? _f : 0);
|
|
356
|
+
const outPairs = [];
|
|
357
|
+
const start = debugMode ? Date.now() : 0;
|
|
358
|
+
const MICRO_BATCH_SIZE = +(process.env.LLAMA_INDEXER_MICRO_BATCH || 10000);
|
|
359
|
+
// Check if we need splitByAddress (mapping by target address)
|
|
360
|
+
const splitByAddress = (targets === null || targets === void 0 ? void 0 : targets.length) && !flatten;
|
|
361
|
+
// If splitByAddress, build buckets directly during flushBatch
|
|
362
|
+
const addressBuckets = splitByAddress ? targets.map(() => []) : [];
|
|
363
|
+
const addressIndexMap = splitByAddress
|
|
364
|
+
? Object.fromEntries(targets.map((t, i) => [t.toLowerCase(), i]))
|
|
365
|
+
: {};
|
|
366
|
+
let remainingOffset = effectiveOffset;
|
|
367
|
+
let remainingLimit = shouldLimit ? effectiveLimit : Number.POSITIVE_INFINITY;
|
|
368
|
+
const flushBatch = async (batch) => {
|
|
369
|
+
var _a, _b, _c;
|
|
370
|
+
if (!batch.length)
|
|
371
|
+
return;
|
|
372
|
+
const t0 = Date.now();
|
|
373
|
+
// Normalize logs before decoding (required for fast-path and consistency)
|
|
374
|
+
for (const log of batch) {
|
|
375
|
+
(0, logs_decode_shared_1.normalizeLog)(log, true); // isIndexerCall = true
|
|
376
|
+
}
|
|
377
|
+
// Use fast-path if available, otherwise fallback to default
|
|
378
|
+
const batchFn = viemFastPath !== null && viemFastPath !== void 0 ? viemFastPath : transformLog.batch;
|
|
379
|
+
let transformedLogs;
|
|
380
|
+
if (batchFn) {
|
|
381
|
+
// Fast-path returns only args, need to reconstruct full log objects
|
|
382
|
+
const decodedArgs = await batchFn(batch);
|
|
383
|
+
transformedLogs = new Array(batch.length);
|
|
384
|
+
for (let i = 0; i < batch.length; i++) {
|
|
385
|
+
if (options.onlyArgs) {
|
|
386
|
+
// If onlyArgs is true, return only the args (like transformLog does)
|
|
387
|
+
transformedLogs[i] = decodedArgs[i];
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
transformedLogs[i] = {
|
|
391
|
+
...batch[i],
|
|
392
|
+
args: decodedArgs[i]
|
|
393
|
+
};
|
|
394
|
+
if (!transformedLogs[i].transactionHash && transformedLogs[i]._originalTransactionHash) {
|
|
395
|
+
transformedLogs[i].transactionHash = transformedLogs[i]._originalTransactionHash;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
transformedLogs = await Promise.all(batch.map((log) => transformLog(log)));
|
|
402
|
+
}
|
|
403
|
+
const decodeTime = Date.now() - t0;
|
|
404
|
+
onDecodeStats === null || onDecodeStats === void 0 ? void 0 : onDecodeStats({ batchSize: batch.length, decodeTime, itemsDecoded: batch.length });
|
|
405
|
+
if (processor)
|
|
406
|
+
await processor(transformedLogs);
|
|
407
|
+
if (splitByAddress) {
|
|
408
|
+
// Map directly to address buckets without storing pairs
|
|
409
|
+
for (let i = 0; i < batch.length; i++) {
|
|
410
|
+
const raw = batch[i];
|
|
411
|
+
const transformed = transformedLogs[i];
|
|
412
|
+
const idx = addressIndexMap[(_c = (_b = ((_a = raw.source) !== null && _a !== void 0 ? _a : raw.address)) === null || _b === void 0 ? void 0 : _b.toLowerCase) === null || _c === void 0 ? void 0 : _c.call(_b)];
|
|
413
|
+
if (idx !== undefined) {
|
|
414
|
+
addressBuckets[idx].push(transformed);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else if (options.collect !== false) {
|
|
419
|
+
// Build pairs if collect=true (needed for final return)
|
|
420
|
+
const pairs = new Array(batch.length);
|
|
421
|
+
for (let i = 0; i < batch.length; i++) {
|
|
422
|
+
pairs[i] = { raw: batch[i], transformed: transformedLogs[i] };
|
|
423
|
+
}
|
|
424
|
+
Array.prototype.push.apply(outPairs, pairs);
|
|
425
|
+
}
|
|
426
|
+
// If collect=false and not splitByAddress, nothing to store (processor already handled it)
|
|
427
|
+
};
|
|
428
|
+
for (const chunk of addressChunks) {
|
|
429
|
+
if (Array.isArray(chunk) && chunk.length === 0)
|
|
430
|
+
throw new Error("Address chunk cannot be empty");
|
|
431
|
+
const qs = new URLSearchParams();
|
|
432
|
+
qs.set("chainId", String(chainId));
|
|
433
|
+
qs.set("topic0", topic);
|
|
434
|
+
if (topic1)
|
|
435
|
+
qs.set("topic1", topic1);
|
|
436
|
+
if (topic2)
|
|
437
|
+
qs.set("topic2", topic2);
|
|
438
|
+
if (topic3)
|
|
439
|
+
qs.set("topic3", topic3);
|
|
440
|
+
if (fromBlock != null)
|
|
441
|
+
qs.set("from_block", String(fromBlock));
|
|
442
|
+
if (toBlock != null)
|
|
443
|
+
qs.set("to_block", String(toBlock));
|
|
444
|
+
if (chunk && Array.isArray(chunk))
|
|
445
|
+
qs.set("addresses", chunk.join(",").toLowerCase());
|
|
446
|
+
if (noTarget)
|
|
447
|
+
qs.set("noTarget", "true");
|
|
448
|
+
qs.set("limit", "all");
|
|
449
|
+
qs.set("offset", "0");
|
|
450
|
+
const transformBatch = [];
|
|
451
|
+
let stopNow = false;
|
|
452
|
+
let flushPromise = Promise.resolve();
|
|
453
|
+
// Chain flushes sequentially to avoid concurrent decoding (CPU/GC thrashing)
|
|
454
|
+
const scheduleFlush = (batch) => {
|
|
455
|
+
flushPromise = flushPromise.then(() => flushBatch(batch));
|
|
456
|
+
};
|
|
457
|
+
await streamJsonArrayFromIndexer({
|
|
458
|
+
path: `/logs?${qs.toString()}`,
|
|
459
|
+
arrayKey: "logs",
|
|
460
|
+
onItem: (raw) => {
|
|
461
|
+
var _a, _b, _c;
|
|
462
|
+
if (stopNow)
|
|
463
|
+
return;
|
|
464
|
+
const okAddress = !addressSet.size || addressSet.has((_c = (_b = ((_a = raw.source) !== null && _a !== void 0 ? _a : raw.address)) === null || _b === void 0 ? void 0 : _b.toLowerCase) === null || _c === void 0 ? void 0 : _c.call(_b));
|
|
465
|
+
if (!okAddress)
|
|
466
|
+
return;
|
|
467
|
+
if (remainingOffset > 0) {
|
|
468
|
+
remainingOffset--;
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (remainingLimit <= 0) {
|
|
472
|
+
stopNow = true;
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
transformBatch.push(raw);
|
|
476
|
+
remainingLimit--;
|
|
477
|
+
if (transformBatch.length >= MICRO_BATCH_SIZE) {
|
|
478
|
+
// Sequential flush: chain to avoid concurrent decoding
|
|
479
|
+
const batch = transformBatch.splice(0, MICRO_BATCH_SIZE);
|
|
480
|
+
scheduleFlush(batch);
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
shouldStop: () => stopNow,
|
|
484
|
+
onChunkStats: (s) => onWireStats === null || onWireStats === void 0 ? void 0 : onWireStats(s),
|
|
485
|
+
});
|
|
486
|
+
if (transformBatch.length > 0) {
|
|
487
|
+
scheduleFlush(transformBatch.splice(0, transformBatch.length));
|
|
488
|
+
}
|
|
489
|
+
await flushPromise;
|
|
490
|
+
if (remainingLimit <= 0)
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
if (debugMode) {
|
|
494
|
+
const ms = Date.now() - start;
|
|
495
|
+
(0, debugLog_1.debugLog)(`[Indexer] stream finished: ${outPairs.length} items in ${ms}ms`);
|
|
496
|
+
}
|
|
497
|
+
if (splitByAddress) {
|
|
498
|
+
return addressBuckets;
|
|
499
|
+
}
|
|
500
|
+
return (options.collect !== false) ? outPairs.map(i => i.transformed) : [];
|
|
501
|
+
}
|
|
502
|
+
const allLogsPairs = [];
|
|
255
503
|
const debugTimeKey = `Indexer-getLogs-${chain}-${topic}-${address}_${Math.random()}`;
|
|
256
504
|
if (debugMode) {
|
|
257
505
|
(0, debugLog_1.debugLog)("[Indexer] Pulling logs " + debugTimeKey);
|
|
@@ -276,65 +524,77 @@ async function getLogs(options) {
|
|
|
276
524
|
limit,
|
|
277
525
|
offset: chunkOffset,
|
|
278
526
|
noTarget,
|
|
279
|
-
eventAbi: options.eventAbi,
|
|
280
|
-
parseLog: options.parseLog,
|
|
281
|
-
onlyArgs: options.onlyArgs,
|
|
282
527
|
};
|
|
283
528
|
const { data: { logs: _logs, totalCount }, } = await axiosInstances.v2(`/logs`, { params }).catch(e => { throw (0, common_1.formError)(e); });
|
|
284
529
|
const filtered = _logs.filter((l) => {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (l.block_number) {
|
|
289
|
-
l.blockNumber = parseInt(l.block_number);
|
|
290
|
-
}
|
|
291
|
-
return true;
|
|
530
|
+
var _a, _b, _c;
|
|
531
|
+
const isWhitelisted = !addressSet.size || addressSet.has((_c = (_b = ((_a = l.source) !== null && _a !== void 0 ? _a : l.address)) === null || _b === void 0 ? void 0 : _b.toLowerCase) === null || _c === void 0 ? void 0 : _c.call(_b));
|
|
532
|
+
return !!isWhitelisted;
|
|
292
533
|
});
|
|
293
|
-
|
|
294
|
-
|
|
534
|
+
const t0 = Date.now();
|
|
535
|
+
// Normalize logs before decoding (required for consistency)
|
|
536
|
+
for (const log of filtered) {
|
|
537
|
+
(0, logs_decode_shared_1.normalizeLog)(log, true); // isIndexerCall = true
|
|
538
|
+
}
|
|
539
|
+
// Legacy path: use transformLog batch if available, otherwise transform individually
|
|
540
|
+
const transformBatchFn = transformLog.batch || ((logs) => Promise.all(logs.map((log) => transformLog(log))));
|
|
541
|
+
const transformedLogs = await transformBatchFn(filtered);
|
|
542
|
+
const transformedPair = filtered.map((log, idx) => ({ raw: log, transformed: transformedLogs[idx] }));
|
|
543
|
+
const decodeTime = Date.now() - t0;
|
|
544
|
+
if (onDecodeStats && filtered.length > 0)
|
|
545
|
+
onDecodeStats({ batchSize: filtered.length, decodeTime, itemsDecoded: filtered.length });
|
|
295
546
|
if (processor)
|
|
296
|
-
await processor(
|
|
297
|
-
|
|
547
|
+
await processor(transformedPair.map((i) => i.transformed));
|
|
548
|
+
// Safe push helper to avoid stack overflow on large arrays
|
|
549
|
+
// Only accumulate if collect is enabled (default true for backward compatibility)
|
|
550
|
+
const shouldCollect = (options.collect !== false);
|
|
551
|
+
if (shouldCollect) {
|
|
552
|
+
safePush(allLogsPairs, transformedPair);
|
|
553
|
+
}
|
|
298
554
|
logCount += _logs.length;
|
|
299
555
|
chunkOffset += limit;
|
|
300
|
-
if (_logs.length
|
|
556
|
+
if (_logs.length === 0) {
|
|
557
|
+
hasMore = false;
|
|
558
|
+
}
|
|
559
|
+
else if (_logs.length < limit) {
|
|
560
|
+
hasMore = false;
|
|
561
|
+
}
|
|
562
|
+
else if (typeof totalCount === 'number' && totalCount <= logCount) {
|
|
301
563
|
hasMore = false;
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
hasMore = true;
|
|
567
|
+
}
|
|
302
568
|
} while (all && hasMore);
|
|
303
569
|
}
|
|
304
570
|
if (debugMode) {
|
|
305
571
|
console.timeEnd(debugTimeKey);
|
|
306
|
-
(0, debugLog_1.debugLog)("Logs pulled " + chain, address,
|
|
572
|
+
(0, debugLog_1.debugLog)("Logs pulled " + chain, address, allLogsPairs.length);
|
|
307
573
|
}
|
|
308
|
-
// -------------------------------------------------------------------------
|
|
309
|
-
// Map / transform result
|
|
310
|
-
// -------------------------------------------------------------------------
|
|
311
574
|
const splitByAddress = (targets === null || targets === void 0 ? void 0 : targets.length) && !flatten;
|
|
312
575
|
if (splitByAddress) {
|
|
313
576
|
const mapped = targets.map(() => []);
|
|
314
577
|
const indexMap = {};
|
|
315
578
|
targets.forEach((t, i) => (indexMap[t.toLowerCase()] = i));
|
|
316
|
-
|
|
317
|
-
|
|
579
|
+
allLogsPairs.forEach(({ raw, transformed }) => {
|
|
580
|
+
var _a, _b, _c;
|
|
581
|
+
const idx = indexMap[(_c = (_b = ((_a = raw.source) !== null && _a !== void 0 ? _a : raw.address)) === null || _b === void 0 ? void 0 : _b.toLowerCase) === null || _c === void 0 ? void 0 : _c.call(_b)];
|
|
318
582
|
if (idx === undefined)
|
|
319
583
|
return; // ignore unknown sources
|
|
320
584
|
mapped[idx].push(transformed);
|
|
321
585
|
});
|
|
322
586
|
return mapped;
|
|
323
587
|
}
|
|
324
|
-
|
|
588
|
+
// Return empty array if collect=false (processor handles results)
|
|
589
|
+
const shouldCollect = (options.collect !== false);
|
|
590
|
+
return shouldCollect ? allLogsPairs.map(i => i.transformed) : [];
|
|
325
591
|
}
|
|
326
|
-
// ---------------------------------------------------------------------------
|
|
327
|
-
// TOKEN TRANSFERS (v2 only)
|
|
328
|
-
// ---------------------------------------------------------------------------
|
|
329
592
|
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, }) {
|
|
330
593
|
var _a, _b;
|
|
331
594
|
if (!debugMode)
|
|
332
595
|
debugMode = debugLog_1.DEBUG_LEVEL2 && !!env_1.ENV_CONSTANTS.GET_LOGS_INDEXER;
|
|
333
596
|
checkIndexerConfig("v2");
|
|
334
597
|
const chainId = getChainId(chain, "v2");
|
|
335
|
-
// -----------------------------------------------------------------------
|
|
336
|
-
// Input validation & pre‑processing
|
|
337
|
-
// -----------------------------------------------------------------------
|
|
338
598
|
const fromFilterEnabled = !!(fromAddressFilter === null || fromAddressFilter === void 0 ? void 0 : fromAddressFilter.length);
|
|
339
599
|
if (typeof fromAddressFilter === "string")
|
|
340
600
|
fromAddressFilter = [fromAddressFilter];
|
|
@@ -364,15 +624,12 @@ async function getTokenTransfers({ chain = "ethereum", fromAddressFilter, fromBl
|
|
|
364
624
|
throw new Error("target|targets is required");
|
|
365
625
|
targets = targets.map((t) => t.toLowerCase());
|
|
366
626
|
const addresses = targets.join(",");
|
|
367
|
-
// Ensure v2 indexer is up
|
|
627
|
+
// Ensure v2 indexer is up-to-date
|
|
368
628
|
const chainIndexStatus = await getChainIndexStatus("v2");
|
|
369
629
|
const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
|
|
370
630
|
if (lastIndexedBlock < toBlock) {
|
|
371
631
|
throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}`);
|
|
372
632
|
}
|
|
373
|
-
// -----------------------------------------------------------------------
|
|
374
|
-
// Fetch transfers
|
|
375
|
-
// -----------------------------------------------------------------------
|
|
376
633
|
const rawTransfers = [];
|
|
377
634
|
const debugTimeKey = `Indexer-tokenTransfers-${chain}-${addresses}_${Math.random()}`;
|
|
378
635
|
if (debugMode) {
|
|
@@ -407,11 +664,10 @@ async function getTokenTransfers({ chain = "ethereum", fromAddressFilter, fromBl
|
|
|
407
664
|
default:
|
|
408
665
|
throw new Error("Invalid transferType");
|
|
409
666
|
}
|
|
410
|
-
const { data: { transfers: _logs
|
|
667
|
+
const { data: { transfers: _logs }, } = await axiosInstances.v2(`/token-transfers`, { params }).catch(e => { throw (0, common_1.formError)(e); });
|
|
411
668
|
rawTransfers.push(..._logs);
|
|
412
669
|
currentOffset += limit;
|
|
413
|
-
|
|
414
|
-
hasMore = false;
|
|
670
|
+
hasMore = _logs.length === limit;
|
|
415
671
|
} while (all && hasMore);
|
|
416
672
|
const filteredTransfers = rawTransfers.filter((l) => {
|
|
417
673
|
if (!fromFilterEnabled)
|
|
@@ -422,9 +678,6 @@ async function getTokenTransfers({ chain = "ethereum", fromAddressFilter, fromBl
|
|
|
422
678
|
console.timeEnd(debugTimeKey);
|
|
423
679
|
(0, debugLog_1.debugLog)("Token Transfers pulled " + chain, addresses, filteredTransfers.length);
|
|
424
680
|
}
|
|
425
|
-
// -----------------------------------------------------------------------
|
|
426
|
-
// Split result if requested
|
|
427
|
-
// -----------------------------------------------------------------------
|
|
428
681
|
const splitByAddress = (targets === null || targets === void 0 ? void 0 : targets.length) && !flatten;
|
|
429
682
|
if (splitByAddress) {
|
|
430
683
|
const mapped = targets.map(() => []);
|
|
@@ -440,9 +693,6 @@ async function getTokenTransfers({ chain = "ethereum", fromAddressFilter, fromBl
|
|
|
440
693
|
}
|
|
441
694
|
return filteredTransfers;
|
|
442
695
|
}
|
|
443
|
-
// ---------------------------------------------------------------------------
|
|
444
|
-
// TRANSACTIONS (v2 only)
|
|
445
|
-
// ---------------------------------------------------------------------------
|
|
446
696
|
async function getTransactions({ chain = "ethereum", addresses, transaction_hashes, from_block, to_block, all = true, limit = 1000, offset = 0, debugMode = false, transactionType = "from", }) {
|
|
447
697
|
var _a, _b;
|
|
448
698
|
if (!debugMode)
|
|
@@ -515,11 +765,7 @@ async function getTransactions({ chain = "ethereum", addresses, transaction_hash
|
|
|
515
765
|
timestamp: t.timestamp,
|
|
516
766
|
}));
|
|
517
767
|
}
|
|
518
|
-
// ---------------------------------------------------------------------------
|
|
519
|
-
// Public helpers
|
|
520
|
-
// ---------------------------------------------------------------------------
|
|
521
768
|
function isIndexerEnabled(chain) {
|
|
522
|
-
// Legacy function kept for backward‑compat: return true if v2 is enabled
|
|
523
769
|
if (!indexerConfigs.v2.endpoint)
|
|
524
770
|
return false;
|
|
525
771
|
if (chain && !exports.supportedChainSet2.has(chain))
|
|
@@ -527,6 +773,5 @@ function isIndexerEnabled(chain) {
|
|
|
527
773
|
return true;
|
|
528
774
|
}
|
|
529
775
|
function isIndexer2Enabled(chain) {
|
|
530
|
-
// Alias – retained so external calls do not break
|
|
531
776
|
return isIndexerEnabled(chain);
|
|
532
777
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ethers, Interface } from "ethers";
|
|
2
|
+
export declare function padTopic32(t: string): `0x${string}`;
|
|
3
|
+
export declare function normalizeLog(log: any, isIndexerCall?: boolean): boolean;
|
|
4
|
+
export declare function normalizeValueByType(value: any, type: string): any;
|
|
5
|
+
export declare function convertTopicsToViemFormat(topics: any[]): [`0x${string}`, ...(`0x${string}`[] | [])] | [];
|
|
6
|
+
export declare function enrichEthersArgsWithNamedProperties(ethersArgs: any, event: ethers.EventFragment | null, iface: Interface): any;
|
|
7
|
+
export declare function buildEthersCompatibleArgs(decodedArgs: any, eventInputs: readonly ethers.ParamType[], normalizeFn?: (value: any, type: string) => any): any;
|
|
8
|
+
export declare function createViemFastPathBatchDecoder(eventAbi: string | any): ((logs: any[]) => Promise<any[]>) | null;
|