@defillama/sdk 5.0.235 → 5.0.238
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/ChainApi.d.ts +23 -1
- package/build/ChainApi.js +13 -0
- package/build/abi/tron.js +7 -1
- package/build/chains/algorand.d.ts +277 -0
- package/build/chains/algorand.js +618 -0
- package/build/chains/aptos.d.ts +230 -0
- package/build/chains/aptos.js +612 -0
- package/build/chains/cardano.d.ts +242 -0
- package/build/chains/cardano.js +407 -0
- package/build/chains/cosmos.d.ts +252 -0
- package/build/chains/cosmos.js +618 -0
- package/build/chains/icp.d.ts +235 -0
- package/build/chains/icp.js +1040 -0
- package/build/chains/index.d.ts +23 -0
- package/build/chains/index.js +59 -0
- package/build/chains/near.d.ts +192 -0
- package/build/chains/near.js +446 -0
- package/build/chains/rpc.d.ts +125 -0
- package/build/chains/rpc.js +326 -0
- package/build/chains/starknet.d.ts +205 -0
- package/build/chains/starknet.js +884 -0
- package/build/chains/stellar.d.ts +410 -0
- package/build/chains/stellar.js +1082 -0
- package/build/chains/substrate.d.ts +333 -0
- package/build/chains/substrate.js +1042 -0
- package/build/chains/sui.d.ts +365 -0
- package/build/chains/sui.js +1135 -0
- package/build/chains/svm.d.ts +340 -0
- package/build/chains/svm.js +760 -0
- package/build/chains/tezos.d.ts +225 -0
- package/build/chains/tezos.js +412 -0
- package/build/chains/ton.d.ts +353 -0
- package/build/chains/ton.js +788 -0
- package/build/chains/tron.d.ts +45 -0
- package/build/chains/tron.js +110 -0
- package/build/chains/utxo.d.ts +202 -0
- package/build/chains/utxo.js +592 -0
- package/build/chains/xrpl.d.ts +179 -0
- package/build/chains/xrpl.js +467 -0
- package/build/index.d.ts +2 -1
- package/build/index.js +3 -1
- package/build/providers.json +1 -1
- package/build/util/LlamaProvider.js +2 -0
- package/build/util/blocks.js +292 -84
- package/build/util/chainUtils/data.json +80 -0
- package/build/util/chainUtils/index.d.ts +1 -0
- package/build/util/chainUtils/index.js +23 -0
- package/build/util/coins.js +14 -3
- package/build/util/common.js +6 -4
- package/build/util/computeTVL.js +1 -1
- package/build/util/cosmos.d.ts +1 -11
- package/build/util/cosmos.js +6 -66
- package/build/util/env.d.ts +1 -0
- package/build/util/env.js +18 -0
- package/build/util/indexer.compatibility.d.ts +6 -0
- package/build/util/indexer.compatibility.js +64 -0
- package/build/util/indexer.d.ts +24 -2
- package/build/util/indexer.js +333 -294
- package/build/util/indexerSql.d.ts +8 -1
- package/build/util/indexerSql.js +53 -22
- package/build/util/logs.d.ts +3 -0
- package/build/util/logs.decode.shared.js +5 -4
- package/build/util/logs.js +57 -26
- package/build/util/transactions.d.ts +23 -1
- package/build/util/updateChainLabels.d.ts +1 -0
- package/build/util/updateChainLabels.js +5 -0
- package/build/util/updateProviderList.js +40 -18
- package/package.json +20 -7
- package/README.md +0 -41
package/build/util/indexer.js
CHANGED
|
@@ -14,6 +14,7 @@ exports.isIndexer2Enabled = isIndexer2Enabled;
|
|
|
14
14
|
const axios_1 = __importDefault(require("axios"));
|
|
15
15
|
const http_1 = __importDefault(require("http"));
|
|
16
16
|
const https_1 = __importDefault(require("https"));
|
|
17
|
+
const promises_1 = require("stream/promises");
|
|
17
18
|
const stream_json_1 = require("stream-json");
|
|
18
19
|
const Pick_1 = require("stream-json/filters/Pick");
|
|
19
20
|
const StreamArray_1 = require("stream-json/streamers/StreamArray");
|
|
@@ -24,11 +25,12 @@ const debugLog_1 = require("./debugLog");
|
|
|
24
25
|
const env_1 = require("./env");
|
|
25
26
|
const logs_1 = require("./logs");
|
|
26
27
|
const logs_decode_shared_1 = require("./logs.decode.shared");
|
|
28
|
+
const indexer_compatibility_1 = require("./indexer.compatibility");
|
|
27
29
|
const LLAMA_INDEXER_V2_ENDPOINT = (0, env_1.getEnvValue)("LLAMA_INDEXER_V2_ENDPOINT");
|
|
28
30
|
const LLAMA_INDEXER_V2_API_KEY = (0, env_1.getEnvValue)("LLAMA_INDEXER_V2_API_KEY");
|
|
29
31
|
const LLAMA_INDEXER_V4_ENDPOINT = (0, env_1.getEnvValue)("LLAMA_INDEXER_V4_ENDPOINT");
|
|
30
32
|
const LLAMA_INDEXER_V4_API_KEY = (_a = (0, env_1.getEnvValue)("LLAMA_INDEXER_V4_API_KEY")) !== null && _a !== void 0 ? _a : LLAMA_INDEXER_V2_API_KEY;
|
|
31
|
-
const LLAMA_INDEXER_PREFER_V4 = (0, env_1.getEnvValue)("LLAMA_INDEXER_PREFER_V4")
|
|
33
|
+
const LLAMA_INDEXER_PREFER_V4 = (0, env_1.getEnvValue)("LLAMA_INDEXER_PREFER_V4") !== "false"; // explicit false allows rollback
|
|
32
34
|
const addressChunkSize = +(0, env_1.getEnvValue)("LLAMA_INDEXER_ADDRESS_CHUNK_SIZE") || 100;
|
|
33
35
|
const INDEXER_REQUEST_TIMEOUT_MS = +(0, env_1.getEnvValue)("LLAMA_INDEXER_TIMEOUT_MS");
|
|
34
36
|
const indexerChainIdChainMapping = {
|
|
@@ -76,31 +78,29 @@ const indexerConfigs = {
|
|
|
76
78
|
v2: { endpoint: LLAMA_INDEXER_V2_ENDPOINT, apiKey: LLAMA_INDEXER_V2_API_KEY },
|
|
77
79
|
v4: { endpoint: LLAMA_INDEXER_V4_ENDPOINT, apiKey: LLAMA_INDEXER_V4_API_KEY },
|
|
78
80
|
};
|
|
79
|
-
// http agents for streaming (keep-alive)
|
|
80
81
|
const httpAgent = new http_1.default.Agent({ keepAlive: true, maxSockets: 256, maxFreeSockets: 64 });
|
|
81
82
|
const httpsAgent = new https_1.default.Agent({ keepAlive: true, maxSockets: 256, maxFreeSockets: 64 });
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
httpsAgent,
|
|
88
|
-
timeout: INDEXER_REQUEST_TIMEOUT_MS,
|
|
89
|
-
}),
|
|
90
|
-
v4: axios_1.default.create({
|
|
91
|
-
headers: { "x-api-key": LLAMA_INDEXER_V4_API_KEY },
|
|
92
|
-
baseURL: LLAMA_INDEXER_V4_ENDPOINT,
|
|
83
|
+
function createIndexerClient(version) {
|
|
84
|
+
const { endpoint, apiKey } = indexerConfigs[version];
|
|
85
|
+
return axios_1.default.create({
|
|
86
|
+
headers: { "x-api-key": apiKey },
|
|
87
|
+
baseURL: endpoint,
|
|
93
88
|
httpAgent,
|
|
94
89
|
httpsAgent,
|
|
95
90
|
timeout: INDEXER_REQUEST_TIMEOUT_MS,
|
|
96
|
-
})
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const axiosInstances = {
|
|
94
|
+
v2: createIndexerClient("v2"),
|
|
95
|
+
v4: createIndexerClient("v4"),
|
|
97
96
|
};
|
|
97
|
+
const INDEXER_HEAD_BUFFER = 50;
|
|
98
98
|
function checkIndexerConfig(version) {
|
|
99
99
|
const { endpoint, apiKey } = indexerConfigs[version];
|
|
100
100
|
if (!endpoint || !apiKey)
|
|
101
101
|
throw new Error(`Llama Indexer (${version}) URL/api key is not set`);
|
|
102
102
|
}
|
|
103
|
-
//
|
|
103
|
+
// Prefer a configured v4 deployment; retain v2-only installations and explicit rollback.
|
|
104
104
|
function getChainIndexerVersion(chain) {
|
|
105
105
|
if (v4OnlyChainSet.has(chain))
|
|
106
106
|
return "v4";
|
|
@@ -117,13 +117,7 @@ function getChainId(chain) {
|
|
|
117
117
|
throw new Error("Chain not supported");
|
|
118
118
|
return +chainId;
|
|
119
119
|
}
|
|
120
|
-
|
|
121
|
-
return new Set(Object.values(allChainIdChainMapping));
|
|
122
|
-
}
|
|
123
|
-
exports.supportedChainSet2 = getSupportedChains();
|
|
124
|
-
function supportsCursorPagination(version) {
|
|
125
|
-
return version === "v4";
|
|
126
|
-
}
|
|
120
|
+
exports.supportedChainSet2 = new Set(Object.values(allChainIdChainMapping));
|
|
127
121
|
function getLogCursor(log) {
|
|
128
122
|
var _a, _b, _c;
|
|
129
123
|
if (!log)
|
|
@@ -142,13 +136,14 @@ function getTransferCursor(transfer) {
|
|
|
142
136
|
cursor.after_id = String(transfer.id);
|
|
143
137
|
return cursor;
|
|
144
138
|
}
|
|
145
|
-
function
|
|
146
|
-
if (!
|
|
139
|
+
function getTransactionCursor(transaction) {
|
|
140
|
+
if (!transaction)
|
|
147
141
|
return;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
142
|
+
return { after_block: +transaction.block_number, after_index: +transaction.transaction_index };
|
|
143
|
+
}
|
|
144
|
+
function safePush(target, source) {
|
|
145
|
+
for (const row of source)
|
|
146
|
+
target.push(row);
|
|
152
147
|
}
|
|
153
148
|
function applyLocalOffset(rows, remainingOffset) {
|
|
154
149
|
if (remainingOffset <= 0)
|
|
@@ -157,6 +152,72 @@ function applyLocalOffset(rows, remainingOffset) {
|
|
|
157
152
|
return { rows: [], remainingOffset: remainingOffset - rows.length };
|
|
158
153
|
return { rows: rows.slice(remainingOffset), remainingOffset: 0 };
|
|
159
154
|
}
|
|
155
|
+
function assertCursorAdvances(next, previous) {
|
|
156
|
+
if (!next || !Number.isSafeInteger(next.after_block) || !Number.isSafeInteger(next.after_index))
|
|
157
|
+
throw new Error("Indexer v4 returned a page without a valid cursor");
|
|
158
|
+
if (!previous)
|
|
159
|
+
return;
|
|
160
|
+
const advances = next.after_block > previous.after_block ||
|
|
161
|
+
(next.after_block === previous.after_block && (next.after_index > previous.after_index ||
|
|
162
|
+
(next.after_index === previous.after_index && next.after_id !== undefined && previous.after_id !== undefined &&
|
|
163
|
+
BigInt(next.after_id) > BigInt(previous.after_id))));
|
|
164
|
+
if (!advances)
|
|
165
|
+
throw new Error("Indexer v4 pagination cursor did not advance");
|
|
166
|
+
}
|
|
167
|
+
// Translate the public offset/limit contract into keyset pagination. Even all=false
|
|
168
|
+
// may need several requests: the v4 server caps offsets, not the public SDK API.
|
|
169
|
+
async function* getV4Pages(path, key, params, limit, offset, all, getCursor) {
|
|
170
|
+
if (!Number.isSafeInteger(limit) || limit < 0 || !Number.isSafeInteger(offset) || offset < 0)
|
|
171
|
+
throw new Error("Indexer pagination requires a non-negative integer limit and offset");
|
|
172
|
+
if (limit === 0)
|
|
173
|
+
return;
|
|
174
|
+
let cursor;
|
|
175
|
+
let remainingOffset = offset;
|
|
176
|
+
let remainingLimit = all ? Infinity : limit;
|
|
177
|
+
while (remainingLimit > 0) {
|
|
178
|
+
const pageParams = { ...params, limit, offset: 0, includeTotal: false, ...cursor };
|
|
179
|
+
const response = await axiosInstances.v4(path, {
|
|
180
|
+
params: pageParams,
|
|
181
|
+
...(key === "transfers" ? { responseType: "text", transformResponse: (data) => data } : {}),
|
|
182
|
+
}).catch((e) => { throw (0, common_1.formError)(e); });
|
|
183
|
+
const data = key === "transfers" && typeof response.data === "string"
|
|
184
|
+
? await (0, indexer_compatibility_1.parseTransferResponse)(response.data) : response.data;
|
|
185
|
+
const page = data[key];
|
|
186
|
+
if (!Array.isArray(page))
|
|
187
|
+
throw new Error(`Indexer v4 returned an invalid ${key} response`);
|
|
188
|
+
// Capture the cursor before consumers normalize/mutate the raw rows.
|
|
189
|
+
const next = getCursor(page[page.length - 1]);
|
|
190
|
+
const result = applyLocalOffset(page, remainingOffset);
|
|
191
|
+
remainingOffset = result.remainingOffset;
|
|
192
|
+
const rows = result.rows.slice(0, remainingLimit);
|
|
193
|
+
remainingLimit -= rows.length;
|
|
194
|
+
const hasMore = page.length >= limit && remainingLimit > 0;
|
|
195
|
+
if (hasMore || (page.length && cursor))
|
|
196
|
+
assertCursorAdvances(next, cursor);
|
|
197
|
+
if (rows.length)
|
|
198
|
+
yield rows.map(row => (0, indexer_compatibility_1.normalizeV4Row)(row, key));
|
|
199
|
+
if (!hasMore)
|
|
200
|
+
return;
|
|
201
|
+
cursor = next;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function* getIndexerPages(version, path, key, params, limit, offset, all, getCursor) {
|
|
205
|
+
if (version === "v4") {
|
|
206
|
+
yield* getV4Pages(path, key, params, limit, offset, all, getCursor);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
let count = 0;
|
|
210
|
+
do {
|
|
211
|
+
const { data } = await axiosInstances.v2(path, { params: { ...params, limit, offset } })
|
|
212
|
+
.catch((e) => { throw (0, common_1.formError)(e); });
|
|
213
|
+
const rows = data[key];
|
|
214
|
+
yield rows;
|
|
215
|
+
count += rows.length;
|
|
216
|
+
if (!rows.length || rows.length < limit || (key === "logs" && typeof data.totalCount === "number" && data.totalCount <= count))
|
|
217
|
+
return;
|
|
218
|
+
offset += limit;
|
|
219
|
+
} while (all);
|
|
220
|
+
}
|
|
160
221
|
const syncStates = {
|
|
161
222
|
v2: { chainIndexStatus: {} },
|
|
162
223
|
v4: { chainIndexStatus: {} },
|
|
@@ -181,94 +242,88 @@ async function getChainIndexStatus(version) {
|
|
|
181
242
|
}
|
|
182
243
|
});
|
|
183
244
|
return (state.chainIndexStatus = info);
|
|
184
|
-
})()
|
|
245
|
+
})().catch((error) => {
|
|
246
|
+
state.timestamp = undefined;
|
|
247
|
+
state.chainIndexStatus = {};
|
|
248
|
+
throw error;
|
|
249
|
+
});
|
|
185
250
|
return state.chainIndexStatus;
|
|
186
251
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
// ---------------------------------------------------------------------------
|
|
190
|
-
async function streamJsonArrayFromIndexer(opts) {
|
|
191
|
-
const { path, version, arrayKey = "logs", onItem, onChunkStats, shouldStop } = opts;
|
|
252
|
+
async function streamLogs(opts) {
|
|
253
|
+
const { path, version, onItem, onChunkStats, shouldStop } = opts;
|
|
192
254
|
const controller = new AbortController();
|
|
193
255
|
let bytesReceived = 0;
|
|
194
256
|
let itemsProcessed = 0;
|
|
195
257
|
const res = await axiosInstances[version].get(path, {
|
|
196
258
|
responseType: "stream",
|
|
197
|
-
timeout:
|
|
259
|
+
timeout: INDEXER_REQUEST_TIMEOUT_MS,
|
|
198
260
|
maxContentLength: Infinity,
|
|
199
261
|
maxBodyLength: Infinity,
|
|
200
262
|
headers: { Accept: "application/json", "Accept-Encoding": "gzip" },
|
|
201
263
|
signal: controller.signal,
|
|
202
|
-
}).catch((e) => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const isCancellationError = (err) => {
|
|
212
|
-
var _a;
|
|
213
|
-
return (err === null || err === void 0 ? void 0 : err.name) === "CanceledError" ||
|
|
214
|
-
((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes("aborted")) ||
|
|
215
|
-
axios_1.default.isCancel(err) ||
|
|
216
|
-
(err === null || err === void 0 ? void 0 : err.code) === 'ERR_CANCELED';
|
|
217
|
-
};
|
|
218
|
-
const handleError = (err) => isCancellationError(err) ? resolve() : reject(err);
|
|
219
|
-
stream.on('data', (chunk) => {
|
|
220
|
-
bytesReceived += chunk.length;
|
|
221
|
-
onChunkStats === null || onChunkStats === void 0 ? void 0 : onChunkStats({ chunkSize: chunk.length, bytesReceived, itemsProcessed });
|
|
222
|
-
});
|
|
223
|
-
const jsonParser = (0, stream_json_1.parser)();
|
|
224
|
-
const pickFilter = (0, Pick_1.pick)({ filter: arrayKey });
|
|
225
|
-
const arrayStream = (0, StreamArray_1.streamArray)();
|
|
226
|
-
arrayStream.on('data', (data) => {
|
|
227
|
-
if (shouldStop === null || shouldStop === void 0 ? void 0 : shouldStop()) {
|
|
228
|
-
controller.abort();
|
|
229
|
-
stream.destroy();
|
|
230
|
-
return;
|
|
264
|
+
}).catch((e) => { throw (0, common_1.formError)(e); });
|
|
265
|
+
let stopped = false;
|
|
266
|
+
let processingError;
|
|
267
|
+
try {
|
|
268
|
+
await (0, promises_1.pipeline)(res.data, async function* (source) {
|
|
269
|
+
for await (const chunk of source) {
|
|
270
|
+
bytesReceived += chunk.length;
|
|
271
|
+
onChunkStats === null || onChunkStats === void 0 ? void 0 : onChunkStats({ chunkSize: chunk.length, bytesReceived, itemsProcessed });
|
|
272
|
+
yield chunk;
|
|
231
273
|
}
|
|
232
|
-
|
|
233
|
-
|
|
274
|
+
}, (0, stream_json_1.parser)(), (0, Pick_1.pick)({ filter: "logs" }), (0, StreamArray_1.streamArray)(), async (source) => {
|
|
275
|
+
for await (const { value } of source) {
|
|
276
|
+
try {
|
|
277
|
+
await onItem(value);
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
processingError = e;
|
|
281
|
+
throw e;
|
|
282
|
+
}
|
|
234
283
|
itemsProcessed++;
|
|
235
|
-
if (
|
|
236
|
-
|
|
284
|
+
if (shouldStop === null || shouldStop === void 0 ? void 0 : shouldStop()) {
|
|
285
|
+
stopped = true;
|
|
286
|
+
controller.abort();
|
|
287
|
+
break;
|
|
237
288
|
}
|
|
238
289
|
}
|
|
239
|
-
catch { }
|
|
240
290
|
});
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
// Only our requested early stop is successful; remote truncation and decode
|
|
294
|
+
// or processor errors must reject instead of returning incomplete results.
|
|
295
|
+
if (!stopped)
|
|
296
|
+
throw processingError !== null && processingError !== void 0 ? processingError : e;
|
|
297
|
+
}
|
|
298
|
+
onChunkStats === null || onChunkStats === void 0 ? void 0 : onChunkStats({ chunkSize: 0, bytesReceived, itemsProcessed });
|
|
299
|
+
}
|
|
300
|
+
async function decodeStreamingLogs(logs, transformLog, batchDecoder, onlyArgs) {
|
|
301
|
+
for (const log of logs)
|
|
302
|
+
(0, logs_decode_shared_1.normalizeLog)(log, true);
|
|
303
|
+
if (!batchDecoder)
|
|
304
|
+
return Promise.all(logs.map(log => transformLog(log)));
|
|
305
|
+
// The historical streaming fast path returns args without buffered parsedLog metadata.
|
|
306
|
+
const args = await batchDecoder(logs);
|
|
307
|
+
return logs.map((log, i) => {
|
|
308
|
+
if (onlyArgs)
|
|
309
|
+
return args[i];
|
|
310
|
+
const decoded = { ...log, args: args[i] };
|
|
311
|
+
if (!decoded.transactionHash && decoded._originalTransactionHash)
|
|
312
|
+
decoded.transactionHash = decoded._originalTransactionHash;
|
|
313
|
+
return decoded;
|
|
255
314
|
});
|
|
256
315
|
}
|
|
257
|
-
// ---------------------------------------------------------------------------
|
|
258
|
-
// LOGS (v2) – streaming opt-in, legacy default
|
|
259
|
-
// ---------------------------------------------------------------------------
|
|
260
316
|
async function getLogs(options) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
var _a, _b, _c, _d, _e, _f;
|
|
264
|
-
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
|
|
265
|
-
decoderType = "viem", // default to viem for performance
|
|
266
|
-
onWireStats, onDecodeStats, } = options;
|
|
317
|
+
var _a, _b, _c, _d, _e;
|
|
318
|
+
let { all = true, limit = 1000, offset: initialOffset = 0, target, targets = [], flatten = true, debugMode = false, noTarget = false, maxBlockRange, processor, clientStreaming = false, decoderType = "viem", onWireStats, onDecodeStats, } = options;
|
|
267
319
|
if (processor && typeof options.collect === "undefined" && clientStreaming) {
|
|
268
|
-
options
|
|
320
|
+
options = { ...options, collect: false };
|
|
269
321
|
}
|
|
270
|
-
const { chain, fromBlock, toBlock, topic, topic1, topic2, topic3, transformLog, } = await (0, logs_1.getLogParams)(options, true);
|
|
271
|
-
const viemFastPath =
|
|
322
|
+
const { chain, fromBlock, toBlock, topic, topic1, topic2, topic3, topics, transformLog, } = await (0, logs_1.getLogParams)(options, true);
|
|
323
|
+
const viemFastPath =
|
|
324
|
+
// Preserve the historical streaming shape, including entireLog: the viem
|
|
325
|
+
// fast path exposes args without adding the buffered decoder's parsedLog.
|
|
326
|
+
decoderType === "viem" && options.eventAbi && options.parseLog !== false && !options.allowParseFailure
|
|
272
327
|
? (0, logs_decode_shared_1.createViemFastPathBatchDecoder)(options.eventAbi)
|
|
273
328
|
: null;
|
|
274
329
|
if (!debugMode)
|
|
@@ -277,7 +332,11 @@ async function getLogs(options) {
|
|
|
277
332
|
if (processor && (targets === null || targets === void 0 ? void 0 : targets.length) > 1 && !flatten)
|
|
278
333
|
throw new Error("processor is not supported with multiple targets when flatten=false");
|
|
279
334
|
const blockRange = toBlock - fromBlock;
|
|
335
|
+
if (!Number.isSafeInteger(fromBlock) || !Number.isSafeInteger(toBlock) || fromBlock > toBlock)
|
|
336
|
+
throw new Error("getLogs requires integer block bounds with fromBlock <= toBlock");
|
|
280
337
|
const effectiveMaxBlockRange = maxBlockRange !== null && maxBlockRange !== void 0 ? maxBlockRange : (noTarget ? 10000 : Infinity);
|
|
338
|
+
if (!(effectiveMaxBlockRange > 0) || (effectiveMaxBlockRange !== Infinity && !Number.isSafeInteger(effectiveMaxBlockRange)))
|
|
339
|
+
throw new Error("maxBlockRange must be a positive integer");
|
|
281
340
|
if (noTarget && blockRange > 500000) {
|
|
282
341
|
throw new Error("When noTarget is true, block range must be less than 500k blocks. Please narrow down your block range.");
|
|
283
342
|
}
|
|
@@ -286,33 +345,32 @@ async function getLogs(options) {
|
|
|
286
345
|
checkIndexerConfig(indexerVersion);
|
|
287
346
|
const chainId = getChainId(chain);
|
|
288
347
|
const chainIndexStatus = await getChainIndexStatus(indexerVersion);
|
|
289
|
-
const lastIndexedBlock = (_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0;
|
|
348
|
+
const lastIndexedBlock = Number((_b = (_a = chainIndexStatus[chain]) === null || _a === void 0 ? void 0 : _a.block) !== null && _b !== void 0 ? _b : 0);
|
|
349
|
+
if (!Number.isSafeInteger(lastIndexedBlock) || lastIndexedBlock < 0)
|
|
350
|
+
throw new Error(`Indexer returned an invalid last indexed block for ${chain}`);
|
|
290
351
|
if (lastIndexedBlock < toBlock) {
|
|
291
352
|
const percentageMissing = ((toBlock - lastIndexedBlock) / (toBlock - fromBlock)) * 100;
|
|
292
353
|
if (percentageMissing > 50)
|
|
293
354
|
throw new Error(`Indexer not up to date for ${chain}. Last indexed block: ${lastIndexedBlock}, requested block: ${toBlock}`);
|
|
294
355
|
if (env_1.ENV_CONSTANTS.GET_LOGS_INDEXER)
|
|
295
356
|
(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.`);
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
if (!flatten) {
|
|
301
|
-
return indexerLogs.map((bucket, i) => bucket.concat(rpcLogs[i]));
|
|
302
|
-
}
|
|
303
|
-
return indexerLogs.concat(rpcLogs);
|
|
357
|
+
// An empty indexer prefix uses RPC for exactly the original window. The two
|
|
358
|
+
// inclusive ranges otherwise meet at breakBlock / breakBlock + 1.
|
|
359
|
+
const breakBlock = Math.max(fromBlock - 1, lastIndexedBlock - INDEXER_HEAD_BUFFER);
|
|
360
|
+
return getPartiallyIndexedLogs(breakBlock);
|
|
304
361
|
}
|
|
305
362
|
// Re-curse if the requested range is too large
|
|
306
363
|
if (blockRange > effectiveMaxBlockRange) {
|
|
307
364
|
const results = [];
|
|
308
|
-
for (let currentFromBlock = fromBlock; currentFromBlock
|
|
365
|
+
for (let currentFromBlock = fromBlock; currentFromBlock <= toBlock; currentFromBlock += effectiveMaxBlockRange) {
|
|
309
366
|
const currentToBlock = Math.min(currentFromBlock + effectiveMaxBlockRange - 1, toBlock);
|
|
310
367
|
const chunk = await getLogs({ ...options, fromBlock: currentFromBlock, toBlock: currentToBlock });
|
|
311
368
|
results.push(chunk);
|
|
312
369
|
}
|
|
313
|
-
|
|
370
|
+
const splitTargets = typeof target === "string" ? [target] : targets;
|
|
371
|
+
if (flatten || !splitTargets.length)
|
|
314
372
|
return results.flat();
|
|
315
|
-
return
|
|
373
|
+
return splitTargets.map((_, i) => results.map((r) => r[i]).flat());
|
|
316
374
|
}
|
|
317
375
|
let address = target;
|
|
318
376
|
if (typeof target === "string")
|
|
@@ -326,36 +384,15 @@ async function getLogs(options) {
|
|
|
326
384
|
const addressChunks = (0, _1.sliceIntoChunks)((_c = address === null || address === void 0 ? void 0 : address.split(",")) !== null && _c !== void 0 ? _c : [], addressChunkSize);
|
|
327
385
|
if (noTarget && addressChunks.length === 0)
|
|
328
386
|
addressChunks.push(undefined);
|
|
329
|
-
// Safely push large arrays without stack overflow (avoid push.apply argument limits (~125k))
|
|
330
|
-
const safePush = (target, source) => {
|
|
331
|
-
if (source.length === 0)
|
|
332
|
-
return;
|
|
333
|
-
// For very large arrays, split into chunks
|
|
334
|
-
if (source.length > 100000) {
|
|
335
|
-
const CHUNK_SIZE = 50000; // Safe chunk size well below push.apply limit
|
|
336
|
-
for (let i = 0; i < source.length; i += CHUNK_SIZE) {
|
|
337
|
-
const chunk = source.slice(i, i + CHUNK_SIZE);
|
|
338
|
-
Array.prototype.push.apply(target, chunk);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
else {
|
|
342
|
-
Array.prototype.push.apply(target, source);
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
// -----------------------------------------------------------------------
|
|
346
|
-
// Streaming (opt-in) — NO server-side parsing; mirror legacy semantics
|
|
347
|
-
// -----------------------------------------------------------------------
|
|
348
387
|
if (clientStreaming) {
|
|
349
388
|
const useAll = all === true;
|
|
350
389
|
const effectiveLimit = useAll ? Number.POSITIVE_INFINITY : ((_d = options.limit) !== null && _d !== void 0 ? _d : limit);
|
|
351
390
|
const shouldLimit = !useAll && Number.isFinite(effectiveLimit);
|
|
352
|
-
const effectiveOffset =
|
|
353
|
-
const
|
|
391
|
+
const effectiveOffset = (_e = options.offset) !== null && _e !== void 0 ? _e : initialOffset;
|
|
392
|
+
const collected = [];
|
|
354
393
|
const start = debugMode ? Date.now() : 0;
|
|
355
394
|
const MICRO_BATCH_SIZE = +(process.env.LLAMA_INDEXER_MICRO_BATCH || 10000);
|
|
356
|
-
// Check if we need splitByAddress (mapping by target address)
|
|
357
395
|
const splitByAddress = (targets === null || targets === void 0 ? void 0 : targets.length) && !flatten;
|
|
358
|
-
// If splitByAddress, build buckets directly during flushBatch
|
|
359
396
|
const addressBuckets = splitByAddress ? targets.map(() => []) : [];
|
|
360
397
|
const addressIndexMap = splitByAddress
|
|
361
398
|
? Object.fromEntries(targets.map((t, i) => [t.toLowerCase(), i]))
|
|
@@ -367,42 +404,12 @@ async function getLogs(options) {
|
|
|
367
404
|
if (!batch.length)
|
|
368
405
|
return;
|
|
369
406
|
const t0 = Date.now();
|
|
370
|
-
|
|
371
|
-
for (const log of batch) {
|
|
372
|
-
(0, logs_decode_shared_1.normalizeLog)(log, true); // isIndexerCall = true
|
|
373
|
-
}
|
|
374
|
-
// Use fast-path if available, otherwise fallback to default
|
|
375
|
-
const batchFn = viemFastPath !== null && viemFastPath !== void 0 ? viemFastPath : transformLog.batch;
|
|
376
|
-
let transformedLogs;
|
|
377
|
-
if (batchFn) {
|
|
378
|
-
// Fast-path returns only args, need to reconstruct full log objects
|
|
379
|
-
const decodedArgs = await batchFn(batch);
|
|
380
|
-
transformedLogs = new Array(batch.length);
|
|
381
|
-
for (let i = 0; i < batch.length; i++) {
|
|
382
|
-
if (options.onlyArgs) {
|
|
383
|
-
// If onlyArgs is true, return only the args (like transformLog does)
|
|
384
|
-
transformedLogs[i] = decodedArgs[i];
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
transformedLogs[i] = {
|
|
388
|
-
...batch[i],
|
|
389
|
-
args: decodedArgs[i]
|
|
390
|
-
};
|
|
391
|
-
if (!transformedLogs[i].transactionHash && transformedLogs[i]._originalTransactionHash) {
|
|
392
|
-
transformedLogs[i].transactionHash = transformedLogs[i]._originalTransactionHash;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
transformedLogs = await Promise.all(batch.map((log) => transformLog(log)));
|
|
399
|
-
}
|
|
407
|
+
const transformedLogs = await decodeStreamingLogs(batch, transformLog, viemFastPath !== null && viemFastPath !== void 0 ? viemFastPath : transformLog.batch, options.onlyArgs);
|
|
400
408
|
const decodeTime = Date.now() - t0;
|
|
401
409
|
onDecodeStats === null || onDecodeStats === void 0 ? void 0 : onDecodeStats({ batchSize: batch.length, decodeTime, itemsDecoded: batch.length });
|
|
402
410
|
if (processor)
|
|
403
411
|
await processor(transformedLogs);
|
|
404
|
-
if (splitByAddress) {
|
|
405
|
-
// Map directly to address buckets without storing pairs
|
|
412
|
+
if (splitByAddress && options.collect !== false) {
|
|
406
413
|
for (let i = 0; i < batch.length; i++) {
|
|
407
414
|
const raw = batch[i];
|
|
408
415
|
const transformed = transformedLogs[i];
|
|
@@ -413,14 +420,8 @@ async function getLogs(options) {
|
|
|
413
420
|
}
|
|
414
421
|
}
|
|
415
422
|
else if (options.collect !== false) {
|
|
416
|
-
|
|
417
|
-
const pairs = new Array(batch.length);
|
|
418
|
-
for (let i = 0; i < batch.length; i++) {
|
|
419
|
-
pairs[i] = { raw: batch[i], transformed: transformedLogs[i] };
|
|
420
|
-
}
|
|
421
|
-
Array.prototype.push.apply(outPairs, pairs);
|
|
423
|
+
safePush(collected, transformedLogs);
|
|
422
424
|
}
|
|
423
|
-
// If collect=false and not splitByAddress, nothing to store (processor already handled it)
|
|
424
425
|
};
|
|
425
426
|
for (const chunk of addressChunks) {
|
|
426
427
|
if (Array.isArray(chunk) && chunk.length === 0)
|
|
@@ -446,19 +447,15 @@ async function getLogs(options) {
|
|
|
446
447
|
qs.set("offset", "0");
|
|
447
448
|
const transformBatch = [];
|
|
448
449
|
let stopNow = false;
|
|
449
|
-
|
|
450
|
-
// Chain flushes sequentially to avoid concurrent decoding (CPU/GC thrashing)
|
|
451
|
-
const scheduleFlush = (batch) => {
|
|
452
|
-
flushPromise = flushPromise.then(() => flushBatch(batch));
|
|
453
|
-
};
|
|
454
|
-
await streamJsonArrayFromIndexer({
|
|
450
|
+
await streamLogs({
|
|
455
451
|
path: `/logs?${qs.toString()}`,
|
|
456
452
|
version: indexerVersion,
|
|
457
|
-
|
|
458
|
-
onItem: (raw) => {
|
|
453
|
+
onItem: async (raw) => {
|
|
459
454
|
var _a, _b, _c;
|
|
460
455
|
if (stopNow)
|
|
461
456
|
return;
|
|
457
|
+
if (indexerVersion === "v4")
|
|
458
|
+
(0, indexer_compatibility_1.normalizeV4Row)(raw, "logs");
|
|
462
459
|
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));
|
|
463
460
|
if (!okAddress)
|
|
464
461
|
return;
|
|
@@ -472,30 +469,30 @@ async function getLogs(options) {
|
|
|
472
469
|
}
|
|
473
470
|
transformBatch.push(raw);
|
|
474
471
|
remainingLimit--;
|
|
472
|
+
if (remainingLimit <= 0)
|
|
473
|
+
stopNow = true;
|
|
475
474
|
if (transformBatch.length >= MICRO_BATCH_SIZE) {
|
|
476
|
-
// Sequential flush: chain to avoid concurrent decoding
|
|
477
475
|
const batch = transformBatch.splice(0, MICRO_BATCH_SIZE);
|
|
478
|
-
|
|
476
|
+
await flushBatch(batch);
|
|
479
477
|
}
|
|
480
478
|
},
|
|
481
479
|
shouldStop: () => stopNow,
|
|
482
|
-
onChunkStats:
|
|
480
|
+
onChunkStats: onWireStats,
|
|
483
481
|
});
|
|
484
482
|
if (transformBatch.length > 0) {
|
|
485
|
-
|
|
483
|
+
await flushBatch(transformBatch.splice(0, transformBatch.length));
|
|
486
484
|
}
|
|
487
|
-
await flushPromise;
|
|
488
485
|
if (remainingLimit <= 0)
|
|
489
486
|
break;
|
|
490
487
|
}
|
|
491
488
|
if (debugMode) {
|
|
492
489
|
const ms = Date.now() - start;
|
|
493
|
-
(0, debugLog_1.debugLog)(`[Indexer] stream finished: ${
|
|
490
|
+
(0, debugLog_1.debugLog)(`[Indexer] stream finished: ${collected.length} items in ${ms}ms`);
|
|
494
491
|
}
|
|
495
492
|
if (splitByAddress) {
|
|
496
493
|
return addressBuckets;
|
|
497
494
|
}
|
|
498
|
-
return
|
|
495
|
+
return collected;
|
|
499
496
|
}
|
|
500
497
|
const allLogsPairs = [];
|
|
501
498
|
const debugTimeKey = `Indexer-getLogs-${chain}-${topic}-${address}_${Math.random()}`;
|
|
@@ -503,48 +500,24 @@ async function getLogs(options) {
|
|
|
503
500
|
(0, debugLog_1.debugLog)("[Indexer] Pulling logs " + debugTimeKey);
|
|
504
501
|
console.time(debugTimeKey);
|
|
505
502
|
}
|
|
506
|
-
const useCursorPagination = supportsCursorPagination(indexerVersion) && all;
|
|
507
503
|
for (const chunk of addressChunks) {
|
|
508
504
|
if (Array.isArray(chunk) && chunk.length === 0)
|
|
509
505
|
throw new Error("Address chunk cannot be empty");
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const
|
|
517
|
-
addresses: hasAddressFilter ? chunk === null || chunk === void 0 ? void 0 : chunk.join(",") : undefined,
|
|
518
|
-
chainId,
|
|
519
|
-
topic0: topic,
|
|
520
|
-
from_block: fromBlock,
|
|
521
|
-
to_block: toBlock,
|
|
522
|
-
topic1,
|
|
523
|
-
topic2,
|
|
524
|
-
topic3,
|
|
525
|
-
limit,
|
|
526
|
-
offset: chunkOffset,
|
|
527
|
-
noTarget,
|
|
528
|
-
};
|
|
529
|
-
if (useCursorPagination) {
|
|
530
|
-
params.includeTotal = false;
|
|
531
|
-
applyCursorParams(params, cursor);
|
|
532
|
-
}
|
|
533
|
-
const { data: { logs: _logs, totalCount }, } = await axiosInstances[indexerVersion](`/logs`, { params }).catch((e) => { throw (0, common_1.formError)(e); });
|
|
534
|
-
const nextCursor = useCursorPagination ? getLogCursor(_logs[_logs.length - 1]) : undefined;
|
|
535
|
-
const offsetResult = useCursorPagination ? applyLocalOffset(_logs, remainingOffset) : { rows: _logs, remainingOffset };
|
|
536
|
-
remainingOffset = offsetResult.remainingOffset;
|
|
537
|
-
const filtered = offsetResult.rows.filter((l) => {
|
|
506
|
+
const params = {
|
|
507
|
+
addresses: hasAddressFilter ? chunk === null || chunk === void 0 ? void 0 : chunk.join(",") : undefined,
|
|
508
|
+
chainId, topic0: topic, from_block: fromBlock, to_block: toBlock,
|
|
509
|
+
topic1, topic2, topic3, noTarget,
|
|
510
|
+
};
|
|
511
|
+
for await (const rows of getIndexerPages(indexerVersion, "/logs", "logs", params, limit, initialOffset, all, getLogCursor)) {
|
|
512
|
+
const filtered = rows.filter((l) => {
|
|
538
513
|
var _a, _b, _c;
|
|
539
514
|
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));
|
|
540
515
|
return !!isWhitelisted;
|
|
541
516
|
});
|
|
542
517
|
const t0 = Date.now();
|
|
543
|
-
// Normalize logs before decoding (required for consistency)
|
|
544
518
|
for (const log of filtered) {
|
|
545
|
-
(0, logs_decode_shared_1.normalizeLog)(log, true);
|
|
519
|
+
(0, logs_decode_shared_1.normalizeLog)(log, true);
|
|
546
520
|
}
|
|
547
|
-
// Legacy path: use transformLog batch if available, otherwise transform individually
|
|
548
521
|
const transformBatchFn = transformLog.batch || ((logs) => Promise.all(logs.map((log) => transformLog(log))));
|
|
549
522
|
const transformedLogs = await transformBatchFn(filtered);
|
|
550
523
|
const transformedPair = filtered.map((log, idx) => ({ raw: log, transformed: transformedLogs[idx] }));
|
|
@@ -553,33 +526,10 @@ async function getLogs(options) {
|
|
|
553
526
|
onDecodeStats({ batchSize: filtered.length, decodeTime, itemsDecoded: filtered.length });
|
|
554
527
|
if (processor)
|
|
555
528
|
await processor(transformedPair.map((i) => i.transformed));
|
|
556
|
-
|
|
557
|
-
// Only accumulate if collect is enabled (default true for backward compatibility)
|
|
558
|
-
const shouldCollect = (options.collect !== false);
|
|
559
|
-
if (shouldCollect) {
|
|
529
|
+
if (options.collect !== false) {
|
|
560
530
|
safePush(allLogsPairs, transformedPair);
|
|
561
531
|
}
|
|
562
|
-
|
|
563
|
-
if (useCursorPagination && nextCursor) {
|
|
564
|
-
cursor = nextCursor;
|
|
565
|
-
chunkOffset = 0;
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
chunkOffset += limit;
|
|
569
|
-
}
|
|
570
|
-
if (_logs.length === 0) {
|
|
571
|
-
hasMore = false;
|
|
572
|
-
}
|
|
573
|
-
else if (_logs.length < limit) {
|
|
574
|
-
hasMore = false;
|
|
575
|
-
}
|
|
576
|
-
else if (typeof totalCount === 'number' && totalCount <= logCount) {
|
|
577
|
-
hasMore = false;
|
|
578
|
-
}
|
|
579
|
-
else {
|
|
580
|
-
hasMore = true;
|
|
581
|
-
}
|
|
582
|
-
} while (all && hasMore);
|
|
532
|
+
}
|
|
583
533
|
}
|
|
584
534
|
if (debugMode) {
|
|
585
535
|
console.timeEnd(debugTimeKey);
|
|
@@ -599,9 +549,111 @@ async function getLogs(options) {
|
|
|
599
549
|
});
|
|
600
550
|
return mapped;
|
|
601
551
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
552
|
+
return allLogsPairs.map(i => i.transformed);
|
|
553
|
+
async function getPartiallyIndexedLogs(breakBlock) {
|
|
554
|
+
if (!Number.isSafeInteger(limit) || limit < 0 || !Number.isSafeInteger(initialOffset) || initialOffset < 0)
|
|
555
|
+
throw new Error("Indexer pagination requires a non-negative integer limit and offset");
|
|
556
|
+
const requestedTargets = typeof target === "string" ? [target] : targets;
|
|
557
|
+
const uniqueTargets = [...new Set(requestedTargets.map(t => t.toLowerCase()))];
|
|
558
|
+
const splitByAddress = requestedTargets.length > 0 && !flatten;
|
|
559
|
+
const collected = splitByAddress ? requestedTargets.map(() => []) : [];
|
|
560
|
+
const targetBuckets = new Map();
|
|
561
|
+
requestedTargets.forEach((t, i) => {
|
|
562
|
+
var _a;
|
|
563
|
+
const key = t.toLowerCase();
|
|
564
|
+
targetBuckets.set(key, [...((_a = targetBuckets.get(key)) !== null && _a !== void 0 ? _a : []), i]);
|
|
565
|
+
});
|
|
566
|
+
let remainingOffset = initialOffset;
|
|
567
|
+
let remainingLimit = all ? Infinity : limit;
|
|
568
|
+
if (limit === 0)
|
|
569
|
+
return collected;
|
|
570
|
+
const rpcTransform = (await (0, logs_1.getLogParams)(options, false)).transformLog;
|
|
571
|
+
const finished = Symbol("partial log limit reached");
|
|
572
|
+
let seenBlock;
|
|
573
|
+
const seen = new Set();
|
|
574
|
+
async function consume(rawRows, isIndexer, start, end) {
|
|
575
|
+
var _a, _b, _c, _d;
|
|
576
|
+
const selected = [];
|
|
577
|
+
for (const row of rawRows) {
|
|
578
|
+
const block = Number((_a = row.blockNumber) !== null && _a !== void 0 ? _a : row.block_number);
|
|
579
|
+
if (!Number.isSafeInteger(block))
|
|
580
|
+
throw new Error("Log returned without a valid block number");
|
|
581
|
+
if (block < start || block > end)
|
|
582
|
+
continue;
|
|
583
|
+
if (block !== seenBlock) {
|
|
584
|
+
seenBlock = block;
|
|
585
|
+
seen.clear();
|
|
586
|
+
}
|
|
587
|
+
const hash = (_b = row.transactionHash) !== null && _b !== void 0 ? _b : row.transaction_hash;
|
|
588
|
+
const index = (_d = (_c = row.logIndex) !== null && _c !== void 0 ? _c : row.index) !== null && _d !== void 0 ? _d : row.log_index;
|
|
589
|
+
if (hash !== undefined && index !== undefined) {
|
|
590
|
+
const key = `${String(hash).toLowerCase()}:${index}`;
|
|
591
|
+
if (seen.has(key))
|
|
592
|
+
continue;
|
|
593
|
+
seen.add(key);
|
|
594
|
+
}
|
|
595
|
+
if (remainingOffset > 0) {
|
|
596
|
+
remainingOffset--;
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
if (remainingLimit <= 0)
|
|
600
|
+
break;
|
|
601
|
+
selected.push(clientStreaming && !isIndexer ? { ...row } : row);
|
|
602
|
+
remainingLimit--;
|
|
603
|
+
}
|
|
604
|
+
if (selected.length) {
|
|
605
|
+
const transform = isIndexer ? transformLog : rpcTransform;
|
|
606
|
+
const startTime = Date.now();
|
|
607
|
+
const decoded = clientStreaming
|
|
608
|
+
? await decodeStreamingLogs(selected, transform, viemFastPath !== null && viemFastPath !== void 0 ? viemFastPath : transform.batch, options.onlyArgs)
|
|
609
|
+
: await Promise.all(selected.map(row => transform(row)));
|
|
610
|
+
onDecodeStats === null || onDecodeStats === void 0 ? void 0 : onDecodeStats({ batchSize: selected.length, decodeTime: Date.now() - startTime, itemsDecoded: selected.length });
|
|
611
|
+
if (processor)
|
|
612
|
+
await processor(decoded);
|
|
613
|
+
if (options.collect !== false) {
|
|
614
|
+
if (splitByAddress)
|
|
615
|
+
selected.forEach((row, i) => {
|
|
616
|
+
var _a, _b, _c;
|
|
617
|
+
for (const bucket of (_c = targetBuckets.get((_b = ((_a = row.source) !== null && _a !== void 0 ? _a : row.address)) === null || _b === void 0 ? void 0 : _b.toLowerCase())) !== null && _c !== void 0 ? _c : [])
|
|
618
|
+
collected[bucket].push(decoded[i]);
|
|
619
|
+
});
|
|
620
|
+
else
|
|
621
|
+
safePush(collected, decoded);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (remainingLimit <= 0)
|
|
625
|
+
throw finished;
|
|
626
|
+
}
|
|
627
|
+
const rawOptions = {
|
|
628
|
+
...options, chain, topic, topics, target: undefined, targets: uniqueTargets,
|
|
629
|
+
eventAbi: undefined, parseLog: false, entireLog: true, onlyArgs: false,
|
|
630
|
+
flatten: true, onDecodeStats: undefined,
|
|
631
|
+
};
|
|
632
|
+
try {
|
|
633
|
+
if (breakBlock >= fromBlock) {
|
|
634
|
+
await getLogs({
|
|
635
|
+
...rawOptions, fromBlock, toBlock: breakBlock, all: true, offset: 0, collect: false,
|
|
636
|
+
processor: rows => consume(rows, true, fromBlock, breakBlock),
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
// RPC cache reads may extend the physical fetch by a buffer, but logs.ts
|
|
640
|
+
// filters to these exact bounds before any decoding or processing here.
|
|
641
|
+
for (let start = breakBlock + 1; start <= toBlock; start += effectiveMaxBlockRange) {
|
|
642
|
+
const end = Math.min(toBlock, start + effectiveMaxBlockRange - 1);
|
|
643
|
+
const rows = await (0, logs_1.getLogs)({
|
|
644
|
+
...rawOptions, fromBlock: start, toBlock: end, skipIndexer: true,
|
|
645
|
+
maxBlockRange: undefined, processor: undefined,
|
|
646
|
+
});
|
|
647
|
+
rows.sort((a, b) => { var _a, _b; return a.blockNumber - b.blockNumber || ((_a = a.logIndex) !== null && _a !== void 0 ? _a : a.index) - ((_b = b.logIndex) !== null && _b !== void 0 ? _b : b.index); });
|
|
648
|
+
await consume(rows, false, start, end);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
if (error !== finished)
|
|
653
|
+
throw error;
|
|
654
|
+
}
|
|
655
|
+
return collected;
|
|
656
|
+
}
|
|
605
657
|
}
|
|
606
658
|
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, }) {
|
|
607
659
|
var _a, _b;
|
|
@@ -651,55 +703,27 @@ async function getTokenTransfers({ chain = "ethereum", fromAddressFilter, fromBl
|
|
|
651
703
|
(0, debugLog_1.debugLog)("[Indexer] Pulling token transfers " + debugTimeKey);
|
|
652
704
|
console.time(debugTimeKey);
|
|
653
705
|
}
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
switch (transferType) {
|
|
676
|
-
case "in":
|
|
677
|
-
params.to_address = true;
|
|
678
|
-
break;
|
|
679
|
-
case "out":
|
|
680
|
-
params.from_address = true;
|
|
681
|
-
break;
|
|
682
|
-
case "all":
|
|
683
|
-
params.from_address = true;
|
|
684
|
-
params.to_address = true;
|
|
685
|
-
break;
|
|
686
|
-
default:
|
|
687
|
-
throw new Error("Invalid transferType");
|
|
688
|
-
}
|
|
689
|
-
const { data: { transfers: _logs }, } = await axiosInstances[indexerVersion](`/token-transfers`, { params }).catch((e) => { throw (0, common_1.formError)(e); });
|
|
690
|
-
const nextCursor = useCursorPagination ? getTransferCursor(_logs[_logs.length - 1]) : undefined;
|
|
691
|
-
const offsetResult = useCursorPagination ? applyLocalOffset(_logs, remainingOffset) : { rows: _logs, remainingOffset };
|
|
692
|
-
remainingOffset = offsetResult.remainingOffset;
|
|
693
|
-
rawTransfers.push(...offsetResult.rows);
|
|
694
|
-
if (useCursorPagination && nextCursor) {
|
|
695
|
-
cursor = nextCursor;
|
|
696
|
-
currentOffset = 0;
|
|
697
|
-
}
|
|
698
|
-
else {
|
|
699
|
-
currentOffset += limit;
|
|
700
|
-
}
|
|
701
|
-
hasMore = _logs.length === limit;
|
|
702
|
-
} while (all && hasMore);
|
|
706
|
+
const params = {
|
|
707
|
+
addresses, chainId, from_block: fromBlock, to_block: toBlock, tokens,
|
|
708
|
+
from_address: false, to_address: false,
|
|
709
|
+
};
|
|
710
|
+
switch (transferType) {
|
|
711
|
+
case "in":
|
|
712
|
+
params.to_address = true;
|
|
713
|
+
break;
|
|
714
|
+
case "out":
|
|
715
|
+
params.from_address = true;
|
|
716
|
+
break;
|
|
717
|
+
case "all":
|
|
718
|
+
params.from_address = true;
|
|
719
|
+
params.to_address = true;
|
|
720
|
+
break;
|
|
721
|
+
default:
|
|
722
|
+
throw new Error("Invalid transferType");
|
|
723
|
+
}
|
|
724
|
+
for await (const rows of getIndexerPages(indexerVersion, "/token-transfers", "transfers", params, limit, offset, all, getTransferCursor)) {
|
|
725
|
+
safePush(rawTransfers, rows);
|
|
726
|
+
}
|
|
703
727
|
const filteredTransfers = rawTransfers.filter((l) => {
|
|
704
728
|
if (!fromFilterEnabled)
|
|
705
729
|
return true;
|
|
@@ -766,7 +790,22 @@ async function getTransactions({ chain = "ethereum", addresses, transaction_hash
|
|
|
766
790
|
(0, debugLog_1.debugLog)("[Indexer] Pulling transactions " + debugTimeKey);
|
|
767
791
|
console.time(debugTimeKey);
|
|
768
792
|
}
|
|
769
|
-
|
|
793
|
+
let transactions;
|
|
794
|
+
if (indexerVersion === "v4") {
|
|
795
|
+
transactions = [];
|
|
796
|
+
// all=false with limit=0 historically omitted limit (server default 1000).
|
|
797
|
+
const requestedLimit = typeof limit === "number" && limit > 0 ? limit : 1000;
|
|
798
|
+
// v2 ignores limit when all=true. Preserve that public contract without
|
|
799
|
+
// turning a small caller limit into hundreds of v4 network round trips.
|
|
800
|
+
const pageLimit = all ? Math.max(1000, requestedLimit) : requestedLimit;
|
|
801
|
+
for await (const rows of getV4Pages("/transactions", "transactions", params, pageLimit, offset, all, getTransactionCursor)) {
|
|
802
|
+
safePush(transactions, rows);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
const { data } = await axiosInstances.v2('/transactions', { params }).catch((e) => { throw (0, common_1.formError)(e); });
|
|
807
|
+
transactions = data.transactions;
|
|
808
|
+
}
|
|
770
809
|
if (debugMode) {
|
|
771
810
|
console.timeEnd(debugTimeKey);
|
|
772
811
|
(0, debugLog_1.debugLog)("Transactions pulled " + chain, addresses || transaction_hashes, (transactions === null || transactions === void 0 ? void 0 : transactions.length) || 0);
|