ponder 0.9.3 → 0.9.4
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/dist/bin/ponder.js +527 -485
- package/dist/bin/ponder.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/build/configAndIndexingFunctions.ts +547 -512
- package/src/config/index.ts +4 -4
|
@@ -23,10 +23,11 @@ import {
|
|
|
23
23
|
defaultTransactionReceiptInclude,
|
|
24
24
|
defaultTransferFilterInclude,
|
|
25
25
|
} from "@/sync/filter.js";
|
|
26
|
+
import type { SyncBlock } from "@/types/sync.js";
|
|
26
27
|
import { chains } from "@/utils/chains.js";
|
|
27
28
|
import { toLowerCase } from "@/utils/lowercase.js";
|
|
28
29
|
import { dedupe } from "@ponder/common";
|
|
29
|
-
import type
|
|
30
|
+
import { BlockNotFoundError, type Hex, type LogTopic, hexToNumber } from "viem";
|
|
30
31
|
import { buildLogFactory } from "./factory.js";
|
|
31
32
|
|
|
32
33
|
const flattenSources = <
|
|
@@ -73,6 +74,45 @@ export async function buildConfigAndIndexingFunctions({
|
|
|
73
74
|
}> {
|
|
74
75
|
const logs: { level: "warn" | "info" | "debug"; msg: string }[] = [];
|
|
75
76
|
|
|
77
|
+
const perNetworkLatestBlockNumber = new Map<string, Promise<number>>();
|
|
78
|
+
|
|
79
|
+
const resolveBlockNumber = async (
|
|
80
|
+
blockNumberOrTag: number | "latest" | undefined,
|
|
81
|
+
network: Network,
|
|
82
|
+
) => {
|
|
83
|
+
if (blockNumberOrTag === undefined) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (Number.isNaN(blockNumberOrTag)) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
if (blockNumberOrTag === "latest") {
|
|
91
|
+
if (perNetworkLatestBlockNumber.has(network.name)) {
|
|
92
|
+
return perNetworkLatestBlockNumber.get(network.name)!;
|
|
93
|
+
} else {
|
|
94
|
+
const blockPromise = network.transport
|
|
95
|
+
.request({
|
|
96
|
+
method: "eth_getBlockByNumber",
|
|
97
|
+
params: ["latest", false],
|
|
98
|
+
})
|
|
99
|
+
.then((block) => {
|
|
100
|
+
if (!block)
|
|
101
|
+
throw new BlockNotFoundError({ blockNumber: "latest" as any });
|
|
102
|
+
return hexToNumber((block as SyncBlock).number);
|
|
103
|
+
})
|
|
104
|
+
.catch((e) => {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Unable to fetch "latest" block for network '${network.name}':\n${e.message}`,
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
perNetworkLatestBlockNumber.set(network.name, blockPromise);
|
|
110
|
+
return blockPromise;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return blockNumberOrTag;
|
|
114
|
+
};
|
|
115
|
+
|
|
76
116
|
const networks: Network[] = await Promise.all(
|
|
77
117
|
Object.entries(config.networks).map(async ([networkName, network]) => {
|
|
78
118
|
const { chainId, transport } = network;
|
|
@@ -217,25 +257,6 @@ export async function buildConfigAndIndexingFunctions({
|
|
|
217
257
|
);
|
|
218
258
|
}
|
|
219
259
|
|
|
220
|
-
const startBlockMaybeNan = source.startBlock;
|
|
221
|
-
const startBlock = Number.isNaN(startBlockMaybeNan)
|
|
222
|
-
? undefined
|
|
223
|
-
: startBlockMaybeNan;
|
|
224
|
-
const endBlockMaybeNan = source.endBlock;
|
|
225
|
-
const endBlock = Number.isNaN(endBlockMaybeNan)
|
|
226
|
-
? undefined
|
|
227
|
-
: endBlockMaybeNan;
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
startBlock !== undefined &&
|
|
231
|
-
endBlock !== undefined &&
|
|
232
|
-
endBlock < startBlock
|
|
233
|
-
) {
|
|
234
|
-
throw new Error(
|
|
235
|
-
`Validation failed: Start block for '${source.name}' is after end block (${startBlock} > ${endBlock}).`,
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
260
|
const network = networks.find((n) => n.name === source.network);
|
|
240
261
|
if (!network) {
|
|
241
262
|
throw new Error(
|
|
@@ -246,511 +267,527 @@ export async function buildConfigAndIndexingFunctions({
|
|
|
246
267
|
.join(", ")}].`,
|
|
247
268
|
);
|
|
248
269
|
}
|
|
270
|
+
|
|
271
|
+
const startBlock = await resolveBlockNumber(source.startBlock, network);
|
|
272
|
+
const endBlock = await resolveBlockNumber(source.endBlock, network);
|
|
273
|
+
|
|
274
|
+
if (
|
|
275
|
+
startBlock !== undefined &&
|
|
276
|
+
endBlock !== undefined &&
|
|
277
|
+
endBlock < startBlock
|
|
278
|
+
) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`Validation failed: Start block for '${source.name}' is after end block (${startBlock} > ${endBlock}).`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
249
283
|
}
|
|
250
284
|
|
|
251
|
-
const contractSources: ContractSource[] =
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
285
|
+
const contractSources: ContractSource[] = (
|
|
286
|
+
await Promise.all(
|
|
287
|
+
flattenSources(config.contracts ?? {}).map(
|
|
288
|
+
async (source): Promise<ContractSource[]> => {
|
|
289
|
+
const network = networks.find((n) => n.name === source.network)!;
|
|
290
|
+
|
|
291
|
+
// Get indexing function that were registered for this contract
|
|
292
|
+
const registeredLogEvents: string[] = [];
|
|
293
|
+
const registeredCallTraceEvents: string[] = [];
|
|
294
|
+
for (const eventName of Object.keys(indexingFunctions)) {
|
|
295
|
+
// log event
|
|
296
|
+
if (eventName.includes(":")) {
|
|
297
|
+
const [logContractName, logEventName] = eventName.split(":") as [
|
|
298
|
+
string,
|
|
299
|
+
string,
|
|
300
|
+
];
|
|
301
|
+
if (logContractName === source.name && logEventName !== "setup") {
|
|
302
|
+
registeredLogEvents.push(logEventName);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// trace event
|
|
307
|
+
if (eventName.includes(".")) {
|
|
308
|
+
const [functionContractName, functionName] = eventName.split(
|
|
309
|
+
".",
|
|
310
|
+
) as [string, string];
|
|
311
|
+
if (functionContractName === source.name) {
|
|
312
|
+
registeredCallTraceEvents.push(functionName);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
269
315
|
}
|
|
270
|
-
}
|
|
271
316
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
317
|
+
// Note: This can probably throw for invalid ABIs. Consider adding explicit ABI validation before this line.
|
|
318
|
+
const abiEvents = buildAbiEvents({ abi: source.abi });
|
|
319
|
+
const abiFunctions = buildAbiFunctions({ abi: source.abi });
|
|
320
|
+
|
|
321
|
+
const registeredEventSelectors: Hex[] = [];
|
|
322
|
+
// Validate that the registered log events exist in the abi
|
|
323
|
+
for (const logEvent of registeredLogEvents) {
|
|
324
|
+
const abiEvent = abiEvents.bySafeName[logEvent];
|
|
325
|
+
if (abiEvent === undefined) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Validation failed: Event name for event '${logEvent}' not found in the contract ABI. Got '${logEvent}', expected one of [${Object.keys(
|
|
328
|
+
abiEvents.bySafeName,
|
|
329
|
+
)
|
|
330
|
+
.map((eventName) => `'${eventName}'`)
|
|
331
|
+
.join(", ")}].`,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
registeredEventSelectors.push(abiEvent.selector);
|
|
280
336
|
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
337
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
);
|
|
300
|
-
}
|
|
338
|
+
const registeredFunctionSelectors: Hex[] = [];
|
|
339
|
+
for (const _function of registeredCallTraceEvents) {
|
|
340
|
+
const abiFunction = abiFunctions.bySafeName[_function];
|
|
341
|
+
if (abiFunction === undefined) {
|
|
342
|
+
throw new Error(
|
|
343
|
+
`Validation failed: Function name for function '${_function}' not found in the contract ABI. Got '${_function}', expected one of [${Object.keys(
|
|
344
|
+
abiFunctions.bySafeName,
|
|
345
|
+
)
|
|
346
|
+
.map((eventName) => `'${eventName}'`)
|
|
347
|
+
.join(", ")}].`,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
registeredFunctionSelectors.push(abiFunction.selector);
|
|
352
|
+
}
|
|
301
353
|
|
|
302
|
-
|
|
303
|
-
|
|
354
|
+
const topicsArray: {
|
|
355
|
+
topic0: LogTopic;
|
|
356
|
+
topic1: LogTopic;
|
|
357
|
+
topic2: LogTopic;
|
|
358
|
+
topic3: LogTopic;
|
|
359
|
+
}[] = [];
|
|
360
|
+
|
|
361
|
+
if (source.filter !== undefined) {
|
|
362
|
+
const eventFilters = Array.isArray(source.filter)
|
|
363
|
+
? source.filter
|
|
364
|
+
: [source.filter];
|
|
365
|
+
|
|
366
|
+
for (const filter of eventFilters) {
|
|
367
|
+
const abiEvent = abiEvents.bySafeName[filter.event];
|
|
368
|
+
if (!abiEvent) {
|
|
369
|
+
throw new Error(
|
|
370
|
+
`Validation failed: Invalid filter for contract '${
|
|
371
|
+
source.name
|
|
372
|
+
}'. Got event name '${filter.event}', expected one of [${Object.keys(
|
|
373
|
+
abiEvents.bySafeName,
|
|
374
|
+
)
|
|
375
|
+
.map((n) => `'${n}'`)
|
|
376
|
+
.join(", ")}].`,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
topicsArray.push(...buildTopics(source.abi, eventFilters));
|
|
382
|
+
|
|
383
|
+
// event selectors that have a filter
|
|
384
|
+
const filteredEventSelectors: Hex[] = topicsArray.map(
|
|
385
|
+
(t) => t.topic0 as Hex,
|
|
386
|
+
);
|
|
387
|
+
// event selectors that are registered but don't have a filter
|
|
388
|
+
const excludedRegisteredEventSelectors =
|
|
389
|
+
registeredEventSelectors.filter(
|
|
390
|
+
(s) => filteredEventSelectors.includes(s) === false,
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
for (const selector of filteredEventSelectors) {
|
|
394
|
+
if (registeredEventSelectors.includes(selector) === false) {
|
|
395
|
+
throw new Error(
|
|
396
|
+
`Validation failed: Event selector '${abiEvents.bySelector[selector]?.safeName}' is used in a filter but does not have a corresponding indexing function.`,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (excludedRegisteredEventSelectors.length > 0) {
|
|
402
|
+
topicsArray.push({
|
|
403
|
+
topic0: excludedRegisteredEventSelectors,
|
|
404
|
+
topic1: null,
|
|
405
|
+
topic2: null,
|
|
406
|
+
topic3: null,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
} else {
|
|
410
|
+
topicsArray.push({
|
|
411
|
+
topic0: registeredEventSelectors,
|
|
412
|
+
topic1: null,
|
|
413
|
+
topic2: null,
|
|
414
|
+
topic3: null,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
304
417
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (abiFunction === undefined) {
|
|
309
|
-
throw new Error(
|
|
310
|
-
`Validation failed: Function name for function '${_function}' not found in the contract ABI. Got '${_function}', expected one of [${Object.keys(
|
|
311
|
-
abiFunctions.bySafeName,
|
|
312
|
-
)
|
|
313
|
-
.map((eventName) => `'${eventName}'`)
|
|
314
|
-
.join(", ")}].`,
|
|
418
|
+
const fromBlock = await resolveBlockNumber(
|
|
419
|
+
source.startBlock,
|
|
420
|
+
network,
|
|
315
421
|
);
|
|
316
|
-
|
|
422
|
+
const toBlock = await resolveBlockNumber(source.endBlock, network);
|
|
317
423
|
|
|
318
|
-
|
|
319
|
-
|
|
424
|
+
const contractMetadata = {
|
|
425
|
+
type: "contract",
|
|
426
|
+
abi: source.abi,
|
|
427
|
+
abiEvents,
|
|
428
|
+
abiFunctions,
|
|
429
|
+
name: source.name,
|
|
430
|
+
network,
|
|
431
|
+
} as const;
|
|
320
432
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
433
|
+
const resolvedAddress = source?.address;
|
|
434
|
+
|
|
435
|
+
if (
|
|
436
|
+
typeof resolvedAddress === "object" &&
|
|
437
|
+
!Array.isArray(resolvedAddress)
|
|
438
|
+
) {
|
|
439
|
+
// Note that this can throw.
|
|
440
|
+
const logFactory = buildLogFactory({
|
|
441
|
+
chainId: network.chainId,
|
|
442
|
+
...resolvedAddress,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const logSources = topicsArray.map(
|
|
446
|
+
(topics) =>
|
|
447
|
+
({
|
|
448
|
+
...contractMetadata,
|
|
449
|
+
filter: {
|
|
450
|
+
type: "log",
|
|
451
|
+
chainId: network.chainId,
|
|
452
|
+
address: logFactory,
|
|
453
|
+
topic0: topics.topic0,
|
|
454
|
+
topic1: topics.topic1,
|
|
455
|
+
topic2: topics.topic2,
|
|
456
|
+
topic3: topics.topic3,
|
|
457
|
+
fromBlock,
|
|
458
|
+
toBlock,
|
|
459
|
+
include: defaultLogFilterInclude.concat(
|
|
460
|
+
source.includeTransactionReceipts
|
|
461
|
+
? defaultTransactionReceiptInclude
|
|
462
|
+
: [],
|
|
463
|
+
),
|
|
464
|
+
},
|
|
465
|
+
}) satisfies ContractSource,
|
|
344
466
|
);
|
|
467
|
+
|
|
468
|
+
if (source.includeCallTraces) {
|
|
469
|
+
return [
|
|
470
|
+
...logSources,
|
|
471
|
+
{
|
|
472
|
+
...contractMetadata,
|
|
473
|
+
filter: {
|
|
474
|
+
type: "trace",
|
|
475
|
+
chainId: network.chainId,
|
|
476
|
+
fromAddress: undefined,
|
|
477
|
+
toAddress: logFactory,
|
|
478
|
+
callType: "CALL",
|
|
479
|
+
functionSelector: registeredFunctionSelectors,
|
|
480
|
+
includeReverted: false,
|
|
481
|
+
fromBlock,
|
|
482
|
+
toBlock,
|
|
483
|
+
include: defaultTraceFilterInclude.concat(
|
|
484
|
+
source.includeTransactionReceipts
|
|
485
|
+
? defaultTransactionReceiptInclude
|
|
486
|
+
: [],
|
|
487
|
+
),
|
|
488
|
+
},
|
|
489
|
+
} satisfies ContractSource,
|
|
490
|
+
];
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return logSources;
|
|
494
|
+
} else if (resolvedAddress !== undefined) {
|
|
495
|
+
for (const address of Array.isArray(resolvedAddress)
|
|
496
|
+
? resolvedAddress
|
|
497
|
+
: [resolvedAddress]) {
|
|
498
|
+
if (!address!.startsWith("0x"))
|
|
499
|
+
throw new Error(
|
|
500
|
+
`Validation failed: Invalid prefix for address '${address}'. Got '${address!.slice(
|
|
501
|
+
0,
|
|
502
|
+
2,
|
|
503
|
+
)}', expected '0x'.`,
|
|
504
|
+
);
|
|
505
|
+
if (address!.length !== 42)
|
|
506
|
+
throw new Error(
|
|
507
|
+
`Validation failed: Invalid length for address '${address}'. Got ${address!.length}, expected 42 characters.`,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
345
510
|
}
|
|
346
|
-
}
|
|
347
511
|
|
|
348
|
-
|
|
512
|
+
const validatedAddress = Array.isArray(resolvedAddress)
|
|
513
|
+
? dedupe(resolvedAddress).map((r) => toLowerCase(r))
|
|
514
|
+
: resolvedAddress !== undefined
|
|
515
|
+
? toLowerCase(resolvedAddress)
|
|
516
|
+
: undefined;
|
|
517
|
+
|
|
518
|
+
const logSources = topicsArray.map(
|
|
519
|
+
(topics) =>
|
|
520
|
+
({
|
|
521
|
+
...contractMetadata,
|
|
522
|
+
filter: {
|
|
523
|
+
type: "log",
|
|
524
|
+
chainId: network.chainId,
|
|
525
|
+
address: validatedAddress,
|
|
526
|
+
topic0: topics.topic0,
|
|
527
|
+
topic1: topics.topic1,
|
|
528
|
+
topic2: topics.topic2,
|
|
529
|
+
topic3: topics.topic3,
|
|
530
|
+
fromBlock,
|
|
531
|
+
toBlock,
|
|
532
|
+
include: defaultLogFilterInclude.concat(
|
|
533
|
+
source.includeTransactionReceipts
|
|
534
|
+
? defaultTransactionReceiptInclude
|
|
535
|
+
: [],
|
|
536
|
+
),
|
|
537
|
+
},
|
|
538
|
+
}) satisfies ContractSource,
|
|
539
|
+
);
|
|
349
540
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
541
|
+
if (source.includeCallTraces) {
|
|
542
|
+
return [
|
|
543
|
+
...logSources,
|
|
544
|
+
{
|
|
545
|
+
...contractMetadata,
|
|
546
|
+
filter: {
|
|
547
|
+
type: "trace",
|
|
548
|
+
chainId: network.chainId,
|
|
549
|
+
fromAddress: undefined,
|
|
550
|
+
toAddress: Array.isArray(validatedAddress)
|
|
551
|
+
? validatedAddress
|
|
552
|
+
: validatedAddress === undefined
|
|
553
|
+
? undefined
|
|
554
|
+
: [validatedAddress],
|
|
555
|
+
callType: "CALL",
|
|
556
|
+
functionSelector: registeredFunctionSelectors,
|
|
557
|
+
includeReverted: false,
|
|
558
|
+
fromBlock,
|
|
559
|
+
toBlock,
|
|
560
|
+
include: defaultTraceFilterInclude.concat(
|
|
561
|
+
source.includeTransactionReceipts
|
|
562
|
+
? defaultTransactionReceiptInclude
|
|
563
|
+
: [],
|
|
564
|
+
),
|
|
565
|
+
},
|
|
566
|
+
} satisfies ContractSource,
|
|
567
|
+
];
|
|
568
|
+
} else return logSources;
|
|
569
|
+
},
|
|
570
|
+
),
|
|
571
|
+
)
|
|
572
|
+
)
|
|
573
|
+
.flat() // Remove sources with no registered indexing functions
|
|
574
|
+
.filter((source) => {
|
|
575
|
+
const hasNoRegisteredIndexingFunctions =
|
|
576
|
+
source.filter.type === "trace"
|
|
577
|
+
? Array.isArray(source.filter.functionSelector) &&
|
|
578
|
+
source.filter.functionSelector.length === 0
|
|
579
|
+
: Array.isArray(source.filter.topic0) &&
|
|
580
|
+
source.filter.topic0?.length === 0;
|
|
581
|
+
if (hasNoRegisteredIndexingFunctions) {
|
|
582
|
+
logs.push({
|
|
583
|
+
level: "debug",
|
|
584
|
+
msg: `No indexing functions were registered for '${
|
|
585
|
+
source.name
|
|
586
|
+
}' ${source.filter.type === "trace" ? "traces" : "logs"}`,
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
return hasNoRegisteredIndexingFunctions === false;
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
const accountSources: AccountSource[] = (
|
|
593
|
+
await Promise.all(
|
|
594
|
+
flattenSources(config.accounts ?? {}).map(
|
|
595
|
+
async (source): Promise<AccountSource[]> => {
|
|
596
|
+
const network = networks.find((n) => n.name === source.network)!;
|
|
597
|
+
|
|
598
|
+
const fromBlock = await resolveBlockNumber(
|
|
599
|
+
source.startBlock,
|
|
600
|
+
network,
|
|
358
601
|
);
|
|
602
|
+
const toBlock = await resolveBlockNumber(source.endBlock, network);
|
|
359
603
|
|
|
360
|
-
|
|
361
|
-
|
|
604
|
+
const resolvedAddress = source?.address;
|
|
605
|
+
|
|
606
|
+
if (resolvedAddress === undefined) {
|
|
362
607
|
throw new Error(
|
|
363
|
-
`Validation failed:
|
|
608
|
+
`Validation failed: Account '${source.name}' must specify an 'address'.`,
|
|
364
609
|
);
|
|
365
610
|
}
|
|
366
|
-
}
|
|
367
611
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
612
|
+
if (
|
|
613
|
+
typeof resolvedAddress === "object" &&
|
|
614
|
+
!Array.isArray(resolvedAddress)
|
|
615
|
+
) {
|
|
616
|
+
// Note that this can throw.
|
|
617
|
+
const logFactory = buildLogFactory({
|
|
618
|
+
chainId: network.chainId,
|
|
619
|
+
...resolvedAddress,
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
return [
|
|
623
|
+
{
|
|
624
|
+
type: "account",
|
|
625
|
+
name: source.name,
|
|
626
|
+
network,
|
|
627
|
+
filter: {
|
|
628
|
+
type: "transaction",
|
|
629
|
+
chainId: network.chainId,
|
|
630
|
+
fromAddress: undefined,
|
|
631
|
+
toAddress: logFactory,
|
|
632
|
+
includeReverted: false,
|
|
633
|
+
fromBlock,
|
|
634
|
+
toBlock,
|
|
635
|
+
include: defaultTransactionFilterInclude,
|
|
636
|
+
},
|
|
637
|
+
} satisfies AccountSource,
|
|
638
|
+
{
|
|
639
|
+
type: "account",
|
|
640
|
+
name: source.name,
|
|
641
|
+
network,
|
|
642
|
+
filter: {
|
|
643
|
+
type: "transaction",
|
|
644
|
+
chainId: network.chainId,
|
|
645
|
+
fromAddress: logFactory,
|
|
646
|
+
toAddress: undefined,
|
|
647
|
+
includeReverted: false,
|
|
648
|
+
fromBlock,
|
|
649
|
+
toBlock,
|
|
650
|
+
include: defaultTransactionFilterInclude,
|
|
651
|
+
},
|
|
652
|
+
} satisfies AccountSource,
|
|
653
|
+
{
|
|
654
|
+
type: "account",
|
|
655
|
+
name: source.name,
|
|
656
|
+
network,
|
|
657
|
+
filter: {
|
|
658
|
+
type: "transfer",
|
|
659
|
+
chainId: network.chainId,
|
|
660
|
+
fromAddress: undefined,
|
|
661
|
+
toAddress: logFactory,
|
|
662
|
+
includeReverted: false,
|
|
663
|
+
fromBlock,
|
|
664
|
+
toBlock,
|
|
665
|
+
include: defaultTransferFilterInclude.concat(
|
|
666
|
+
source.includeTransactionReceipts
|
|
667
|
+
? defaultTransactionReceiptInclude
|
|
668
|
+
: [],
|
|
669
|
+
),
|
|
670
|
+
},
|
|
671
|
+
} satisfies AccountSource,
|
|
672
|
+
{
|
|
673
|
+
type: "account",
|
|
674
|
+
name: source.name,
|
|
675
|
+
network,
|
|
676
|
+
filter: {
|
|
677
|
+
type: "transfer",
|
|
678
|
+
chainId: network.chainId,
|
|
679
|
+
fromAddress: logFactory,
|
|
680
|
+
toAddress: undefined,
|
|
681
|
+
includeReverted: false,
|
|
682
|
+
fromBlock,
|
|
683
|
+
toBlock,
|
|
684
|
+
include: defaultTransferFilterInclude.concat(
|
|
685
|
+
source.includeTransactionReceipts
|
|
686
|
+
? defaultTransactionReceiptInclude
|
|
687
|
+
: [],
|
|
688
|
+
),
|
|
689
|
+
},
|
|
690
|
+
} satisfies AccountSource,
|
|
691
|
+
];
|
|
692
|
+
}
|
|
384
693
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
network,
|
|
401
|
-
} as const;
|
|
402
|
-
|
|
403
|
-
const resolvedAddress = source?.address;
|
|
694
|
+
for (const address of Array.isArray(resolvedAddress)
|
|
695
|
+
? resolvedAddress
|
|
696
|
+
: [resolvedAddress]) {
|
|
697
|
+
if (!address!.startsWith("0x"))
|
|
698
|
+
throw new Error(
|
|
699
|
+
`Validation failed: Invalid prefix for address '${address}'. Got '${address!.slice(
|
|
700
|
+
0,
|
|
701
|
+
2,
|
|
702
|
+
)}', expected '0x'.`,
|
|
703
|
+
);
|
|
704
|
+
if (address!.length !== 42)
|
|
705
|
+
throw new Error(
|
|
706
|
+
`Validation failed: Invalid length for address '${address}'. Got ${address!.length}, expected 42 characters.`,
|
|
707
|
+
);
|
|
708
|
+
}
|
|
404
709
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const logFactory = buildLogFactory({
|
|
411
|
-
chainId: network.chainId,
|
|
412
|
-
...resolvedAddress,
|
|
413
|
-
});
|
|
710
|
+
const validatedAddress = Array.isArray(resolvedAddress)
|
|
711
|
+
? dedupe(resolvedAddress).map((r) => toLowerCase(r))
|
|
712
|
+
: resolvedAddress !== undefined
|
|
713
|
+
? toLowerCase(resolvedAddress)
|
|
714
|
+
: undefined;
|
|
414
715
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
716
|
+
return [
|
|
717
|
+
{
|
|
718
|
+
type: "account",
|
|
719
|
+
name: source.name,
|
|
720
|
+
network,
|
|
419
721
|
filter: {
|
|
420
|
-
type: "
|
|
722
|
+
type: "transaction",
|
|
421
723
|
chainId: network.chainId,
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
topic2: topics.topic2,
|
|
426
|
-
topic3: topics.topic3,
|
|
724
|
+
fromAddress: undefined,
|
|
725
|
+
toAddress: validatedAddress,
|
|
726
|
+
includeReverted: false,
|
|
427
727
|
fromBlock,
|
|
428
728
|
toBlock,
|
|
429
|
-
include:
|
|
729
|
+
include: defaultTransactionFilterInclude,
|
|
730
|
+
},
|
|
731
|
+
} satisfies AccountSource,
|
|
732
|
+
{
|
|
733
|
+
type: "account",
|
|
734
|
+
name: source.name,
|
|
735
|
+
network,
|
|
736
|
+
filter: {
|
|
737
|
+
type: "transaction",
|
|
738
|
+
chainId: network.chainId,
|
|
739
|
+
fromAddress: validatedAddress,
|
|
740
|
+
toAddress: undefined,
|
|
741
|
+
includeReverted: false,
|
|
742
|
+
fromBlock,
|
|
743
|
+
toBlock,
|
|
744
|
+
include: defaultTransactionFilterInclude,
|
|
745
|
+
},
|
|
746
|
+
} satisfies AccountSource,
|
|
747
|
+
{
|
|
748
|
+
type: "account",
|
|
749
|
+
name: source.name,
|
|
750
|
+
network,
|
|
751
|
+
filter: {
|
|
752
|
+
type: "transfer",
|
|
753
|
+
chainId: network.chainId,
|
|
754
|
+
fromAddress: undefined,
|
|
755
|
+
toAddress: validatedAddress,
|
|
756
|
+
includeReverted: false,
|
|
757
|
+
fromBlock,
|
|
758
|
+
toBlock,
|
|
759
|
+
include: defaultTransferFilterInclude.concat(
|
|
430
760
|
source.includeTransactionReceipts
|
|
431
761
|
? defaultTransactionReceiptInclude
|
|
432
762
|
: [],
|
|
433
763
|
),
|
|
434
764
|
},
|
|
435
|
-
}
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
if (source.includeCallTraces) {
|
|
439
|
-
return [
|
|
440
|
-
...logSources,
|
|
765
|
+
} satisfies AccountSource,
|
|
441
766
|
{
|
|
442
|
-
|
|
767
|
+
type: "account",
|
|
768
|
+
name: source.name,
|
|
769
|
+
network,
|
|
443
770
|
filter: {
|
|
444
|
-
type: "
|
|
771
|
+
type: "transfer",
|
|
445
772
|
chainId: network.chainId,
|
|
446
|
-
fromAddress:
|
|
447
|
-
toAddress:
|
|
448
|
-
callType: "CALL",
|
|
449
|
-
functionSelector: registeredFunctionSelectors,
|
|
773
|
+
fromAddress: validatedAddress,
|
|
774
|
+
toAddress: undefined,
|
|
450
775
|
includeReverted: false,
|
|
451
776
|
fromBlock,
|
|
452
777
|
toBlock,
|
|
453
|
-
include:
|
|
778
|
+
include: defaultTransferFilterInclude.concat(
|
|
454
779
|
source.includeTransactionReceipts
|
|
455
780
|
? defaultTransactionReceiptInclude
|
|
456
781
|
: [],
|
|
457
782
|
),
|
|
458
783
|
},
|
|
459
|
-
} satisfies
|
|
784
|
+
} satisfies AccountSource,
|
|
460
785
|
];
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
? resolvedAddress
|
|
467
|
-
: [resolvedAddress]) {
|
|
468
|
-
if (!address!.startsWith("0x"))
|
|
469
|
-
throw new Error(
|
|
470
|
-
`Validation failed: Invalid prefix for address '${address}'. Got '${address!.slice(
|
|
471
|
-
0,
|
|
472
|
-
2,
|
|
473
|
-
)}', expected '0x'.`,
|
|
474
|
-
);
|
|
475
|
-
if (address!.length !== 42)
|
|
476
|
-
throw new Error(
|
|
477
|
-
`Validation failed: Invalid length for address '${address}'. Got ${address!.length}, expected 42 characters.`,
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
const validatedAddress = Array.isArray(resolvedAddress)
|
|
483
|
-
? dedupe(resolvedAddress).map((r) => toLowerCase(r))
|
|
484
|
-
: resolvedAddress !== undefined
|
|
485
|
-
? toLowerCase(resolvedAddress)
|
|
486
|
-
: undefined;
|
|
487
|
-
|
|
488
|
-
const logSources = topicsArray.map(
|
|
489
|
-
(topics) =>
|
|
490
|
-
({
|
|
491
|
-
...contractMetadata,
|
|
492
|
-
filter: {
|
|
493
|
-
type: "log",
|
|
494
|
-
chainId: network.chainId,
|
|
495
|
-
address: validatedAddress,
|
|
496
|
-
topic0: topics.topic0,
|
|
497
|
-
topic1: topics.topic1,
|
|
498
|
-
topic2: topics.topic2,
|
|
499
|
-
topic3: topics.topic3,
|
|
500
|
-
fromBlock,
|
|
501
|
-
toBlock,
|
|
502
|
-
include: defaultLogFilterInclude.concat(
|
|
503
|
-
source.includeTransactionReceipts
|
|
504
|
-
? defaultTransactionReceiptInclude
|
|
505
|
-
: [],
|
|
506
|
-
),
|
|
507
|
-
},
|
|
508
|
-
}) satisfies ContractSource,
|
|
509
|
-
);
|
|
510
|
-
|
|
511
|
-
if (source.includeCallTraces) {
|
|
512
|
-
return [
|
|
513
|
-
...logSources,
|
|
514
|
-
{
|
|
515
|
-
...contractMetadata,
|
|
516
|
-
filter: {
|
|
517
|
-
type: "trace",
|
|
518
|
-
chainId: network.chainId,
|
|
519
|
-
fromAddress: undefined,
|
|
520
|
-
toAddress: Array.isArray(validatedAddress)
|
|
521
|
-
? validatedAddress
|
|
522
|
-
: validatedAddress === undefined
|
|
523
|
-
? undefined
|
|
524
|
-
: [validatedAddress],
|
|
525
|
-
callType: "CALL",
|
|
526
|
-
functionSelector: registeredFunctionSelectors,
|
|
527
|
-
includeReverted: false,
|
|
528
|
-
fromBlock,
|
|
529
|
-
toBlock,
|
|
530
|
-
include: defaultTraceFilterInclude.concat(
|
|
531
|
-
source.includeTransactionReceipts
|
|
532
|
-
? defaultTransactionReceiptInclude
|
|
533
|
-
: [],
|
|
534
|
-
),
|
|
535
|
-
},
|
|
536
|
-
} satisfies ContractSource,
|
|
537
|
-
];
|
|
538
|
-
} else return logSources;
|
|
539
|
-
}) // Remove sources with no registered indexing functions
|
|
540
|
-
.filter((source) => {
|
|
541
|
-
const hasNoRegisteredIndexingFunctions =
|
|
542
|
-
source.filter.type === "trace"
|
|
543
|
-
? Array.isArray(source.filter.functionSelector) &&
|
|
544
|
-
source.filter.functionSelector.length === 0
|
|
545
|
-
: Array.isArray(source.filter.topic0) &&
|
|
546
|
-
source.filter.topic0?.length === 0;
|
|
547
|
-
if (hasNoRegisteredIndexingFunctions) {
|
|
548
|
-
logs.push({
|
|
549
|
-
level: "debug",
|
|
550
|
-
msg: `No indexing functions were registered for '${
|
|
551
|
-
source.name
|
|
552
|
-
}' ${source.filter.type === "trace" ? "traces" : "logs"}`,
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
return hasNoRegisteredIndexingFunctions === false;
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
const accountSources: AccountSource[] = flattenSources(config.accounts ?? {})
|
|
559
|
-
.flatMap((source): AccountSource[] => {
|
|
560
|
-
const network = networks.find((n) => n.name === source.network)!;
|
|
561
|
-
|
|
562
|
-
const startBlockMaybeNan = source.startBlock;
|
|
563
|
-
const fromBlock = Number.isNaN(startBlockMaybeNan)
|
|
564
|
-
? undefined
|
|
565
|
-
: startBlockMaybeNan;
|
|
566
|
-
const endBlockMaybeNan = source.endBlock;
|
|
567
|
-
const toBlock = Number.isNaN(endBlockMaybeNan)
|
|
568
|
-
? undefined
|
|
569
|
-
: endBlockMaybeNan;
|
|
570
|
-
|
|
571
|
-
const resolvedAddress = source?.address;
|
|
572
|
-
|
|
573
|
-
if (resolvedAddress === undefined) {
|
|
574
|
-
throw new Error(
|
|
575
|
-
`Validation failed: Account '${source.name}' must specify an 'address'.`,
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
if (
|
|
580
|
-
typeof resolvedAddress === "object" &&
|
|
581
|
-
!Array.isArray(resolvedAddress)
|
|
582
|
-
) {
|
|
583
|
-
// Note that this can throw.
|
|
584
|
-
const logFactory = buildLogFactory({
|
|
585
|
-
chainId: network.chainId,
|
|
586
|
-
...resolvedAddress,
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
return [
|
|
590
|
-
{
|
|
591
|
-
type: "account",
|
|
592
|
-
name: source.name,
|
|
593
|
-
network,
|
|
594
|
-
filter: {
|
|
595
|
-
type: "transaction",
|
|
596
|
-
chainId: network.chainId,
|
|
597
|
-
fromAddress: undefined,
|
|
598
|
-
toAddress: logFactory,
|
|
599
|
-
includeReverted: false,
|
|
600
|
-
fromBlock,
|
|
601
|
-
toBlock,
|
|
602
|
-
include: defaultTransactionFilterInclude,
|
|
603
|
-
},
|
|
604
|
-
} satisfies AccountSource,
|
|
605
|
-
{
|
|
606
|
-
type: "account",
|
|
607
|
-
name: source.name,
|
|
608
|
-
network,
|
|
609
|
-
filter: {
|
|
610
|
-
type: "transaction",
|
|
611
|
-
chainId: network.chainId,
|
|
612
|
-
fromAddress: logFactory,
|
|
613
|
-
toAddress: undefined,
|
|
614
|
-
includeReverted: false,
|
|
615
|
-
fromBlock,
|
|
616
|
-
toBlock,
|
|
617
|
-
include: defaultTransactionFilterInclude,
|
|
618
|
-
},
|
|
619
|
-
} satisfies AccountSource,
|
|
620
|
-
{
|
|
621
|
-
type: "account",
|
|
622
|
-
name: source.name,
|
|
623
|
-
network,
|
|
624
|
-
filter: {
|
|
625
|
-
type: "transfer",
|
|
626
|
-
chainId: network.chainId,
|
|
627
|
-
fromAddress: undefined,
|
|
628
|
-
toAddress: logFactory,
|
|
629
|
-
includeReverted: false,
|
|
630
|
-
fromBlock,
|
|
631
|
-
toBlock,
|
|
632
|
-
include: defaultTransferFilterInclude.concat(
|
|
633
|
-
source.includeTransactionReceipts
|
|
634
|
-
? defaultTransactionReceiptInclude
|
|
635
|
-
: [],
|
|
636
|
-
),
|
|
637
|
-
},
|
|
638
|
-
} satisfies AccountSource,
|
|
639
|
-
{
|
|
640
|
-
type: "account",
|
|
641
|
-
name: source.name,
|
|
642
|
-
network,
|
|
643
|
-
filter: {
|
|
644
|
-
type: "transfer",
|
|
645
|
-
chainId: network.chainId,
|
|
646
|
-
fromAddress: logFactory,
|
|
647
|
-
toAddress: undefined,
|
|
648
|
-
includeReverted: false,
|
|
649
|
-
fromBlock,
|
|
650
|
-
toBlock,
|
|
651
|
-
include: defaultTransferFilterInclude.concat(
|
|
652
|
-
source.includeTransactionReceipts
|
|
653
|
-
? defaultTransactionReceiptInclude
|
|
654
|
-
: [],
|
|
655
|
-
),
|
|
656
|
-
},
|
|
657
|
-
} satisfies AccountSource,
|
|
658
|
-
];
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
for (const address of Array.isArray(resolvedAddress)
|
|
662
|
-
? resolvedAddress
|
|
663
|
-
: [resolvedAddress]) {
|
|
664
|
-
if (!address!.startsWith("0x"))
|
|
665
|
-
throw new Error(
|
|
666
|
-
`Validation failed: Invalid prefix for address '${address}'. Got '${address!.slice(
|
|
667
|
-
0,
|
|
668
|
-
2,
|
|
669
|
-
)}', expected '0x'.`,
|
|
670
|
-
);
|
|
671
|
-
if (address!.length !== 42)
|
|
672
|
-
throw new Error(
|
|
673
|
-
`Validation failed: Invalid length for address '${address}'. Got ${address!.length}, expected 42 characters.`,
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
const validatedAddress = Array.isArray(resolvedAddress)
|
|
678
|
-
? dedupe(resolvedAddress).map((r) => toLowerCase(r))
|
|
679
|
-
: resolvedAddress !== undefined
|
|
680
|
-
? toLowerCase(resolvedAddress)
|
|
681
|
-
: undefined;
|
|
682
|
-
|
|
683
|
-
return [
|
|
684
|
-
{
|
|
685
|
-
type: "account",
|
|
686
|
-
name: source.name,
|
|
687
|
-
network,
|
|
688
|
-
filter: {
|
|
689
|
-
type: "transaction",
|
|
690
|
-
chainId: network.chainId,
|
|
691
|
-
fromAddress: undefined,
|
|
692
|
-
toAddress: validatedAddress,
|
|
693
|
-
includeReverted: false,
|
|
694
|
-
fromBlock,
|
|
695
|
-
toBlock,
|
|
696
|
-
include: defaultTransactionFilterInclude,
|
|
697
|
-
},
|
|
698
|
-
} satisfies AccountSource,
|
|
699
|
-
{
|
|
700
|
-
type: "account",
|
|
701
|
-
name: source.name,
|
|
702
|
-
network,
|
|
703
|
-
filter: {
|
|
704
|
-
type: "transaction",
|
|
705
|
-
chainId: network.chainId,
|
|
706
|
-
fromAddress: validatedAddress,
|
|
707
|
-
toAddress: undefined,
|
|
708
|
-
includeReverted: false,
|
|
709
|
-
fromBlock,
|
|
710
|
-
toBlock,
|
|
711
|
-
include: defaultTransactionFilterInclude,
|
|
712
|
-
},
|
|
713
|
-
} satisfies AccountSource,
|
|
714
|
-
{
|
|
715
|
-
type: "account",
|
|
716
|
-
name: source.name,
|
|
717
|
-
network,
|
|
718
|
-
filter: {
|
|
719
|
-
type: "transfer",
|
|
720
|
-
chainId: network.chainId,
|
|
721
|
-
fromAddress: undefined,
|
|
722
|
-
toAddress: validatedAddress,
|
|
723
|
-
includeReverted: false,
|
|
724
|
-
fromBlock,
|
|
725
|
-
toBlock,
|
|
726
|
-
include: defaultTransferFilterInclude.concat(
|
|
727
|
-
source.includeTransactionReceipts
|
|
728
|
-
? defaultTransactionReceiptInclude
|
|
729
|
-
: [],
|
|
730
|
-
),
|
|
731
|
-
},
|
|
732
|
-
} satisfies AccountSource,
|
|
733
|
-
{
|
|
734
|
-
type: "account",
|
|
735
|
-
name: source.name,
|
|
736
|
-
network,
|
|
737
|
-
filter: {
|
|
738
|
-
type: "transfer",
|
|
739
|
-
chainId: network.chainId,
|
|
740
|
-
fromAddress: validatedAddress,
|
|
741
|
-
toAddress: undefined,
|
|
742
|
-
includeReverted: false,
|
|
743
|
-
fromBlock,
|
|
744
|
-
toBlock,
|
|
745
|
-
include: defaultTransferFilterInclude.concat(
|
|
746
|
-
source.includeTransactionReceipts
|
|
747
|
-
? defaultTransactionReceiptInclude
|
|
748
|
-
: [],
|
|
749
|
-
),
|
|
750
|
-
},
|
|
751
|
-
} satisfies AccountSource,
|
|
752
|
-
];
|
|
753
|
-
})
|
|
786
|
+
},
|
|
787
|
+
),
|
|
788
|
+
)
|
|
789
|
+
)
|
|
790
|
+
.flat()
|
|
754
791
|
.filter((source) => {
|
|
755
792
|
const eventName =
|
|
756
793
|
source.filter.type === "transaction"
|
|
@@ -772,43 +809,41 @@ export async function buildConfigAndIndexingFunctions({
|
|
|
772
809
|
return hasRegisteredIndexingFunction;
|
|
773
810
|
});
|
|
774
811
|
|
|
775
|
-
const blockSources: BlockSource[] =
|
|
776
|
-
.
|
|
777
|
-
|
|
812
|
+
const blockSources: BlockSource[] = (
|
|
813
|
+
await Promise.all(
|
|
814
|
+
flattenSources(config.blocks ?? {}).map(async (source) => {
|
|
815
|
+
const network = networks.find((n) => n.name === source.network)!;
|
|
778
816
|
|
|
779
|
-
|
|
780
|
-
|
|
817
|
+
const intervalMaybeNan = source.interval ?? 1;
|
|
818
|
+
const interval = Number.isNaN(intervalMaybeNan) ? 0 : intervalMaybeNan;
|
|
781
819
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
820
|
+
if (!Number.isInteger(interval) || interval === 0) {
|
|
821
|
+
throw new Error(
|
|
822
|
+
`Validation failed: Invalid interval for block source '${source.name}'. Got ${interval}, expected a non-zero integer.`,
|
|
823
|
+
);
|
|
824
|
+
}
|
|
787
825
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
? undefined
|
|
791
|
-
: startBlockMaybeNan;
|
|
792
|
-
const endBlockMaybeNan = source.endBlock;
|
|
793
|
-
const toBlock = Number.isNaN(endBlockMaybeNan)
|
|
794
|
-
? undefined
|
|
795
|
-
: endBlockMaybeNan;
|
|
826
|
+
const fromBlock = await resolveBlockNumber(source.startBlock, network);
|
|
827
|
+
const toBlock = await resolveBlockNumber(source.endBlock, network);
|
|
796
828
|
|
|
797
|
-
|
|
798
|
-
type: "block",
|
|
799
|
-
name: source.name,
|
|
800
|
-
network,
|
|
801
|
-
filter: {
|
|
829
|
+
return {
|
|
802
830
|
type: "block",
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
831
|
+
name: source.name,
|
|
832
|
+
network,
|
|
833
|
+
filter: {
|
|
834
|
+
type: "block",
|
|
835
|
+
chainId: network.chainId,
|
|
836
|
+
interval: interval,
|
|
837
|
+
offset: (fromBlock ?? 0) % interval,
|
|
838
|
+
fromBlock,
|
|
839
|
+
toBlock,
|
|
840
|
+
include: defaultBlockFilterInclude,
|
|
841
|
+
},
|
|
842
|
+
} satisfies BlockSource;
|
|
843
|
+
}),
|
|
844
|
+
)
|
|
845
|
+
)
|
|
846
|
+
.flat()
|
|
812
847
|
.filter((blockSource) => {
|
|
813
848
|
const hasRegisteredIndexingFunction =
|
|
814
849
|
indexingFunctions[`${blockSource.name}:block`] !== undefined;
|