ponder 0.17.11 → 0.18.0
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/CHANGELOG.md +30 -0
- package/dist/esm/bin/commands/codegen.js +3 -3
- package/dist/esm/bin/commands/codegen.js.map +1 -1
- package/dist/esm/bin/commands/dev.js +3 -3
- package/dist/esm/bin/commands/dev.js.map +1 -1
- package/dist/esm/bin/commands/serve.js +3 -3
- package/dist/esm/bin/commands/serve.js.map +1 -1
- package/dist/esm/bin/commands/start.js +3 -3
- package/dist/esm/bin/commands/start.js.map +1 -1
- package/dist/esm/build/config.js +19 -20
- package/dist/esm/build/config.js.map +1 -1
- package/dist/esm/build/index.js +79 -65
- package/dist/esm/build/index.js.map +1 -1
- package/dist/esm/build/plugin.js +9 -0
- package/dist/esm/build/plugin.js.map +1 -1
- package/dist/esm/build/stacktrace.js +25 -68
- package/dist/esm/build/stacktrace.js.map +1 -1
- package/dist/esm/database/index.js +83 -67
- package/dist/esm/database/index.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexing/client.js +35 -23
- package/dist/esm/indexing/client.js.map +1 -1
- package/dist/esm/indexing/index.js +3 -1
- package/dist/esm/indexing/index.js.map +1 -1
- package/dist/esm/indexing/profile.js +4 -3
- package/dist/esm/indexing/profile.js.map +1 -1
- package/dist/esm/indexing-store/index.js +2 -2
- package/dist/esm/indexing-store/index.js.map +1 -1
- package/dist/esm/rpc/actions.js +143 -67
- package/dist/esm/rpc/actions.js.map +1 -1
- package/dist/esm/runtime/events.js +33 -19
- package/dist/esm/runtime/events.js.map +1 -1
- package/dist/esm/runtime/filter.js +10 -9
- package/dist/esm/runtime/filter.js.map +1 -1
- package/dist/esm/runtime/fragments.js +50 -24
- package/dist/esm/runtime/fragments.js.map +1 -1
- package/dist/esm/runtime/historical.js +212 -68
- package/dist/esm/runtime/historical.js.map +1 -1
- package/dist/esm/runtime/index.js +11 -15
- package/dist/esm/runtime/index.js.map +1 -1
- package/dist/esm/runtime/isolated.js +1 -0
- package/dist/esm/runtime/isolated.js.map +1 -1
- package/dist/esm/runtime/multichain.js +1 -0
- package/dist/esm/runtime/multichain.js.map +1 -1
- package/dist/esm/runtime/omnichain.js +1 -0
- package/dist/esm/runtime/omnichain.js.map +1 -1
- package/dist/esm/runtime/realtime.js +17 -16
- package/dist/esm/runtime/realtime.js.map +1 -1
- package/dist/esm/server/index.js +8 -6
- package/dist/esm/server/index.js.map +1 -1
- package/dist/esm/sync-historical/in-memory.js +539 -0
- package/dist/esm/sync-historical/in-memory.js.map +1 -0
- package/dist/esm/sync-historical/index.js +46 -63
- package/dist/esm/sync-historical/index.js.map +1 -1
- package/dist/esm/sync-realtime/index.js +56 -83
- package/dist/esm/sync-realtime/index.js.map +1 -1
- package/dist/esm/sync-store/encode.js +1 -6
- package/dist/esm/sync-store/encode.js.map +1 -1
- package/dist/esm/sync-store/index.js +58 -62
- package/dist/esm/sync-store/index.js.map +1 -1
- package/dist/esm/sync-store/schema.js +23 -15
- package/dist/esm/sync-store/schema.js.map +1 -1
- package/dist/esm/sync-store/sql/ponder_rpc_cache_1/copy.sql +23 -0
- package/dist/esm/sync-store/sql/ponder_rpc_cache_1/create.sql +130 -0
- package/dist/esm/utils/copy.js +4 -4
- package/dist/esm/utils/copy.js.map +1 -1
- package/dist/esm/utils/finality.js +41 -32
- package/dist/esm/utils/finality.js.map +1 -1
- package/dist/esm/utils/pglite.js +0 -65
- package/dist/esm/utils/pglite.js.map +1 -1
- package/dist/types/bin/commands/codegen.d.ts.map +1 -1
- package/dist/types/bin/commands/dev.d.ts.map +1 -1
- package/dist/types/bin/commands/serve.d.ts.map +1 -1
- package/dist/types/bin/commands/start.d.ts.map +1 -1
- package/dist/types/build/config.d.ts.map +1 -1
- package/dist/types/build/index.d.ts.map +1 -1
- package/dist/types/build/plugin.d.ts.map +1 -1
- package/dist/types/build/stacktrace.d.ts +1 -12
- package/dist/types/build/stacktrace.d.ts.map +1 -1
- package/dist/types/config/index.d.ts +12 -2
- package/dist/types/config/index.d.ts.map +1 -1
- package/dist/types/database/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/indexing/client.d.ts.map +1 -1
- package/dist/types/indexing/profile.d.ts.map +1 -1
- package/dist/types/indexing-store/index.d.ts.map +1 -1
- package/dist/types/internal/types.d.ts +8 -13
- package/dist/types/internal/types.d.ts.map +1 -1
- package/dist/types/rpc/actions.d.ts +30 -0
- package/dist/types/rpc/actions.d.ts.map +1 -1
- package/dist/types/runtime/events.d.ts.map +1 -1
- package/dist/types/runtime/filter.d.ts.map +1 -1
- package/dist/types/runtime/fragments.d.ts +9 -1
- package/dist/types/runtime/fragments.d.ts.map +1 -1
- package/dist/types/runtime/historical.d.ts +18 -1
- package/dist/types/runtime/historical.d.ts.map +1 -1
- package/dist/types/runtime/index.d.ts +1 -0
- package/dist/types/runtime/index.d.ts.map +1 -1
- package/dist/types/runtime/isolated.d.ts.map +1 -1
- package/dist/types/runtime/multichain.d.ts.map +1 -1
- package/dist/types/runtime/omnichain.d.ts.map +1 -1
- package/dist/types/runtime/realtime.d.ts.map +1 -1
- package/dist/types/server/index.d.ts.map +1 -1
- package/dist/types/sync-historical/in-memory.d.ts +24 -0
- package/dist/types/sync-historical/in-memory.d.ts.map +1 -0
- package/dist/types/sync-historical/index.d.ts.map +1 -1
- package/dist/types/sync-realtime/index.d.ts +3 -2
- package/dist/types/sync-realtime/index.d.ts.map +1 -1
- package/dist/types/sync-store/encode.d.ts.map +1 -1
- package/dist/types/sync-store/index.d.ts +3 -3
- package/dist/types/sync-store/index.d.ts.map +1 -1
- package/dist/types/sync-store/schema.d.ts +63 -98
- package/dist/types/sync-store/schema.d.ts.map +1 -1
- package/dist/types/types/eth.d.ts +2 -8
- package/dist/types/types/eth.d.ts.map +1 -1
- package/dist/types/utils/finality.d.ts +12 -9
- package/dist/types/utils/finality.d.ts.map +1 -1
- package/dist/types/utils/pglite.d.ts +0 -18
- package/dist/types/utils/pglite.d.ts.map +1 -1
- package/package.json +5 -11
- package/src/bin/commands/codegen.ts +3 -3
- package/src/bin/commands/dev.ts +3 -3
- package/src/bin/commands/serve.ts +3 -3
- package/src/bin/commands/start.ts +3 -3
- package/src/build/config.ts +29 -28
- package/src/build/index.ts +88 -71
- package/src/build/plugin.ts +6 -0
- package/src/build/stacktrace.ts +38 -65
- package/src/config/index.ts +12 -2
- package/src/database/index.ts +132 -89
- package/src/index.ts +5 -0
- package/src/indexing/client.ts +56 -40
- package/src/indexing/index.ts +4 -1
- package/src/indexing/profile.ts +5 -2
- package/src/indexing-store/index.ts +2 -8
- package/src/internal/types.ts +10 -13
- package/src/rpc/actions.ts +211 -77
- package/src/runtime/events.ts +40 -19
- package/src/runtime/filter.ts +11 -11
- package/src/runtime/fragments.ts +70 -26
- package/src/runtime/historical.ts +314 -97
- package/src/runtime/index.ts +27 -14
- package/src/runtime/isolated.ts +1 -0
- package/src/runtime/multichain.ts +1 -0
- package/src/runtime/omnichain.ts +1 -0
- package/src/runtime/realtime.ts +24 -25
- package/src/server/index.ts +14 -7
- package/src/sync-historical/in-memory.ts +834 -0
- package/src/sync-historical/index.ts +76 -120
- package/src/sync-realtime/index.ts +73 -133
- package/src/sync-store/encode.ts +1 -6
- package/src/sync-store/index.ts +90 -95
- package/src/sync-store/schema.ts +24 -27
- package/src/types/eth.ts +2 -8
- package/src/utils/copy.ts +3 -3
- package/src/utils/finality.ts +67 -34
- package/src/utils/pglite.ts +0 -79
- package/dist/esm/sync-store/migrations.js +0 -1595
- package/dist/esm/sync-store/migrations.js.map +0 -1
- package/dist/types/sync-store/migrations.d.ts +0 -8
- package/dist/types/sync-store/migrations.d.ts.map +0 -1
- package/src/sync-store/migrations.ts +0 -1801
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Address,
|
|
3
|
+
type Hash,
|
|
4
|
+
hexToNumber,
|
|
5
|
+
numberToHex,
|
|
6
|
+
toHex,
|
|
7
|
+
zeroHash,
|
|
8
|
+
} from "viem";
|
|
9
|
+
import type { Common } from "@/internal/common.js";
|
|
10
|
+
import type {
|
|
11
|
+
Chain,
|
|
12
|
+
Factory,
|
|
13
|
+
Filter,
|
|
14
|
+
SyncBlock,
|
|
15
|
+
SyncLog,
|
|
16
|
+
SyncTrace,
|
|
17
|
+
SyncTransaction,
|
|
18
|
+
} from "@/internal/types.js";
|
|
19
|
+
import {
|
|
20
|
+
debug_traceBlockByNumber,
|
|
21
|
+
eth_getBlockByNumber,
|
|
22
|
+
eth_getLogsWithPagination,
|
|
23
|
+
eth_getTransactionReceipts,
|
|
24
|
+
validateLogsAndBlock,
|
|
25
|
+
validateReceiptsAndBlock,
|
|
26
|
+
validateTracesAndBlock,
|
|
27
|
+
validateTransactionsAndBlock,
|
|
28
|
+
} from "@/rpc/actions.js";
|
|
29
|
+
import { type Rpc, sanitizeLogTopics } from "@/rpc/index.js";
|
|
30
|
+
import {
|
|
31
|
+
syncBlockToInternal,
|
|
32
|
+
syncLogToInternal,
|
|
33
|
+
syncTraceToInternal,
|
|
34
|
+
syncTransactionReceiptToInternal,
|
|
35
|
+
syncTransactionToInternal,
|
|
36
|
+
} from "@/runtime/events.js";
|
|
37
|
+
import {
|
|
38
|
+
getChildAddress,
|
|
39
|
+
getFilterFactories,
|
|
40
|
+
isAddressFactory,
|
|
41
|
+
isAddressMatched,
|
|
42
|
+
isBlockFilterMatched,
|
|
43
|
+
isBlockInFilter,
|
|
44
|
+
isLogFactoryMatched,
|
|
45
|
+
isLogFilterMatched,
|
|
46
|
+
isTraceFilterMatched,
|
|
47
|
+
isTransactionFilterMatched,
|
|
48
|
+
isTransferFilterMatched,
|
|
49
|
+
} from "@/runtime/filter.js";
|
|
50
|
+
import type {
|
|
51
|
+
ChildAddresses,
|
|
52
|
+
IntervalWithFactory,
|
|
53
|
+
IntervalWithFilter,
|
|
54
|
+
} from "@/runtime/index.js";
|
|
55
|
+
import type { SyncStore } from "@/sync-store/index.js";
|
|
56
|
+
import { isAsyncExecutionChain } from "@/utils/finality.js";
|
|
57
|
+
import { drainAsyncGenerator } from "@/utils/generators.js";
|
|
58
|
+
import { type Interval, intervalBounds } from "@/utils/interval.js";
|
|
59
|
+
import { startClock } from "@/utils/timer.js";
|
|
60
|
+
|
|
61
|
+
type BlockData = Awaited<ReturnType<SyncStore["getEventData"]>>;
|
|
62
|
+
|
|
63
|
+
export type InMemoryHistoricalSync = {
|
|
64
|
+
syncBlockData(params: {
|
|
65
|
+
requiredIntervals: IntervalWithFilter[];
|
|
66
|
+
requiredFactoryIntervals: IntervalWithFactory[];
|
|
67
|
+
}): AsyncGenerator<BlockData>;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export function createInMemoryHistoricalSync(params: {
|
|
71
|
+
common: Common;
|
|
72
|
+
chain: Chain;
|
|
73
|
+
rpc: Rpc;
|
|
74
|
+
childAddress: ChildAddresses;
|
|
75
|
+
}): InMemoryHistoricalSync {
|
|
76
|
+
return {
|
|
77
|
+
async *syncBlockData({ requiredIntervals, requiredFactoryIntervals }) {
|
|
78
|
+
const context = {
|
|
79
|
+
logger: params.common.logger.child({ action: "fetch_block_data" }),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const factoryIntervalsById = new Map<
|
|
83
|
+
Factory["id"],
|
|
84
|
+
IntervalWithFactory
|
|
85
|
+
>();
|
|
86
|
+
|
|
87
|
+
for (const { factory, interval } of requiredFactoryIntervals) {
|
|
88
|
+
const existing = factoryIntervalsById.get(factory.id);
|
|
89
|
+
factoryIntervalsById.set(factory.id, {
|
|
90
|
+
factory,
|
|
91
|
+
interval: existing
|
|
92
|
+
? intervalBounds([existing.interval, interval])
|
|
93
|
+
: interval,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Fetch the child addresses of `factory` in `interval`, one page for each
|
|
99
|
+
* `next()`. Yields the last block of each page.
|
|
100
|
+
*/
|
|
101
|
+
async function* fetchChildAddressesWithPagination(
|
|
102
|
+
factory: Factory,
|
|
103
|
+
interval: Interval,
|
|
104
|
+
): AsyncGenerator<number> {
|
|
105
|
+
const factoryChildAddresses = params.childAddress.get(factory.id)!;
|
|
106
|
+
|
|
107
|
+
let endClock = startClock();
|
|
108
|
+
for await (const page of eth_getLogsWithPagination(
|
|
109
|
+
params.rpc,
|
|
110
|
+
[
|
|
111
|
+
{
|
|
112
|
+
address: factory.address,
|
|
113
|
+
topics: [factory.eventSelector],
|
|
114
|
+
fromBlock: numberToHex(interval[0]),
|
|
115
|
+
toBlock: numberToHex(interval[1]),
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
{
|
|
119
|
+
...context,
|
|
120
|
+
ethGetLogsBlockRange: params.chain.ethGetLogsBlockRange,
|
|
121
|
+
},
|
|
122
|
+
)) {
|
|
123
|
+
let childAddressCount = 0;
|
|
124
|
+
|
|
125
|
+
for (const log of page.logs) {
|
|
126
|
+
if (isLogFactoryMatched({ factory, log }) === false) continue;
|
|
127
|
+
|
|
128
|
+
let address: Address;
|
|
129
|
+
try {
|
|
130
|
+
address = getChildAddress({ log, factory });
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (factory.address !== undefined) throw error;
|
|
133
|
+
params.common.logger.debug({
|
|
134
|
+
msg: "Failed to extract child address from log matched by factory using the provided ABI item",
|
|
135
|
+
chain: params.chain.name,
|
|
136
|
+
chain_id: params.chain.id,
|
|
137
|
+
factory: factory.sourceId,
|
|
138
|
+
block_number: hexToNumber(log.blockNumber),
|
|
139
|
+
log_index: hexToNumber(log.logIndex),
|
|
140
|
+
data: log.data,
|
|
141
|
+
topics: JSON.stringify(log.topics),
|
|
142
|
+
});
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const blockNumber = hexToNumber(log.blockNumber);
|
|
147
|
+
const existingBlockNumber = factoryChildAddresses.get(address);
|
|
148
|
+
if (
|
|
149
|
+
existingBlockNumber === undefined ||
|
|
150
|
+
existingBlockNumber > blockNumber
|
|
151
|
+
) {
|
|
152
|
+
factoryChildAddresses.set(address, blockNumber);
|
|
153
|
+
childAddressCount++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
params.common.logger.debug(
|
|
158
|
+
{
|
|
159
|
+
msg: "Fetched block range data",
|
|
160
|
+
chain: params.chain.name,
|
|
161
|
+
chain_id: params.chain.id,
|
|
162
|
+
data_type: "factory_log",
|
|
163
|
+
block_range: JSON.stringify([page.fromBlock, page.toBlock]),
|
|
164
|
+
log_count: page.logs.length,
|
|
165
|
+
child_address_count: childAddressCount,
|
|
166
|
+
duration: endClock(),
|
|
167
|
+
},
|
|
168
|
+
["chain", "data_type", "block_range"],
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
yield page.toBlock;
|
|
172
|
+
endClock = startClock();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Closest-to-tip block with all child addresses fetched (inclusive), for each
|
|
178
|
+
* factory. `Infinity` once all child addresses are fetched.
|
|
179
|
+
*
|
|
180
|
+
* Note: Factories are not part of `mergeGeneratorIntervals`. Instead, the filters
|
|
181
|
+
* that depend on a factory fetch its pages when they need more child addresses.
|
|
182
|
+
*/
|
|
183
|
+
const factoryProgress = new Map<Factory["id"], number>();
|
|
184
|
+
const factoryPages = new Map<Factory["id"], AsyncGenerator<number>>();
|
|
185
|
+
|
|
186
|
+
for (const { factory, interval } of factoryIntervalsById.values()) {
|
|
187
|
+
factoryProgress.set(factory.id, interval[0] - 1);
|
|
188
|
+
factoryPages.set(
|
|
189
|
+
factory.id,
|
|
190
|
+
fetchChildAddressesWithPagination(factory, interval),
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Logs keyed by block number, then by log index.
|
|
196
|
+
*/
|
|
197
|
+
const perBlockLogs = new Map<number, Map<number, SyncLog>>();
|
|
198
|
+
|
|
199
|
+
const intervalGenerators = new Map<
|
|
200
|
+
IntervalWithFilter,
|
|
201
|
+
AsyncGenerator<Interval>
|
|
202
|
+
>();
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Yield the sub-intervals of `interval` for which all `factories`
|
|
206
|
+
* have fetched child addresses. Fetches factory pages as needed.
|
|
207
|
+
*/
|
|
208
|
+
async function* paginateFactoryDependencies(
|
|
209
|
+
interval: Interval,
|
|
210
|
+
factories: Factory["id"][],
|
|
211
|
+
): AsyncGenerator<Interval> {
|
|
212
|
+
let cursor = interval[0];
|
|
213
|
+
|
|
214
|
+
while (cursor <= interval[1]) {
|
|
215
|
+
const behind = factories.filter(
|
|
216
|
+
(id) =>
|
|
217
|
+
(factoryProgress.get(id) ?? Number.POSITIVE_INFINITY) < cursor,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
if (behind.length > 0) {
|
|
221
|
+
// Note: Concurrent `next()` calls on one generator are queued, so filters
|
|
222
|
+
// that depend on the same factory do not repeat requests.
|
|
223
|
+
await Promise.all(
|
|
224
|
+
behind.map(async (id) => {
|
|
225
|
+
const result = await factoryPages.get(id)!.next();
|
|
226
|
+
factoryProgress.set(
|
|
227
|
+
id,
|
|
228
|
+
result.done ? Number.POSITIVE_INFINITY : result.value,
|
|
229
|
+
);
|
|
230
|
+
}),
|
|
231
|
+
);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const progressBlock = Math.min(
|
|
236
|
+
interval[1],
|
|
237
|
+
...factories.map(
|
|
238
|
+
(id) => factoryProgress.get(id) ?? Number.POSITIVE_INFINITY,
|
|
239
|
+
),
|
|
240
|
+
);
|
|
241
|
+
yield [cursor, progressBlock];
|
|
242
|
+
cursor = progressBlock + 1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (const requiredInterval of requiredIntervals) {
|
|
247
|
+
intervalGenerators.set(
|
|
248
|
+
requiredInterval,
|
|
249
|
+
(async function* (): AsyncGenerator<Interval> {
|
|
250
|
+
const { filter, interval } = requiredInterval;
|
|
251
|
+
|
|
252
|
+
switch (filter.type) {
|
|
253
|
+
case "block":
|
|
254
|
+
case "transaction":
|
|
255
|
+
case "trace":
|
|
256
|
+
case "transfer":
|
|
257
|
+
// Note: `syncBlock` matches factory addresses, so it requires all
|
|
258
|
+
// child addresses up to each block.
|
|
259
|
+
yield* paginateFactoryDependencies(
|
|
260
|
+
interval,
|
|
261
|
+
getFilterFactories(filter).map(({ id }) => id),
|
|
262
|
+
);
|
|
263
|
+
break;
|
|
264
|
+
case "log": {
|
|
265
|
+
const factory = isAddressFactory(filter.address)
|
|
266
|
+
? filter.address
|
|
267
|
+
: undefined;
|
|
268
|
+
|
|
269
|
+
for await (const factoryInterval of paginateFactoryDependencies(
|
|
270
|
+
interval,
|
|
271
|
+
factory ? [factory.id] : [],
|
|
272
|
+
)) {
|
|
273
|
+
let address: Address | Address[] | undefined;
|
|
274
|
+
if (factory) {
|
|
275
|
+
const childAddresses = params.childAddress.get(factory.id)!;
|
|
276
|
+
if (childAddresses.size === 0) {
|
|
277
|
+
yield factoryInterval;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
address =
|
|
282
|
+
childAddresses.size >=
|
|
283
|
+
params.common.options.factoryAddressCountThreshold
|
|
284
|
+
? undefined
|
|
285
|
+
: Array.from(childAddresses.keys());
|
|
286
|
+
} else {
|
|
287
|
+
address = filter.address as Address | Address[] | undefined;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let endClock = startClock();
|
|
291
|
+
for await (const page of eth_getLogsWithPagination(
|
|
292
|
+
params.rpc,
|
|
293
|
+
[
|
|
294
|
+
{
|
|
295
|
+
address,
|
|
296
|
+
topics: sanitizeLogTopics([
|
|
297
|
+
filter.topic0,
|
|
298
|
+
filter.topic1 ?? null,
|
|
299
|
+
filter.topic2 ?? null,
|
|
300
|
+
filter.topic3 ?? null,
|
|
301
|
+
]),
|
|
302
|
+
fromBlock: numberToHex(factoryInterval[0]),
|
|
303
|
+
toBlock: numberToHex(factoryInterval[1]),
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
{
|
|
307
|
+
...context,
|
|
308
|
+
ethGetLogsBlockRange: params.chain.ethGetLogsBlockRange,
|
|
309
|
+
},
|
|
310
|
+
)) {
|
|
311
|
+
params.common.logger.debug(
|
|
312
|
+
{
|
|
313
|
+
msg: "Fetched block range data",
|
|
314
|
+
chain: params.chain.name,
|
|
315
|
+
chain_id: params.chain.id,
|
|
316
|
+
block_range: JSON.stringify([
|
|
317
|
+
page.fromBlock,
|
|
318
|
+
page.toBlock,
|
|
319
|
+
]),
|
|
320
|
+
log_count: page.logs.length,
|
|
321
|
+
duration: endClock(),
|
|
322
|
+
},
|
|
323
|
+
["chain", "block_range"],
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
for (const log of page.logs) {
|
|
327
|
+
if (log.transactionHash === zeroHash) {
|
|
328
|
+
params.common.logger.warn({
|
|
329
|
+
msg: "Detected log with empty transaction hash. This is expected for some chains like ZKsync.",
|
|
330
|
+
action: "fetch_block_data",
|
|
331
|
+
chain: params.chain.name,
|
|
332
|
+
chain_id: params.chain.id,
|
|
333
|
+
number: hexToNumber(log.blockNumber),
|
|
334
|
+
hash: log.blockHash,
|
|
335
|
+
logIndex: hexToNumber(log.logIndex),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
const blockNumber = hexToNumber(log.blockNumber);
|
|
339
|
+
if (perBlockLogs.has(blockNumber) === false) {
|
|
340
|
+
perBlockLogs.set(blockNumber, new Map());
|
|
341
|
+
}
|
|
342
|
+
perBlockLogs
|
|
343
|
+
.get(blockNumber)!
|
|
344
|
+
.set(hexToNumber(log.logIndex), log);
|
|
345
|
+
}
|
|
346
|
+
yield [page.fromBlock, page.toBlock];
|
|
347
|
+
endClock = startClock();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
})(),
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Returns `true` if `blockNumber` has required data.
|
|
358
|
+
*/
|
|
359
|
+
const isBlockRequired = (blockNumber: number) =>
|
|
360
|
+
perBlockLogs.has(blockNumber) ||
|
|
361
|
+
requiredIntervals.some(({ filter, interval }) => {
|
|
362
|
+
if (blockNumber < interval[0] || blockNumber > interval[1]) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Note: Blocks with logs are in `perBlockLogs`.
|
|
367
|
+
if (filter.type === "log") return false;
|
|
368
|
+
if (filter.type === "block") {
|
|
369
|
+
return isBlockFilterMatched({
|
|
370
|
+
filter,
|
|
371
|
+
block: { number: BigInt(blockNumber) },
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
// Transaction, trace, and transfer filters require every block.
|
|
375
|
+
return true;
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const syncBlock = async (blockNumber: number): Promise<BlockData> => {
|
|
379
|
+
const filters = requiredIntervals
|
|
380
|
+
.filter(
|
|
381
|
+
({ interval }) =>
|
|
382
|
+
interval[0] <= blockNumber && blockNumber <= interval[1],
|
|
383
|
+
)
|
|
384
|
+
.map(({ filter }) => filter);
|
|
385
|
+
const blockFilters = filters.filter(
|
|
386
|
+
(filter): filter is Extract<Filter, { type: "block" }> =>
|
|
387
|
+
filter.type === "block",
|
|
388
|
+
);
|
|
389
|
+
const transactionFilters = filters.filter(
|
|
390
|
+
(filter): filter is Extract<Filter, { type: "transaction" }> =>
|
|
391
|
+
filter.type === "transaction",
|
|
392
|
+
);
|
|
393
|
+
const traceFilters = filters.filter(
|
|
394
|
+
(filter): filter is Extract<Filter, { type: "trace" }> =>
|
|
395
|
+
filter.type === "trace",
|
|
396
|
+
);
|
|
397
|
+
const transferFilters = filters.filter(
|
|
398
|
+
(filter): filter is Extract<Filter, { type: "transfer" }> =>
|
|
399
|
+
filter.type === "transfer",
|
|
400
|
+
);
|
|
401
|
+
const logFilters = filters.filter(
|
|
402
|
+
(filter): filter is Extract<Filter, { type: "log" }> =>
|
|
403
|
+
filter.type === "log",
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
// Note: `perBlockLogs` is keyed by log index.
|
|
407
|
+
const blockLogs = perBlockLogs.has(blockNumber)
|
|
408
|
+
? Array.from(perBlockLogs.get(blockNumber)!.entries())
|
|
409
|
+
.sort(([a], [b]) => a - b)
|
|
410
|
+
.map(([, log]) => log)
|
|
411
|
+
: undefined;
|
|
412
|
+
perBlockLogs.delete(blockNumber);
|
|
413
|
+
|
|
414
|
+
const shouldRequestTraces =
|
|
415
|
+
traceFilters.some((filter) => isBlockInFilter(filter, blockNumber)) ||
|
|
416
|
+
transferFilters.some((filter) =>
|
|
417
|
+
isBlockInFilter(filter, blockNumber),
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const endClock = startClock();
|
|
421
|
+
let block: SyncBlock | undefined;
|
|
422
|
+
|
|
423
|
+
const requiredTransactions = new Set<Hash>();
|
|
424
|
+
const requiredTransactionReceipts = new Set<Hash>();
|
|
425
|
+
|
|
426
|
+
////////
|
|
427
|
+
// Logs
|
|
428
|
+
////////
|
|
429
|
+
|
|
430
|
+
let logs: SyncLog[] = [];
|
|
431
|
+
if (blockLogs !== undefined) {
|
|
432
|
+
block = await eth_getBlockByNumber(
|
|
433
|
+
params.rpc,
|
|
434
|
+
[numberToHex(blockNumber), true],
|
|
435
|
+
context,
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
logs = blockLogs.filter((log) => {
|
|
439
|
+
let isMatched = false;
|
|
440
|
+
|
|
441
|
+
for (const filter of logFilters) {
|
|
442
|
+
if (
|
|
443
|
+
isLogFilterMatched({ filter, log }) &&
|
|
444
|
+
(isAddressFactory(filter.address)
|
|
445
|
+
? isAddressMatched({
|
|
446
|
+
address: log.address,
|
|
447
|
+
blockNumber,
|
|
448
|
+
childAddresses: params.childAddress.get(
|
|
449
|
+
filter.address.id,
|
|
450
|
+
)!,
|
|
451
|
+
})
|
|
452
|
+
: true)
|
|
453
|
+
) {
|
|
454
|
+
isMatched = true;
|
|
455
|
+
|
|
456
|
+
if (log.transactionHash !== zeroHash) {
|
|
457
|
+
requiredTransactions.add(log.transactionHash);
|
|
458
|
+
if (filter.hasTransactionReceipt) {
|
|
459
|
+
requiredTransactionReceipts.add(log.transactionHash);
|
|
460
|
+
|
|
461
|
+
// skip to next log
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return isMatched;
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
if (logs.length > 0) {
|
|
472
|
+
// Note: `logsRequest` could be more accurate by tracking the exact
|
|
473
|
+
// request made to include `address` and `topics`.
|
|
474
|
+
validateLogsAndBlock(
|
|
475
|
+
logs,
|
|
476
|
+
block,
|
|
477
|
+
{
|
|
478
|
+
method: "eth_getLogs",
|
|
479
|
+
params: [
|
|
480
|
+
{
|
|
481
|
+
fromBlock: toHex(blockNumber),
|
|
482
|
+
toBlock: toHex(blockNumber),
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
method: "eth_getBlockByNumber",
|
|
488
|
+
params: [toHex(blockNumber), true],
|
|
489
|
+
},
|
|
490
|
+
isAsyncExecutionChain(params.chain.id),
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
////////
|
|
496
|
+
// Traces
|
|
497
|
+
////////
|
|
498
|
+
|
|
499
|
+
let traces: SyncTrace[] = [];
|
|
500
|
+
if (shouldRequestTraces) {
|
|
501
|
+
if (block === undefined) {
|
|
502
|
+
[block, traces] = await Promise.all([
|
|
503
|
+
eth_getBlockByNumber(
|
|
504
|
+
params.rpc,
|
|
505
|
+
[numberToHex(blockNumber), true],
|
|
506
|
+
context,
|
|
507
|
+
),
|
|
508
|
+
debug_traceBlockByNumber(
|
|
509
|
+
params.rpc,
|
|
510
|
+
[numberToHex(blockNumber), { tracer: "callTracer" }],
|
|
511
|
+
context,
|
|
512
|
+
),
|
|
513
|
+
]);
|
|
514
|
+
} else {
|
|
515
|
+
traces = await debug_traceBlockByNumber(
|
|
516
|
+
params.rpc,
|
|
517
|
+
[numberToHex(blockNumber), { tracer: "callTracer" }],
|
|
518
|
+
context,
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
traces = traces.filter((trace) => {
|
|
523
|
+
let isMatched = false;
|
|
524
|
+
for (const filter of transferFilters) {
|
|
525
|
+
if (
|
|
526
|
+
isTransferFilterMatched({
|
|
527
|
+
filter,
|
|
528
|
+
trace: trace.trace,
|
|
529
|
+
block: { number: BigInt(blockNumber) },
|
|
530
|
+
}) &&
|
|
531
|
+
(isAddressFactory(filter.fromAddress)
|
|
532
|
+
? isAddressMatched({
|
|
533
|
+
address: trace.trace.from,
|
|
534
|
+
blockNumber,
|
|
535
|
+
childAddresses: params.childAddress.get(
|
|
536
|
+
filter.fromAddress.id,
|
|
537
|
+
)!,
|
|
538
|
+
})
|
|
539
|
+
: true) &&
|
|
540
|
+
(isAddressFactory(filter.toAddress)
|
|
541
|
+
? isAddressMatched({
|
|
542
|
+
address: trace.trace.to,
|
|
543
|
+
blockNumber,
|
|
544
|
+
childAddresses: params.childAddress.get(
|
|
545
|
+
filter.toAddress.id,
|
|
546
|
+
)!,
|
|
547
|
+
})
|
|
548
|
+
: true)
|
|
549
|
+
) {
|
|
550
|
+
isMatched = true;
|
|
551
|
+
requiredTransactions.add(trace.transactionHash);
|
|
552
|
+
if (filter.hasTransactionReceipt) {
|
|
553
|
+
requiredTransactionReceipts.add(trace.transactionHash);
|
|
554
|
+
// skip to next trace
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
for (const filter of traceFilters) {
|
|
561
|
+
if (
|
|
562
|
+
isTraceFilterMatched({
|
|
563
|
+
filter,
|
|
564
|
+
trace: trace.trace,
|
|
565
|
+
block: { number: BigInt(blockNumber) },
|
|
566
|
+
}) &&
|
|
567
|
+
(isAddressFactory(filter.fromAddress)
|
|
568
|
+
? isAddressMatched({
|
|
569
|
+
address: trace.trace.from,
|
|
570
|
+
blockNumber,
|
|
571
|
+
childAddresses: params.childAddress.get(
|
|
572
|
+
filter.fromAddress.id,
|
|
573
|
+
)!,
|
|
574
|
+
})
|
|
575
|
+
: true) &&
|
|
576
|
+
(isAddressFactory(filter.toAddress)
|
|
577
|
+
? isAddressMatched({
|
|
578
|
+
address: trace.trace.to,
|
|
579
|
+
blockNumber,
|
|
580
|
+
childAddresses: params.childAddress.get(
|
|
581
|
+
filter.toAddress.id,
|
|
582
|
+
)!,
|
|
583
|
+
})
|
|
584
|
+
: true)
|
|
585
|
+
) {
|
|
586
|
+
isMatched = true;
|
|
587
|
+
requiredTransactions.add(trace.transactionHash);
|
|
588
|
+
if (filter.hasTransactionReceipt) {
|
|
589
|
+
requiredTransactionReceipts.add(trace.transactionHash);
|
|
590
|
+
// skip to next trace
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
return isMatched;
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
if (traces.length > 0) {
|
|
600
|
+
validateTracesAndBlock(
|
|
601
|
+
traces,
|
|
602
|
+
block,
|
|
603
|
+
{
|
|
604
|
+
method: "debug_traceBlockByNumber",
|
|
605
|
+
params: [toHex(blockNumber), { tracer: "callTracer" }],
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
method: "eth_getBlockByNumber",
|
|
609
|
+
params: [toHex(blockNumber), true],
|
|
610
|
+
},
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
////////
|
|
616
|
+
// Block
|
|
617
|
+
////////
|
|
618
|
+
|
|
619
|
+
if (
|
|
620
|
+
block === undefined &&
|
|
621
|
+
blockFilters.some((filter) =>
|
|
622
|
+
isBlockFilterMatched({
|
|
623
|
+
filter,
|
|
624
|
+
block: { number: BigInt(blockNumber) },
|
|
625
|
+
}),
|
|
626
|
+
)
|
|
627
|
+
) {
|
|
628
|
+
block = await eth_getBlockByNumber(
|
|
629
|
+
params.rpc,
|
|
630
|
+
[numberToHex(blockNumber), true],
|
|
631
|
+
context,
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
////////
|
|
636
|
+
// Transactions
|
|
637
|
+
////////
|
|
638
|
+
|
|
639
|
+
if (block === undefined) {
|
|
640
|
+
block = await eth_getBlockByNumber(
|
|
641
|
+
params.rpc,
|
|
642
|
+
[numberToHex(blockNumber), true],
|
|
643
|
+
context,
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const transactions = block.transactions.filter((transaction) => {
|
|
648
|
+
let isMatched = requiredTransactions.has(transaction.hash);
|
|
649
|
+
for (const filter of transactionFilters) {
|
|
650
|
+
if (
|
|
651
|
+
isTransactionFilterMatched({ filter, transaction }) &&
|
|
652
|
+
(isAddressFactory(filter.fromAddress)
|
|
653
|
+
? isAddressMatched({
|
|
654
|
+
address: transaction.from,
|
|
655
|
+
blockNumber,
|
|
656
|
+
childAddresses: params.childAddress.get(
|
|
657
|
+
filter.fromAddress.id,
|
|
658
|
+
)!,
|
|
659
|
+
})
|
|
660
|
+
: true) &&
|
|
661
|
+
(isAddressFactory(filter.toAddress)
|
|
662
|
+
? isAddressMatched({
|
|
663
|
+
address: transaction.to ?? undefined,
|
|
664
|
+
blockNumber,
|
|
665
|
+
childAddresses: params.childAddress.get(
|
|
666
|
+
filter.toAddress.id,
|
|
667
|
+
)!,
|
|
668
|
+
})
|
|
669
|
+
: true)
|
|
670
|
+
) {
|
|
671
|
+
requiredTransactionReceipts.add(transaction.hash);
|
|
672
|
+
isMatched = true;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return isMatched;
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
if (transactions.length > 0) {
|
|
679
|
+
validateTransactionsAndBlock(block, {
|
|
680
|
+
method: "eth_getBlockByNumber",
|
|
681
|
+
params: [toHex(blockNumber), true],
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const transactionsByHash = new Map<Hash, SyncTransaction>();
|
|
686
|
+
for (const transaction of transactions) {
|
|
687
|
+
transactionsByHash.set(transaction.hash, transaction);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
////////
|
|
691
|
+
// Transaction Receipts
|
|
692
|
+
////////
|
|
693
|
+
|
|
694
|
+
const receiptResponses = await eth_getTransactionReceipts(
|
|
695
|
+
params.rpc,
|
|
696
|
+
{
|
|
697
|
+
blockHash: block.hash,
|
|
698
|
+
transactionHashes: requiredTransactionReceipts,
|
|
699
|
+
},
|
|
700
|
+
context,
|
|
701
|
+
);
|
|
702
|
+
const transactionReceipts = receiptResponses.flatMap(
|
|
703
|
+
({ receipts, request }) => {
|
|
704
|
+
validateReceiptsAndBlock(receipts, block, request, {
|
|
705
|
+
method: "eth_getBlockByNumber",
|
|
706
|
+
params: [block.number, true],
|
|
707
|
+
});
|
|
708
|
+
return receipts.filter((receipt) =>
|
|
709
|
+
requiredTransactionReceipts.has(receipt.transactionHash),
|
|
710
|
+
);
|
|
711
|
+
},
|
|
712
|
+
);
|
|
713
|
+
|
|
714
|
+
params.common.logger.debug(
|
|
715
|
+
{
|
|
716
|
+
msg: "Fetched block data",
|
|
717
|
+
chain: params.chain.name,
|
|
718
|
+
chain_id: params.chain.id,
|
|
719
|
+
block: blockNumber,
|
|
720
|
+
transaction_count: transactions.length,
|
|
721
|
+
receipt_count: transactionReceipts.length,
|
|
722
|
+
trace_count: traces.length,
|
|
723
|
+
duration: endClock(),
|
|
724
|
+
},
|
|
725
|
+
["chain", "block"],
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
return {
|
|
729
|
+
blocks: [syncBlockToInternal({ block })],
|
|
730
|
+
logs: logs.map((log) => syncLogToInternal({ log })),
|
|
731
|
+
transactions: transactions.map((transaction) =>
|
|
732
|
+
syncTransactionToInternal({ transaction }),
|
|
733
|
+
),
|
|
734
|
+
transactionReceipts: transactionReceipts.map((transactionReceipt) =>
|
|
735
|
+
syncTransactionReceiptToInternal({ transactionReceipt }),
|
|
736
|
+
),
|
|
737
|
+
traces: traces.map((trace) =>
|
|
738
|
+
syncTraceToInternal({
|
|
739
|
+
trace,
|
|
740
|
+
block: block!,
|
|
741
|
+
transaction: transactionsByHash.get(trace.transactionHash)!,
|
|
742
|
+
}),
|
|
743
|
+
),
|
|
744
|
+
cursor: blockNumber,
|
|
745
|
+
};
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
const MAX_BLOCKS_IN_MEM = 100;
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Blocks that are fetched, but not yet yielded, in order. At most
|
|
752
|
+
* `MAX_BLOCKS_IN_MEM` blocks are fetched ahead of the consumer.
|
|
753
|
+
*/
|
|
754
|
+
const pendingBlockData: Promise<BlockData>[] = [];
|
|
755
|
+
|
|
756
|
+
for await (const interval of mergeGeneratorIntervals(
|
|
757
|
+
intervalGenerators,
|
|
758
|
+
)) {
|
|
759
|
+
for (
|
|
760
|
+
let blockNumber = interval[0];
|
|
761
|
+
blockNumber <= interval[1];
|
|
762
|
+
blockNumber++
|
|
763
|
+
) {
|
|
764
|
+
if (isBlockRequired(blockNumber) === false) continue;
|
|
765
|
+
|
|
766
|
+
const promise = syncBlock(blockNumber);
|
|
767
|
+
// Note: Prevent unhandled rejections for blocks that are not awaited yet or never
|
|
768
|
+
// awaited (generator returned early). Errors are still thrown when awaited below.
|
|
769
|
+
promise.catch(() => {});
|
|
770
|
+
pendingBlockData.push(promise);
|
|
771
|
+
|
|
772
|
+
if (pendingBlockData.length >= MAX_BLOCKS_IN_MEM) {
|
|
773
|
+
yield await pendingBlockData.shift()!;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
while (pendingBlockData.length > 0) {
|
|
779
|
+
yield await pendingBlockData.shift()!;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Note: Fetch all child addresses, even those after the last required block.
|
|
783
|
+
// Realtime sync requires them, e.g. when the contract `startBlock` is after the
|
|
784
|
+
// finalized block but the factory `startBlock` is not.
|
|
785
|
+
for (const pages of factoryPages.values()) {
|
|
786
|
+
await drainAsyncGenerator(pages);
|
|
787
|
+
}
|
|
788
|
+
},
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
export async function* mergeGeneratorIntervals(
|
|
793
|
+
filterGenerators: Map<{ interval: Interval }, AsyncGenerator<Interval>>,
|
|
794
|
+
): AsyncGenerator<Interval> {
|
|
795
|
+
const results = await Promise.all(
|
|
796
|
+
Array.from(filterGenerators.values()).map((gen) => gen.next()),
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
let cursor = Math.min(
|
|
800
|
+
...Array.from(filterGenerators.keys()).map(({ interval }) => interval[0]),
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
while (results.some((res) => res.done !== true)) {
|
|
804
|
+
const supremum = Math.min(
|
|
805
|
+
...results
|
|
806
|
+
.map((res) => (res.done ? undefined : res.value[1]))
|
|
807
|
+
.filter((x): x is number => x !== undefined),
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
const minIndices = Array.from(
|
|
811
|
+
Array.from(results.entries())
|
|
812
|
+
.map(([index, result]) => {
|
|
813
|
+
if (result.done) return undefined;
|
|
814
|
+
if (result.value[1] === supremum) return index;
|
|
815
|
+
return undefined;
|
|
816
|
+
})
|
|
817
|
+
.filter((x): x is number => x !== undefined),
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
const resultPromise = Promise.all(
|
|
821
|
+
minIndices.map((index) =>
|
|
822
|
+
Array.from(filterGenerators.values())[index]!.next(),
|
|
823
|
+
),
|
|
824
|
+
);
|
|
825
|
+
if (cursor <= supremum!) {
|
|
826
|
+
yield [cursor, supremum!];
|
|
827
|
+
cursor = supremum! + 1;
|
|
828
|
+
}
|
|
829
|
+
const nextResults = await resultPromise;
|
|
830
|
+
for (const [index, result] of nextResults.entries()) {
|
|
831
|
+
results[minIndices[index]!] = result;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|