ponder 0.14.13 → 0.15.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 +16 -0
- package/dist/esm/bin/commands/createViews.js +28 -11
- package/dist/esm/bin/commands/createViews.js.map +1 -1
- package/dist/esm/bin/commands/dev.js +42 -22
- package/dist/esm/bin/commands/dev.js.map +1 -1
- package/dist/esm/bin/commands/prune.js +3 -0
- package/dist/esm/bin/commands/prune.js.map +1 -1
- package/dist/esm/bin/commands/serve.js +4 -1
- package/dist/esm/bin/commands/serve.js.map +1 -1
- package/dist/esm/bin/commands/start.js +18 -6
- package/dist/esm/bin/commands/start.js.map +1 -1
- package/dist/esm/bin/isolatedController.js +200 -0
- package/dist/esm/bin/isolatedController.js.map +1 -0
- package/dist/esm/bin/isolatedWorker.js +146 -0
- package/dist/esm/bin/isolatedWorker.js.map +1 -0
- package/dist/esm/build/config.js +322 -402
- package/dist/esm/build/config.js.map +1 -1
- package/dist/esm/build/index.js +8 -11
- package/dist/esm/build/index.js.map +1 -1
- package/dist/esm/build/pre.js +1 -4
- package/dist/esm/build/pre.js.map +1 -1
- package/dist/esm/build/schema.js +25 -3
- package/dist/esm/build/schema.js.map +1 -1
- package/dist/esm/client/index.js +306 -42
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/database/actions.js +264 -104
- package/dist/esm/database/actions.js.map +1 -1
- package/dist/esm/database/index.js +39 -33
- package/dist/esm/database/index.js.map +1 -1
- package/dist/esm/database/queryBuilder.js +1 -0
- package/dist/esm/database/queryBuilder.js.map +1 -1
- package/dist/esm/drizzle/index.js +11 -7
- package/dist/esm/drizzle/index.js.map +1 -1
- package/dist/esm/drizzle/onchain.js +18 -0
- package/dist/esm/drizzle/onchain.js.map +1 -1
- package/dist/esm/indexing/client.js +32 -25
- package/dist/esm/indexing/client.js.map +1 -1
- package/dist/esm/indexing/index.js +110 -178
- package/dist/esm/indexing/index.js.map +1 -1
- package/dist/esm/indexing/profile.js +1 -1
- package/dist/esm/indexing/profile.js.map +1 -1
- package/dist/esm/indexing-store/cache.js +196 -274
- package/dist/esm/indexing-store/cache.js.map +1 -1
- package/dist/esm/indexing-store/historical.js +17 -13
- package/dist/esm/indexing-store/historical.js.map +1 -1
- package/dist/esm/indexing-store/index.js +10 -1
- package/dist/esm/indexing-store/index.js.map +1 -1
- package/dist/esm/indexing-store/profile.js +3 -3
- package/dist/esm/indexing-store/profile.js.map +1 -1
- package/dist/esm/indexing-store/realtime.js +27 -2
- package/dist/esm/indexing-store/realtime.js.map +1 -1
- package/dist/esm/internal/errors.js +28 -0
- package/dist/esm/internal/errors.js.map +1 -1
- package/dist/esm/internal/metrics.js +279 -82
- package/dist/esm/internal/metrics.js.map +1 -1
- package/dist/esm/internal/options.js +1 -0
- package/dist/esm/internal/options.js.map +1 -1
- package/dist/esm/internal/telemetry.js +1 -1
- package/dist/esm/internal/telemetry.js.map +1 -1
- package/dist/esm/rpc/http.js +130 -0
- package/dist/esm/rpc/http.js.map +1 -0
- package/dist/esm/rpc/index.js +38 -7
- package/dist/esm/rpc/index.js.map +1 -1
- package/dist/esm/runtime/events.js +179 -212
- package/dist/esm/runtime/events.js.map +1 -1
- package/dist/esm/runtime/filter.js +71 -0
- package/dist/esm/runtime/filter.js.map +1 -1
- package/dist/esm/runtime/fragments.js +78 -73
- package/dist/esm/runtime/fragments.js.map +1 -1
- package/dist/esm/runtime/historical.js +306 -130
- package/dist/esm/runtime/historical.js.map +1 -1
- package/dist/esm/runtime/index.js +183 -58
- package/dist/esm/runtime/index.js.map +1 -1
- package/dist/esm/runtime/isolated.js +462 -0
- package/dist/esm/runtime/isolated.js.map +1 -0
- package/dist/esm/runtime/multichain.js +80 -73
- package/dist/esm/runtime/multichain.js.map +1 -1
- package/dist/esm/runtime/omnichain.js +82 -75
- package/dist/esm/runtime/omnichain.js.map +1 -1
- package/dist/esm/runtime/realtime.js +198 -66
- package/dist/esm/runtime/realtime.js.map +1 -1
- package/dist/esm/sync-historical/index.js +416 -457
- package/dist/esm/sync-historical/index.js.map +1 -1
- package/dist/esm/sync-realtime/bloom.js +3 -3
- package/dist/esm/sync-realtime/bloom.js.map +1 -1
- package/dist/esm/sync-realtime/index.js +27 -46
- package/dist/esm/sync-realtime/index.js.map +1 -1
- package/dist/esm/sync-store/index.js +112 -63
- package/dist/esm/sync-store/index.js.map +1 -1
- package/dist/esm/utils/abi.js +20 -32
- package/dist/esm/utils/abi.js.map +1 -1
- package/dist/esm/utils/chunk.js +8 -0
- package/dist/esm/utils/chunk.js.map +1 -0
- package/dist/esm/utils/promiseAllSettledWithThrow.js +19 -0
- package/dist/esm/utils/promiseAllSettledWithThrow.js.map +1 -0
- package/dist/esm/{client/parse.js → utils/sql-parse.js} +94 -80
- package/dist/esm/utils/sql-parse.js.map +1 -0
- package/dist/types/bin/commands/createViews.d.ts.map +1 -1
- package/dist/types/bin/commands/dev.d.ts.map +1 -1
- package/dist/types/bin/commands/prune.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/bin/isolatedController.d.ts +13 -0
- package/dist/types/bin/isolatedController.d.ts.map +1 -0
- package/dist/types/bin/isolatedWorker.d.ts +9 -0
- package/dist/types/bin/isolatedWorker.d.ts.map +1 -0
- package/dist/types/build/config.d.ts +29 -11
- package/dist/types/build/config.d.ts.map +1 -1
- package/dist/types/build/index.d.ts +3 -2
- package/dist/types/build/index.d.ts.map +1 -1
- package/dist/types/build/pre.d.ts +1 -1
- package/dist/types/build/pre.d.ts.map +1 -1
- package/dist/types/build/schema.d.ts +5 -3
- package/dist/types/build/schema.d.ts.map +1 -1
- package/dist/types/client/index.d.ts +1 -1
- package/dist/types/client/index.d.ts.map +1 -1
- package/dist/types/config/index.d.ts +3 -3
- package/dist/types/config/index.d.ts.map +1 -1
- package/dist/types/database/actions.d.ts +53 -7
- package/dist/types/database/actions.d.ts.map +1 -1
- package/dist/types/database/index.d.ts +21 -21
- package/dist/types/database/index.d.ts.map +1 -1
- package/dist/types/database/queryBuilder.d.ts.map +1 -1
- package/dist/types/drizzle/index.d.ts +4 -5
- package/dist/types/drizzle/index.d.ts.map +1 -1
- package/dist/types/drizzle/onchain.d.ts +6 -0
- package/dist/types/drizzle/onchain.d.ts.map +1 -1
- package/dist/types/indexing/client.d.ts.map +1 -1
- package/dist/types/indexing/index.d.ts +2 -5
- package/dist/types/indexing/index.d.ts.map +1 -1
- package/dist/types/indexing-store/cache.d.ts +3 -2
- package/dist/types/indexing-store/cache.d.ts.map +1 -1
- package/dist/types/indexing-store/historical.d.ts +2 -1
- package/dist/types/indexing-store/historical.d.ts.map +1 -1
- package/dist/types/indexing-store/index.d.ts +1 -0
- package/dist/types/indexing-store/index.d.ts.map +1 -1
- package/dist/types/indexing-store/realtime.d.ts +2 -1
- package/dist/types/indexing-store/realtime.d.ts.map +1 -1
- package/dist/types/internal/errors.d.ts +5 -0
- package/dist/types/internal/errors.d.ts.map +1 -1
- package/dist/types/internal/metrics.d.ts +21 -0
- package/dist/types/internal/metrics.d.ts.map +1 -1
- package/dist/types/internal/options.d.ts +2 -0
- package/dist/types/internal/options.d.ts.map +1 -1
- package/dist/types/internal/types.d.ts +66 -58
- package/dist/types/internal/types.d.ts.map +1 -1
- package/dist/types/rpc/http.d.ts +17 -0
- package/dist/types/rpc/http.d.ts.map +1 -0
- package/dist/types/rpc/index.d.ts.map +1 -1
- package/dist/types/runtime/events.d.ts +4 -4
- package/dist/types/runtime/events.d.ts.map +1 -1
- package/dist/types/runtime/filter.d.ts +5 -1
- package/dist/types/runtime/filter.d.ts.map +1 -1
- package/dist/types/runtime/fragments.d.ts +4 -3
- package/dist/types/runtime/fragments.d.ts.map +1 -1
- package/dist/types/runtime/historical.d.ts +29 -13
- package/dist/types/runtime/historical.d.ts.map +1 -1
- package/dist/types/runtime/index.d.ts +49 -6
- package/dist/types/runtime/index.d.ts.map +1 -1
- package/dist/types/runtime/init.d.ts +5 -5
- package/dist/types/runtime/init.d.ts.map +1 -1
- package/dist/types/runtime/isolated.d.ts +14 -0
- package/dist/types/runtime/isolated.d.ts.map +1 -0
- 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 +21 -10
- package/dist/types/runtime/realtime.d.ts.map +1 -1
- package/dist/types/sync-historical/index.d.ts +18 -8
- package/dist/types/sync-historical/index.d.ts.map +1 -1
- package/dist/types/sync-realtime/bloom.d.ts.map +1 -1
- package/dist/types/sync-realtime/index.d.ts +2 -2
- package/dist/types/sync-realtime/index.d.ts.map +1 -1
- package/dist/types/sync-store/index.d.ts +9 -9
- package/dist/types/sync-store/index.d.ts.map +1 -1
- package/dist/types/utils/abi.d.ts +3 -34
- package/dist/types/utils/abi.d.ts.map +1 -1
- package/dist/types/utils/chunk.d.ts +2 -0
- package/dist/types/utils/chunk.d.ts.map +1 -0
- package/dist/types/utils/promiseAllSettledWithThrow.d.ts +8 -0
- package/dist/types/utils/promiseAllSettledWithThrow.d.ts.map +1 -0
- package/dist/types/utils/sql-parse.d.ts +21 -0
- package/dist/types/utils/sql-parse.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/bin/commands/createViews.ts +35 -15
- package/src/bin/commands/dev.ts +43 -21
- package/src/bin/commands/prune.ts +6 -0
- package/src/bin/commands/serve.ts +4 -1
- package/src/bin/commands/start.ts +20 -5
- package/src/bin/isolatedController.ts +300 -0
- package/src/bin/isolatedWorker.ts +192 -0
- package/src/build/config.ts +570 -632
- package/src/build/index.ts +14 -14
- package/src/build/pre.ts +1 -4
- package/src/build/schema.ts +49 -4
- package/src/client/index.ts +386 -48
- package/src/config/index.ts +3 -3
- package/src/database/actions.ts +469 -120
- package/src/database/index.ts +85 -58
- package/src/database/queryBuilder.ts +1 -0
- package/src/drizzle/index.ts +15 -7
- package/src/drizzle/onchain.ts +19 -0
- package/src/indexing/client.ts +38 -25
- package/src/indexing/index.ts +137 -230
- package/src/indexing/profile.ts +1 -1
- package/src/indexing-store/cache.ts +285 -414
- package/src/indexing-store/historical.ts +20 -10
- package/src/indexing-store/index.ts +16 -0
- package/src/indexing-store/profile.ts +3 -3
- package/src/indexing-store/realtime.ts +28 -0
- package/src/internal/errors.ts +26 -0
- package/src/internal/metrics.ts +341 -111
- package/src/internal/options.ts +4 -0
- package/src/internal/telemetry.ts +1 -1
- package/src/internal/types.ts +70 -87
- package/src/rpc/http.ts +164 -0
- package/src/rpc/index.ts +39 -7
- package/src/runtime/events.ts +195 -240
- package/src/runtime/filter.ts +85 -1
- package/src/runtime/fragments.ts +109 -113
- package/src/runtime/historical.ts +467 -189
- package/src/runtime/index.ts +337 -69
- package/src/runtime/init.ts +5 -5
- package/src/runtime/isolated.ts +768 -0
- package/src/runtime/multichain.ts +137 -102
- package/src/runtime/omnichain.ts +138 -106
- package/src/runtime/realtime.ts +322 -123
- package/src/sync-historical/index.ts +556 -692
- package/src/sync-realtime/bloom.ts +7 -3
- package/src/sync-realtime/index.ts +31 -46
- package/src/sync-store/index.ts +189 -95
- package/src/utils/abi.ts +33 -90
- package/src/utils/chunk.ts +7 -0
- package/src/utils/promiseAllSettledWithThrow.ts +27 -0
- package/src/{client/parse.ts → utils/sql-parse.ts} +100 -90
- package/dist/esm/client/parse.js.map +0 -1
- package/dist/types/client/parse.d.ts +0 -14
- package/dist/types/client/parse.d.ts.map +0 -1
|
@@ -1,41 +1,16 @@
|
|
|
1
1
|
import { _debug_traceBlockByNumber, _eth_getBlockByNumber, _eth_getBlockReceipts, _eth_getLogs, _eth_getTransactionReceipt, validateLogsAndBlock, validateReceiptsAndBlock, validateTracesAndBlock, validateTransactionsAndBlock, } from '../rpc/actions.js';
|
|
2
|
-
import { getChildAddress, isAddressFactory, isAddressMatched, isLogFactoryMatched, isTraceFilterMatched, isTransactionFilterMatched, isTransferFilterMatched, } from '../runtime/filter.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { getChildAddress, isAddressFactory, isAddressMatched, isBlockFilterMatched, isBlockInFilter, isLogFactoryMatched, isLogFilterMatched, isTraceFilterMatched, isTransactionFilterMatched, isTransferFilterMatched, } from '../runtime/filter.js';
|
|
3
|
+
import { getChunks, intervalBounds, intervalRange, } from '../utils/interval.js';
|
|
4
|
+
import { promiseAllSettledWithThrow } from '../utils/promiseAllSettledWithThrow.js';
|
|
5
|
+
import { createQueue } from '../utils/queue.js';
|
|
5
6
|
import { startClock } from '../utils/timer.js';
|
|
6
7
|
import { getLogsRetryHelper } from "@ponder/utils";
|
|
7
|
-
import {
|
|
8
|
+
import { hexToNumber, toHex, zeroHash, } from "viem";
|
|
8
9
|
export const createHistoricalSync = (args) => {
|
|
9
10
|
/**
|
|
10
11
|
* Flag to fetch transaction receipts through _eth_getBlockReceipts (true) or _eth_getTransactionReceipt (false)
|
|
11
12
|
*/
|
|
12
13
|
let isBlockReceipts = true;
|
|
13
|
-
/**
|
|
14
|
-
* Blocks that have already been extracted.
|
|
15
|
-
* Note: All entries are deleted at the end of each call to `sync()`.
|
|
16
|
-
*/
|
|
17
|
-
const blockCache = new Map();
|
|
18
|
-
/**
|
|
19
|
-
* Traces that have already been fetched.
|
|
20
|
-
* Note: All entries are deleted at the end of each call to `sync()`.
|
|
21
|
-
*/
|
|
22
|
-
const traceCache = new Map();
|
|
23
|
-
/**
|
|
24
|
-
* Transactions that should be saved to the sync-store.
|
|
25
|
-
* Note: All entries are deleted at the end of each call to `sync()`.
|
|
26
|
-
*/
|
|
27
|
-
const transactionsCache = new Set();
|
|
28
|
-
/**
|
|
29
|
-
* Block transaction receipts that have already been fetched.
|
|
30
|
-
* Note: All entries are deleted at the end of each call to `sync()`.
|
|
31
|
-
*/
|
|
32
|
-
const blockReceiptsCache = new Map();
|
|
33
|
-
/**
|
|
34
|
-
* Transaction receipts that have already been fetched.
|
|
35
|
-
* Note: All entries are deleted at the end of each call to `sync()`.
|
|
36
|
-
*/
|
|
37
|
-
const transactionReceiptsCache = new Map();
|
|
38
|
-
const childAddressesCache = new Map();
|
|
39
14
|
/**
|
|
40
15
|
* Data about the range passed to "eth_getLogs" share among all log
|
|
41
16
|
* filters and log factories.
|
|
@@ -43,30 +18,23 @@ export const createHistoricalSync = (args) => {
|
|
|
43
18
|
let logsRequestMetadata = {
|
|
44
19
|
estimatedRange: 500,
|
|
45
20
|
};
|
|
46
|
-
// Closest-to-tip block that has been synced.
|
|
47
|
-
let latestBlock;
|
|
48
|
-
////////
|
|
49
|
-
// Helper functions for sync tasks
|
|
50
|
-
////////
|
|
51
21
|
/**
|
|
52
22
|
* Split "eth_getLogs" requests into ranges inferred from errors
|
|
53
23
|
* and batch requests.
|
|
54
24
|
*/
|
|
55
|
-
const syncLogsDynamic = async ({
|
|
56
|
-
//
|
|
25
|
+
const syncLogsDynamic = async ({ address, topic0, topic1, topic2, topic3, interval }, context) => {
|
|
26
|
+
// Use the recommended range if available, else don't chunk the interval at all.
|
|
57
27
|
const intervals = getChunks({
|
|
58
28
|
interval,
|
|
59
29
|
maxChunkSize: logsRequestMetadata.confirmedRange ??
|
|
60
30
|
logsRequestMetadata.estimatedRange,
|
|
61
31
|
});
|
|
62
|
-
const topics =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
filter.topic3 ?? null,
|
|
69
|
-
];
|
|
32
|
+
const topics = [
|
|
33
|
+
topic0 ?? null,
|
|
34
|
+
topic1 ?? null,
|
|
35
|
+
topic2 ?? null,
|
|
36
|
+
topic3 ?? null,
|
|
37
|
+
];
|
|
70
38
|
// Note: the `topics` field is very fragile for many rpc providers, and
|
|
71
39
|
// cannot handle extra "null" topics
|
|
72
40
|
if (topics[3] === null) {
|
|
@@ -136,7 +104,7 @@ export const createHistoricalSync = (args) => {
|
|
|
136
104
|
? range
|
|
137
105
|
: undefined,
|
|
138
106
|
};
|
|
139
|
-
return syncLogsDynamic({ address,
|
|
107
|
+
return syncLogsDynamic({ address, topic0, topic1, topic2, topic3, interval }, context);
|
|
140
108
|
})))).then((logs) => logs.flat());
|
|
141
109
|
/**
|
|
142
110
|
* Dynamically increase the range used in "eth_getLogs" if an
|
|
@@ -147,58 +115,18 @@ export const createHistoricalSync = (args) => {
|
|
|
147
115
|
}
|
|
148
116
|
return logs;
|
|
149
117
|
};
|
|
150
|
-
/**
|
|
151
|
-
* Extract block, using `blockCache` to avoid fetching
|
|
152
|
-
* the same block twice. Also, update `latestBlock`.
|
|
153
|
-
*
|
|
154
|
-
* @param number Block to be extracted
|
|
155
|
-
*
|
|
156
|
-
* Note: This function could more accurately skip chain requests by taking
|
|
157
|
-
* advantage of `syncStore.hasBlock` and `syncStore.hasTransaction`.
|
|
158
|
-
*/
|
|
159
|
-
const syncBlock = async (number, context) => {
|
|
160
|
-
let block;
|
|
161
|
-
/**
|
|
162
|
-
* `blockCache` contains all blocks that have been extracted during the
|
|
163
|
-
* current call to `sync()`. If `number` is present in `blockCache` use it,
|
|
164
|
-
* otherwise, request the block and add it to `blockCache` and the sync-store.
|
|
165
|
-
*/
|
|
166
|
-
if (blockCache.has(number)) {
|
|
167
|
-
block = await blockCache.get(number);
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
const _block = _eth_getBlockByNumber(args.rpc, { blockNumber: toHex(number) }, { ...context, retryNullBlockRequest: true });
|
|
171
|
-
blockCache.set(number, _block);
|
|
172
|
-
block = await _block;
|
|
173
|
-
// Update `latestBlock` if `block` is closer to tip.
|
|
174
|
-
if (hexToBigInt(block.number) >= hexToBigInt(latestBlock?.number ?? "0x0")) {
|
|
175
|
-
latestBlock = block;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return block;
|
|
179
|
-
};
|
|
180
|
-
const syncTrace = async (block, context) => {
|
|
181
|
-
if (traceCache.has(block)) {
|
|
182
|
-
return traceCache.get(block);
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
const traces = _debug_traceBlockByNumber(args.rpc, { blockNumber: block }, context);
|
|
186
|
-
traceCache.set(block, traces);
|
|
187
|
-
return traces;
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
118
|
const syncTransactionReceipts = async (block, transactionHashes, context) => {
|
|
191
119
|
if (transactionHashes.size === 0) {
|
|
192
120
|
return [];
|
|
193
121
|
}
|
|
194
122
|
if (isBlockReceipts === false) {
|
|
195
|
-
const transactionReceipts = await Promise.all(Array.from(transactionHashes).map((hash) =>
|
|
123
|
+
const transactionReceipts = await Promise.all(Array.from(transactionHashes).map((hash) => _eth_getTransactionReceipt(args.rpc, { hash }, context)));
|
|
196
124
|
validateReceiptsAndBlock(transactionReceipts, block, "eth_getTransactionReceipt", "number");
|
|
197
125
|
return transactionReceipts;
|
|
198
126
|
}
|
|
199
127
|
let blockReceipts;
|
|
200
128
|
try {
|
|
201
|
-
blockReceipts = await
|
|
129
|
+
blockReceipts = await _eth_getBlockReceipts(args.rpc, { blockHash: block.hash }, context);
|
|
202
130
|
}
|
|
203
131
|
catch (_error) {
|
|
204
132
|
const error = _error;
|
|
@@ -216,34 +144,18 @@ export const createHistoricalSync = (args) => {
|
|
|
216
144
|
const transactionReceipts = blockReceipts.filter((receipt) => transactionHashes.has(receipt.transactionHash));
|
|
217
145
|
return transactionReceipts;
|
|
218
146
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
transactionReceiptsCache.set(transaction, receipt);
|
|
226
|
-
return receipt;
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
const syncBlockReceipts = async (block, context) => {
|
|
230
|
-
if (blockReceiptsCache.has(block.hash)) {
|
|
231
|
-
return blockReceiptsCache.get(block.hash);
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
const blockReceipts = _eth_getBlockReceipts(args.rpc, { blockHash: block.hash }, context);
|
|
235
|
-
blockReceiptsCache.set(block.hash, blockReceipts);
|
|
236
|
-
return blockReceipts;
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
/** Extract and insert the log-based addresses that match `filter` + `interval`. */
|
|
240
|
-
const syncLogFactory = async (factory, interval, context) => {
|
|
147
|
+
/**
|
|
148
|
+
* Fetch child addresses for `factory` within `interval`
|
|
149
|
+
*
|
|
150
|
+
* @dev Newly fetched child addresses are added into `args.childAddresses`
|
|
151
|
+
*/
|
|
152
|
+
const syncAddressFactory = async (factory, interval, context) => {
|
|
241
153
|
const logs = await syncLogsDynamic({
|
|
242
|
-
filter: factory,
|
|
243
|
-
interval,
|
|
244
154
|
address: factory.address,
|
|
155
|
+
topic0: factory.eventSelector,
|
|
156
|
+
interval,
|
|
245
157
|
}, context);
|
|
246
|
-
const childAddresses =
|
|
158
|
+
const childAddresses = new Map();
|
|
247
159
|
const childAddressesRecord = args.childAddresses.get(factory.id);
|
|
248
160
|
for (const log of logs) {
|
|
249
161
|
if (isLogFactoryMatched({ factory, log })) {
|
|
@@ -257,72 +169,130 @@ export const createHistoricalSync = (args) => {
|
|
|
257
169
|
}
|
|
258
170
|
}
|
|
259
171
|
}
|
|
260
|
-
|
|
261
|
-
// and not be a recovered factory from `recoverFilter`.
|
|
262
|
-
childAddressesCache.set(factory, childAddresses);
|
|
172
|
+
return childAddresses;
|
|
263
173
|
};
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return;
|
|
290
|
-
logs = await syncLogsDynamic({
|
|
291
|
-
filter,
|
|
292
|
-
interval,
|
|
293
|
-
address: childAddresses.size >=
|
|
294
|
-
args.common.options.factoryAddressCountThreshold
|
|
295
|
-
? undefined
|
|
296
|
-
: Array.from(childAddresses.keys()),
|
|
297
|
-
}, context);
|
|
298
|
-
logs = logs.filter((log) => isAddressMatched({
|
|
299
|
-
address: log.address,
|
|
300
|
-
blockNumber: hexToNumber(log.blockNumber),
|
|
301
|
-
childAddresses,
|
|
174
|
+
return {
|
|
175
|
+
async syncBlockRangeData({ interval, requiredIntervals, requiredFactoryIntervals, syncStore, }) {
|
|
176
|
+
const context = {
|
|
177
|
+
logger: args.common.logger.child({ action: "fetch_block_data" }),
|
|
178
|
+
};
|
|
179
|
+
const endClock = startClock();
|
|
180
|
+
const childAddresses = new Map();
|
|
181
|
+
const logs = [];
|
|
182
|
+
// Dedupe factory intervals by factory id
|
|
183
|
+
const factoryIntervalsById = new Map();
|
|
184
|
+
for (const { factory, interval } of requiredFactoryIntervals) {
|
|
185
|
+
if (factoryIntervalsById.has(factory.id)) {
|
|
186
|
+
const existingInterval = factoryIntervalsById.get(factory.id).interval;
|
|
187
|
+
factoryIntervalsById.get(factory.id).interval = intervalBounds([
|
|
188
|
+
existingInterval,
|
|
189
|
+
interval,
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
factoryIntervalsById.set(factory.id, { factory, interval });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
requiredFactoryIntervals = Array.from(factoryIntervalsById.values());
|
|
197
|
+
await Promise.all(requiredFactoryIntervals.map(async ({ factory, interval }) => {
|
|
198
|
+
childAddresses.set(factory.id, await syncAddressFactory(factory, interval, context));
|
|
302
199
|
}));
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
filter
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
200
|
+
const mergedEthGetLogsParams = new Map();
|
|
201
|
+
const singleEthGetLogsParams = [];
|
|
202
|
+
for (const { filter, interval } of requiredIntervals) {
|
|
203
|
+
if (filter.type !== "log")
|
|
204
|
+
continue;
|
|
205
|
+
const hasAddress = filter.address !== undefined;
|
|
206
|
+
const hasTopic1 = filter.topic1 !== undefined;
|
|
207
|
+
const hasTopic2 = filter.topic2 !== undefined;
|
|
208
|
+
const hasTopic3 = filter.topic3 !== undefined;
|
|
209
|
+
if (hasAddress === false || hasTopic1 || hasTopic2 || hasTopic3) {
|
|
210
|
+
if (isAddressFactory(filter.address)) {
|
|
211
|
+
const childAddresses = args.childAddresses.get(filter.address.id);
|
|
212
|
+
singleEthGetLogsParams.push({
|
|
213
|
+
address: childAddresses.size >=
|
|
214
|
+
args.common.options.factoryAddressCountThreshold
|
|
215
|
+
? undefined
|
|
216
|
+
: Array.from(childAddresses.keys()),
|
|
217
|
+
topic0: filter.topic0,
|
|
218
|
+
topic1: filter.topic1,
|
|
219
|
+
topic2: filter.topic2,
|
|
220
|
+
topic3: filter.topic3,
|
|
221
|
+
interval,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
singleEthGetLogsParams.push({
|
|
226
|
+
address: filter.address,
|
|
227
|
+
topic0: filter.topic0,
|
|
228
|
+
topic1: filter.topic1,
|
|
229
|
+
topic2: filter.topic2,
|
|
230
|
+
topic3: filter.topic3,
|
|
231
|
+
interval,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
let addressKey;
|
|
237
|
+
if (isAddressFactory(filter.address)) {
|
|
238
|
+
addressKey = filter.address.id;
|
|
239
|
+
}
|
|
240
|
+
else if (Array.isArray(filter.address)) {
|
|
241
|
+
addressKey = filter.address.join("_");
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
addressKey = filter.address;
|
|
245
|
+
}
|
|
246
|
+
if (mergedEthGetLogsParams.has(addressKey) === false) {
|
|
247
|
+
if (isAddressFactory(filter.address)) {
|
|
248
|
+
const childAddresses = args.childAddresses.get(filter.address.id);
|
|
249
|
+
mergedEthGetLogsParams.set(addressKey, {
|
|
250
|
+
address: childAddresses.size >=
|
|
251
|
+
args.common.options.factoryAddressCountThreshold
|
|
252
|
+
? undefined
|
|
253
|
+
: Array.from(childAddresses.keys()),
|
|
254
|
+
topic0: filter.topic0,
|
|
255
|
+
topic1: filter.topic1,
|
|
256
|
+
topic2: filter.topic2,
|
|
257
|
+
topic3: filter.topic3,
|
|
258
|
+
interval,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
mergedEthGetLogsParams.set(addressKey, {
|
|
263
|
+
address: filter.address,
|
|
264
|
+
topic0: filter.topic0,
|
|
265
|
+
topic1: filter.topic1,
|
|
266
|
+
topic2: filter.topic2,
|
|
267
|
+
topic3: filter.topic3,
|
|
268
|
+
interval,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
const existingInterval = mergedEthGetLogsParams.get(addressKey).interval;
|
|
274
|
+
const existingTopic0 = mergedEthGetLogsParams.get(addressKey)
|
|
275
|
+
.topic0;
|
|
276
|
+
mergedEthGetLogsParams.get(addressKey).topic0 = [
|
|
277
|
+
...(Array.isArray(existingTopic0)
|
|
278
|
+
? existingTopic0
|
|
279
|
+
: [existingTopic0]),
|
|
280
|
+
filter.topic0,
|
|
281
|
+
];
|
|
282
|
+
mergedEthGetLogsParams.get(addressKey).interval = intervalBounds([
|
|
283
|
+
existingInterval,
|
|
284
|
+
interval,
|
|
285
|
+
]);
|
|
286
|
+
}
|
|
317
287
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
288
|
+
const ethGetLogsParams = [
|
|
289
|
+
...singleEthGetLogsParams,
|
|
290
|
+
...Array.from(mergedEthGetLogsParams.values()),
|
|
291
|
+
];
|
|
292
|
+
await Promise.all(ethGetLogsParams.map(async (params) => {
|
|
293
|
+
const _logs = await syncLogsDynamic(params, context);
|
|
294
|
+
logs.push(..._logs);
|
|
295
|
+
}));
|
|
326
296
|
for (const log of logs) {
|
|
327
297
|
if (log.transactionHash === zeroHash) {
|
|
328
298
|
args.common.logger.warn({
|
|
@@ -330,318 +300,307 @@ export const createHistoricalSync = (args) => {
|
|
|
330
300
|
action: "fetch_block_data",
|
|
331
301
|
chain: args.chain.name,
|
|
332
302
|
chain_id: args.chain.id,
|
|
333
|
-
number: hexToNumber(
|
|
334
|
-
hash:
|
|
303
|
+
number: hexToNumber(log.blockNumber),
|
|
304
|
+
hash: log.blockHash,
|
|
335
305
|
logIndex: hexToNumber(log.logIndex),
|
|
336
306
|
});
|
|
337
307
|
}
|
|
338
308
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
transactionsCache.add(hash);
|
|
343
|
-
}
|
|
344
|
-
if (filter.hasTransactionReceipt) {
|
|
345
|
-
const transactionReceipts = await Promise.all(blocks.map((block) => {
|
|
346
|
-
const blockTransactionHashes = new Set();
|
|
347
|
-
for (const log of logsPerBlock.get(hexToNumber(block.number))) {
|
|
348
|
-
if (log.transactionHash !== zeroHash) {
|
|
349
|
-
blockTransactionHashes.add(log.transactionHash);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
return syncTransactionReceipts(block, blockTransactionHashes, context);
|
|
353
|
-
})).then((receipts) => receipts.flat());
|
|
354
|
-
await args.syncStore.insertTransactionReceipts({
|
|
355
|
-
transactionReceipts,
|
|
356
|
-
chainId: args.chain.id,
|
|
357
|
-
}, context);
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
const syncBlockFilter = async (filter, interval, context) => {
|
|
361
|
-
const baseOffset = (interval[0] - filter.offset) % filter.interval;
|
|
362
|
-
const offset = baseOffset === 0 ? 0 : filter.interval - baseOffset;
|
|
363
|
-
// Determine which blocks are matched by the block filter.
|
|
364
|
-
const requiredBlocks = [];
|
|
365
|
-
for (let b = interval[0] + offset; b <= interval[1]; b += filter.interval) {
|
|
366
|
-
requiredBlocks.push(b);
|
|
367
|
-
}
|
|
368
|
-
await Promise.all(requiredBlocks.map(async (number) => {
|
|
369
|
-
const block = await syncBlock(number, context);
|
|
370
|
-
validateTransactionsAndBlock(block, "number");
|
|
371
|
-
return block;
|
|
372
|
-
}));
|
|
373
|
-
};
|
|
374
|
-
const syncTransactionFilter = async (filter, interval, context) => {
|
|
375
|
-
const fromChildAddresses = isAddressFactory(filter.fromAddress)
|
|
376
|
-
? await syncAddressFactory(filter.fromAddress, interval, context)
|
|
377
|
-
: undefined;
|
|
378
|
-
const toChildAddresses = isAddressFactory(filter.toAddress)
|
|
379
|
-
? await syncAddressFactory(filter.toAddress, interval, context)
|
|
380
|
-
: undefined;
|
|
381
|
-
// Note: Exit early when only the factory needs to be synced
|
|
382
|
-
if ((filter.fromBlock ?? 0) > interval[1])
|
|
383
|
-
return;
|
|
384
|
-
const blocks = await Promise.all(intervalRange(interval).map((number) => syncBlock(number, context)));
|
|
385
|
-
const transactionHashes = new Set();
|
|
386
|
-
const requiredBlocks = new Set();
|
|
387
|
-
for (const block of blocks) {
|
|
388
|
-
validateTransactionsAndBlock(block, "number");
|
|
389
|
-
for (const transaction of block.transactions) {
|
|
390
|
-
if (isTransactionFilterMatched({ filter, transaction }) === false) {
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
if (isAddressFactory(filter.fromAddress) &&
|
|
394
|
-
isAddressMatched({
|
|
395
|
-
address: transaction.from,
|
|
396
|
-
blockNumber: Number(block.number),
|
|
397
|
-
childAddresses: fromChildAddresses,
|
|
398
|
-
}) === false) {
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
if (isAddressFactory(filter.toAddress) &&
|
|
402
|
-
isAddressMatched({
|
|
403
|
-
address: transaction.to ?? undefined,
|
|
404
|
-
blockNumber: Number(block.number),
|
|
405
|
-
childAddresses: toChildAddresses,
|
|
406
|
-
}) === false) {
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
transactionHashes.add(transaction.hash);
|
|
410
|
-
requiredBlocks.add(block);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
for (const hash of transactionHashes) {
|
|
414
|
-
transactionsCache.add(hash);
|
|
415
|
-
}
|
|
416
|
-
const transactionReceipts = await Promise.all(Array.from(requiredBlocks).map((block) => {
|
|
417
|
-
const blockTransactionHashes = new Set(block.transactions
|
|
418
|
-
.filter((t) => transactionHashes.has(t.hash))
|
|
419
|
-
.map((t) => t.hash));
|
|
420
|
-
return syncTransactionReceipts(block, blockTransactionHashes, context);
|
|
421
|
-
})).then((receipts) => receipts.flat());
|
|
422
|
-
await args.syncStore.insertTransactionReceipts({
|
|
423
|
-
transactionReceipts,
|
|
424
|
-
chainId: args.chain.id,
|
|
425
|
-
}, context);
|
|
426
|
-
};
|
|
427
|
-
const syncTraceOrTransferFilter = async (filter, interval, context) => {
|
|
428
|
-
const fromChildAddresses = isAddressFactory(filter.fromAddress)
|
|
429
|
-
? await syncAddressFactory(filter.fromAddress, interval, context)
|
|
430
|
-
: undefined;
|
|
431
|
-
const toChildAddresses = isAddressFactory(filter.toAddress)
|
|
432
|
-
? await syncAddressFactory(filter.toAddress, interval, context)
|
|
433
|
-
: undefined;
|
|
434
|
-
// Note: Exit early when only the factory needs to be synced
|
|
435
|
-
if ((filter.fromBlock ?? 0) > interval[1])
|
|
436
|
-
return;
|
|
437
|
-
const requiredBlocks = new Set();
|
|
438
|
-
const traces = await Promise.all(intervalRange(interval).map(async (number) => {
|
|
439
|
-
let traces = await syncTrace(number, context);
|
|
440
|
-
// remove unmatched traces
|
|
441
|
-
traces = traces.filter((trace) => {
|
|
442
|
-
if (filter.type === "trace" &&
|
|
443
|
-
isTraceFilterMatched({
|
|
444
|
-
filter,
|
|
445
|
-
trace: trace.trace,
|
|
446
|
-
block: { number: BigInt(number) },
|
|
447
|
-
}) === false) {
|
|
448
|
-
return false;
|
|
449
|
-
}
|
|
450
|
-
if (filter.type === "transfer" &&
|
|
451
|
-
isTransferFilterMatched({
|
|
452
|
-
filter,
|
|
453
|
-
trace: trace.trace,
|
|
454
|
-
block: { number: BigInt(number) },
|
|
455
|
-
}) === false) {
|
|
456
|
-
return false;
|
|
457
|
-
}
|
|
458
|
-
if (isAddressFactory(filter.fromAddress) &&
|
|
459
|
-
isAddressMatched({
|
|
460
|
-
address: trace.trace.from,
|
|
461
|
-
blockNumber: number,
|
|
462
|
-
childAddresses: fromChildAddresses,
|
|
463
|
-
}) === false) {
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
if (isAddressFactory(filter.toAddress) &&
|
|
467
|
-
isAddressMatched({
|
|
468
|
-
address: trace.trace.to,
|
|
469
|
-
blockNumber: number,
|
|
470
|
-
childAddresses: toChildAddresses,
|
|
471
|
-
}) === false) {
|
|
472
|
-
return false;
|
|
473
|
-
}
|
|
474
|
-
return true;
|
|
475
|
-
});
|
|
476
|
-
if (traces.length === 0)
|
|
477
|
-
return [];
|
|
478
|
-
const block = await syncBlock(number, context);
|
|
479
|
-
validateTransactionsAndBlock(block, "number");
|
|
480
|
-
validateTracesAndBlock(traces, block, "number");
|
|
481
|
-
requiredBlocks.add(block);
|
|
482
|
-
const transactionsByHash = new Map();
|
|
483
|
-
for (const transaction of block.transactions) {
|
|
484
|
-
transactionsByHash.set(transaction.hash, transaction);
|
|
309
|
+
let childAddressCount = 0;
|
|
310
|
+
for (const { size } of childAddresses.values()) {
|
|
311
|
+
childAddressCount += size;
|
|
485
312
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const blockTransactionHashes = new Set(traces
|
|
499
|
-
.filter((t) => t.block.hash === block.hash)
|
|
500
|
-
.map((t) => t.transaction.hash));
|
|
501
|
-
return syncTransactionReceipts(block, blockTransactionHashes, context);
|
|
502
|
-
})).then((receipts) => receipts.flat());
|
|
503
|
-
await args.syncStore.insertTransactionReceipts({
|
|
504
|
-
transactionReceipts,
|
|
313
|
+
args.common.logger.debug({
|
|
314
|
+
msg: "Fetched block range data",
|
|
315
|
+
chain: args.chain.name,
|
|
316
|
+
chain_id: args.chain.id,
|
|
317
|
+
block_range: JSON.stringify(interval),
|
|
318
|
+
log_count: logs.length,
|
|
319
|
+
child_address_count: childAddressCount,
|
|
320
|
+
duration: endClock(),
|
|
321
|
+
}, ["chain", "block_range"]);
|
|
322
|
+
await promiseAllSettledWithThrow(Array.from(childAddresses.entries()).map(([factoryId, childAddresses]) => syncStore.insertChildAddresses({
|
|
323
|
+
factory: factoryIntervalsById.get(factoryId).factory,
|
|
324
|
+
childAddresses,
|
|
505
325
|
chainId: args.chain.id,
|
|
506
|
-
}, context);
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
async sync(_interval) {
|
|
326
|
+
}, context)));
|
|
327
|
+
return logs;
|
|
328
|
+
},
|
|
329
|
+
async syncBlockData({ syncStore, interval, requiredIntervals, logs }) {
|
|
511
330
|
const context = {
|
|
512
331
|
logger: args.common.logger.child({ action: "fetch_block_data" }),
|
|
513
332
|
};
|
|
514
333
|
const endClock = startClock();
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
[
|
|
522
|
-
Math.max(filter.fromBlock ?? 0, _interval[0]),
|
|
523
|
-
Math.min(filter.toBlock ?? Number.POSITIVE_INFINITY, _interval[1]),
|
|
524
|
-
],
|
|
525
|
-
];
|
|
334
|
+
const blockFilters = [];
|
|
335
|
+
const transactionFilters = [];
|
|
336
|
+
const traceFilters = [];
|
|
337
|
+
const logFilters = [];
|
|
338
|
+
const transferFilters = [];
|
|
339
|
+
for (const { filter } of requiredIntervals) {
|
|
526
340
|
switch (filter.type) {
|
|
527
|
-
case "
|
|
528
|
-
|
|
529
|
-
filterIntervals.push([
|
|
530
|
-
Math.max(filter.address.fromBlock ?? 0, _interval[0]),
|
|
531
|
-
Math.min(filter.address.toBlock ?? Number.POSITIVE_INFINITY, _interval[1]),
|
|
532
|
-
]);
|
|
533
|
-
}
|
|
341
|
+
case "block": {
|
|
342
|
+
blockFilters.push(filter);
|
|
534
343
|
break;
|
|
535
|
-
case "trace":
|
|
536
|
-
case "transaction":
|
|
537
|
-
case "transfer":
|
|
538
|
-
if (isAddressFactory(filter.fromAddress)) {
|
|
539
|
-
filterIntervals.push([
|
|
540
|
-
Math.max(filter.fromAddress.fromBlock ?? 0, _interval[0]),
|
|
541
|
-
Math.min(filter.fromAddress.toBlock ?? Number.POSITIVE_INFINITY, _interval[1]),
|
|
542
|
-
]);
|
|
543
|
-
}
|
|
544
|
-
if (isAddressFactory(filter.toAddress)) {
|
|
545
|
-
filterIntervals.push([
|
|
546
|
-
Math.max(filter.toAddress.fromBlock ?? 0, _interval[0]),
|
|
547
|
-
Math.min(filter.toAddress.toBlock ?? Number.POSITIVE_INFINITY, _interval[1]),
|
|
548
|
-
]);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
filterIntervals = filterIntervals.filter(([start, end]) => start <= end);
|
|
552
|
-
if (filterIntervals.length === 0) {
|
|
553
|
-
continue;
|
|
554
|
-
}
|
|
555
|
-
filterIntervals = intervalUnion(filterIntervals);
|
|
556
|
-
const completedIntervals = args.cachedIntervals.get(filter);
|
|
557
|
-
const requiredIntervals = [];
|
|
558
|
-
for (const { fragment, intervals: fragmentIntervals, } of completedIntervals) {
|
|
559
|
-
const requiredFragmentIntervals = intervalDifference(filterIntervals, fragmentIntervals);
|
|
560
|
-
if (requiredFragmentIntervals.length > 0) {
|
|
561
|
-
requiredIntervals.push({
|
|
562
|
-
fragment,
|
|
563
|
-
intervals: requiredFragmentIntervals,
|
|
564
|
-
});
|
|
565
344
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const requiredInterval = intervalBounds(requiredIntervals.flatMap(({ intervals }) => intervals));
|
|
569
|
-
const requiredFilter = recoverFilter(filter, requiredIntervals.map(({ fragment }) => fragment));
|
|
570
|
-
intervalsToSync.push({
|
|
571
|
-
filter: requiredFilter,
|
|
572
|
-
interval: requiredInterval,
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
await Promise.all(intervalsToSync.map(async ({ filter, interval }) => {
|
|
577
|
-
// Request last block of interval
|
|
578
|
-
const blockPromise = syncBlock(interval[1], context);
|
|
579
|
-
switch (filter.type) {
|
|
580
|
-
case "log": {
|
|
581
|
-
await syncLogFilter(filter, interval, context);
|
|
345
|
+
case "transaction": {
|
|
346
|
+
transactionFilters.push(filter);
|
|
582
347
|
break;
|
|
583
348
|
}
|
|
584
|
-
case "
|
|
585
|
-
|
|
349
|
+
case "trace": {
|
|
350
|
+
traceFilters.push(filter);
|
|
586
351
|
break;
|
|
587
352
|
}
|
|
588
|
-
case "
|
|
589
|
-
|
|
353
|
+
case "log": {
|
|
354
|
+
logFilters.push(filter);
|
|
590
355
|
break;
|
|
591
356
|
}
|
|
592
|
-
case "trace":
|
|
593
357
|
case "transfer": {
|
|
594
|
-
|
|
358
|
+
transferFilters.push(filter);
|
|
595
359
|
break;
|
|
596
360
|
}
|
|
597
361
|
}
|
|
598
|
-
await blockPromise;
|
|
599
|
-
}));
|
|
600
|
-
const blocks = await Promise.all(blockCache.values());
|
|
601
|
-
await Promise.all([
|
|
602
|
-
args.syncStore.insertBlocks({ blocks, chainId: args.chain.id }, context),
|
|
603
|
-
args.syncStore.insertTransactions({
|
|
604
|
-
transactions: blocks.flatMap((block) => block.transactions.filter(({ hash }) => transactionsCache.has(hash))),
|
|
605
|
-
chainId: args.chain.id,
|
|
606
|
-
}, context),
|
|
607
|
-
...Array.from(childAddressesCache.entries()).map(([factory, childAddresses]) => args.syncStore.insertChildAddresses({
|
|
608
|
-
factory,
|
|
609
|
-
childAddresses,
|
|
610
|
-
chainId: args.chain.id,
|
|
611
|
-
}, context)),
|
|
612
|
-
]);
|
|
613
|
-
// Add corresponding intervals to the sync-store
|
|
614
|
-
// Note: this should happen after so the database doesn't become corrupted
|
|
615
|
-
if (args.chain.disableCache === false) {
|
|
616
|
-
await args.syncStore.insertIntervals({
|
|
617
|
-
intervals: intervalsToSync,
|
|
618
|
-
chainId: args.chain.id,
|
|
619
|
-
}, context);
|
|
620
362
|
}
|
|
621
|
-
|
|
622
|
-
for (const
|
|
623
|
-
|
|
363
|
+
const perBlockLogs = new Map();
|
|
364
|
+
for (const log of logs) {
|
|
365
|
+
const blockNumber = hexToNumber(log.blockNumber);
|
|
366
|
+
if (perBlockLogs.has(blockNumber) === false) {
|
|
367
|
+
perBlockLogs.set(blockNumber, []);
|
|
368
|
+
}
|
|
369
|
+
perBlockLogs.get(blockNumber).push(log);
|
|
370
|
+
}
|
|
371
|
+
let closestToTipBlock;
|
|
372
|
+
const syncBlockData = async (blockNumber) => {
|
|
373
|
+
let block;
|
|
374
|
+
const requiredTransactions = new Set();
|
|
375
|
+
const requiredTransactionReceipts = new Set();
|
|
376
|
+
////////
|
|
377
|
+
// Logs
|
|
378
|
+
////////
|
|
379
|
+
let logs = [];
|
|
380
|
+
if (perBlockLogs.has(blockNumber)) {
|
|
381
|
+
block = await _eth_getBlockByNumber(args.rpc, { blockNumber }, context);
|
|
382
|
+
logs = perBlockLogs.get(blockNumber).filter((log) => {
|
|
383
|
+
let isMatched = false;
|
|
384
|
+
for (const filter of logFilters) {
|
|
385
|
+
if (isLogFilterMatched({ filter, log }) &&
|
|
386
|
+
(isAddressFactory(filter.address)
|
|
387
|
+
? isAddressMatched({
|
|
388
|
+
address: log.address,
|
|
389
|
+
blockNumber,
|
|
390
|
+
childAddresses: args.childAddresses.get(filter.address.id),
|
|
391
|
+
})
|
|
392
|
+
: true)) {
|
|
393
|
+
isMatched = true;
|
|
394
|
+
requiredTransactions.add(log.transactionHash);
|
|
395
|
+
if (filter.hasTransactionReceipt) {
|
|
396
|
+
requiredTransactionReceipts.add(log.transactionHash);
|
|
397
|
+
// skip to next log
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return isMatched;
|
|
403
|
+
});
|
|
404
|
+
if (logs.length > 0) {
|
|
405
|
+
validateLogsAndBlock(logs, block, "number");
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
////////
|
|
409
|
+
// Traces
|
|
410
|
+
////////
|
|
411
|
+
const shouldRequestTraces = traceFilters.some((filter) => isBlockInFilter(filter, blockNumber)) ||
|
|
412
|
+
transferFilters.some((filter) => isBlockInFilter(filter, blockNumber));
|
|
413
|
+
let traces = [];
|
|
414
|
+
if (shouldRequestTraces) {
|
|
415
|
+
if (block === undefined) {
|
|
416
|
+
[block, traces] = await Promise.all([
|
|
417
|
+
_eth_getBlockByNumber(args.rpc, { blockNumber }, context),
|
|
418
|
+
_debug_traceBlockByNumber(args.rpc, { blockNumber }, context),
|
|
419
|
+
]);
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
traces = await _debug_traceBlockByNumber(args.rpc, { blockNumber }, context);
|
|
423
|
+
}
|
|
424
|
+
traces = traces.filter((trace) => {
|
|
425
|
+
let isMatched = false;
|
|
426
|
+
for (const filter of transferFilters) {
|
|
427
|
+
if (isTransferFilterMatched({
|
|
428
|
+
filter,
|
|
429
|
+
trace: trace.trace,
|
|
430
|
+
block: { number: BigInt(blockNumber) },
|
|
431
|
+
}) &&
|
|
432
|
+
(isAddressFactory(filter.fromAddress)
|
|
433
|
+
? isAddressMatched({
|
|
434
|
+
address: trace.trace.from,
|
|
435
|
+
blockNumber,
|
|
436
|
+
childAddresses: args.childAddresses.get(filter.fromAddress.id),
|
|
437
|
+
})
|
|
438
|
+
: true) &&
|
|
439
|
+
(isAddressFactory(filter.toAddress)
|
|
440
|
+
? isAddressMatched({
|
|
441
|
+
address: trace.trace.to,
|
|
442
|
+
blockNumber,
|
|
443
|
+
childAddresses: args.childAddresses.get(filter.toAddress.id),
|
|
444
|
+
})
|
|
445
|
+
: true)) {
|
|
446
|
+
isMatched = true;
|
|
447
|
+
requiredTransactions.add(trace.transactionHash);
|
|
448
|
+
if (filter.hasTransactionReceipt) {
|
|
449
|
+
requiredTransactionReceipts.add(trace.transactionHash);
|
|
450
|
+
// skip to next trace
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
for (const filter of traceFilters) {
|
|
456
|
+
if (isTraceFilterMatched({
|
|
457
|
+
filter,
|
|
458
|
+
trace: trace.trace,
|
|
459
|
+
block: { number: BigInt(blockNumber) },
|
|
460
|
+
}) &&
|
|
461
|
+
(isAddressFactory(filter.fromAddress)
|
|
462
|
+
? isAddressMatched({
|
|
463
|
+
address: trace.trace.from,
|
|
464
|
+
blockNumber,
|
|
465
|
+
childAddresses: args.childAddresses.get(filter.fromAddress.id),
|
|
466
|
+
})
|
|
467
|
+
: true) &&
|
|
468
|
+
(isAddressFactory(filter.toAddress)
|
|
469
|
+
? isAddressMatched({
|
|
470
|
+
address: trace.trace.to,
|
|
471
|
+
blockNumber,
|
|
472
|
+
childAddresses: args.childAddresses.get(filter.toAddress.id),
|
|
473
|
+
})
|
|
474
|
+
: true)) {
|
|
475
|
+
isMatched = true;
|
|
476
|
+
requiredTransactions.add(trace.transactionHash);
|
|
477
|
+
if (filter.hasTransactionReceipt) {
|
|
478
|
+
requiredTransactionReceipts.add(trace.transactionHash);
|
|
479
|
+
// skip to next trace
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return isMatched;
|
|
485
|
+
});
|
|
486
|
+
if (traces.length > 0) {
|
|
487
|
+
validateTracesAndBlock(traces, block, "number");
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
////////
|
|
491
|
+
// Block
|
|
492
|
+
////////
|
|
493
|
+
if (block === undefined &&
|
|
494
|
+
blockFilters.some((filter) => isBlockFilterMatched({
|
|
495
|
+
filter,
|
|
496
|
+
block: { number: BigInt(blockNumber) },
|
|
497
|
+
}))) {
|
|
498
|
+
block = await _eth_getBlockByNumber(args.rpc, { blockNumber }, context);
|
|
499
|
+
}
|
|
500
|
+
////////
|
|
501
|
+
// Transactions
|
|
502
|
+
////////
|
|
503
|
+
// Return early if no data is fetched
|
|
504
|
+
if (block === undefined &&
|
|
505
|
+
transactionFilters.some((filter) => isBlockInFilter(filter, blockNumber)) === false) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (block === undefined) {
|
|
509
|
+
block = await _eth_getBlockByNumber(args.rpc, { blockNumber }, context);
|
|
510
|
+
}
|
|
511
|
+
if (closestToTipBlock === undefined ||
|
|
512
|
+
hexToNumber(block.number) > hexToNumber(closestToTipBlock.number)) {
|
|
513
|
+
closestToTipBlock = block;
|
|
514
|
+
}
|
|
515
|
+
const transactions = block.transactions.filter((transaction) => {
|
|
516
|
+
let isMatched = requiredTransactions.has(transaction.hash);
|
|
517
|
+
for (const filter of transactionFilters) {
|
|
518
|
+
if (isTransactionFilterMatched({ filter, transaction }) &&
|
|
519
|
+
(isAddressFactory(filter.fromAddress)
|
|
520
|
+
? isAddressMatched({
|
|
521
|
+
address: transaction.from,
|
|
522
|
+
blockNumber,
|
|
523
|
+
childAddresses: args.childAddresses.get(filter.fromAddress.id),
|
|
524
|
+
})
|
|
525
|
+
: true) &&
|
|
526
|
+
(isAddressFactory(filter.toAddress)
|
|
527
|
+
? isAddressMatched({
|
|
528
|
+
address: transaction.to ?? undefined,
|
|
529
|
+
blockNumber,
|
|
530
|
+
childAddresses: args.childAddresses.get(filter.toAddress.id),
|
|
531
|
+
})
|
|
532
|
+
: true)) {
|
|
533
|
+
requiredTransactionReceipts.add(transaction.hash);
|
|
534
|
+
isMatched = true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return isMatched;
|
|
538
|
+
});
|
|
539
|
+
if (transactions.length > 0) {
|
|
540
|
+
validateTransactionsAndBlock(block, "number");
|
|
541
|
+
}
|
|
542
|
+
const transactionsByHash = new Map();
|
|
543
|
+
for (const transaction of transactions) {
|
|
544
|
+
transactionsByHash.set(transaction.hash, transaction);
|
|
545
|
+
}
|
|
546
|
+
////////
|
|
547
|
+
// Transaction Receipts
|
|
548
|
+
////////
|
|
549
|
+
const transactionReceipts = await syncTransactionReceipts(block, requiredTransactionReceipts);
|
|
550
|
+
blockCount += 1;
|
|
551
|
+
transactionCount += transactions.length;
|
|
552
|
+
receiptCount += transactionReceipts.length;
|
|
553
|
+
traceCount += traces.length;
|
|
554
|
+
// Free memory of all unused transactions
|
|
555
|
+
block.transactions = transactions;
|
|
556
|
+
await promiseAllSettledWithThrow([
|
|
557
|
+
syncStore.insertBlocks({ blocks: [block], chainId: args.chain.id }),
|
|
558
|
+
syncStore.insertTransactions({
|
|
559
|
+
transactions,
|
|
560
|
+
chainId: args.chain.id,
|
|
561
|
+
}),
|
|
562
|
+
syncStore.insertTransactionReceipts({
|
|
563
|
+
transactionReceipts,
|
|
564
|
+
chainId: args.chain.id,
|
|
565
|
+
}),
|
|
566
|
+
syncStore.insertTraces({
|
|
567
|
+
traces: traces.map((trace) => ({
|
|
568
|
+
trace,
|
|
569
|
+
block: block,
|
|
570
|
+
transaction: transactionsByHash.get(trace.transactionHash),
|
|
571
|
+
})),
|
|
572
|
+
chainId: args.chain.id,
|
|
573
|
+
}),
|
|
574
|
+
syncStore.insertLogs({ logs, chainId: args.chain.id }),
|
|
575
|
+
]);
|
|
576
|
+
};
|
|
577
|
+
let blockCount = 0;
|
|
578
|
+
let transactionCount = 0;
|
|
579
|
+
let receiptCount = 0;
|
|
580
|
+
let traceCount = 0;
|
|
581
|
+
// Same memory usage as `sync-realtime`.
|
|
582
|
+
const MAX_BLOCKS_IN_MEM = Math.max(args.chain.finalityBlockCount * 2, 100);
|
|
583
|
+
if (requiredIntervals.length > 0) {
|
|
584
|
+
const queue = createQueue({
|
|
585
|
+
browser: false,
|
|
586
|
+
initialStart: true,
|
|
587
|
+
concurrency: MAX_BLOCKS_IN_MEM,
|
|
588
|
+
worker: syncBlockData,
|
|
589
|
+
});
|
|
590
|
+
await Promise.all(intervalRange(interval).map((blockNumber) => queue.add(blockNumber)));
|
|
624
591
|
}
|
|
625
592
|
args.common.logger.debug({
|
|
626
593
|
msg: "Fetched block data",
|
|
627
594
|
chain: args.chain.name,
|
|
628
595
|
chain_id: args.chain.id,
|
|
629
|
-
block_range: JSON.stringify(
|
|
630
|
-
block_count:
|
|
631
|
-
transaction_count:
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
trace_count: traceCache.size,
|
|
635
|
-
child_address_count: childAddressCount,
|
|
596
|
+
block_range: JSON.stringify(interval),
|
|
597
|
+
block_count: blockCount,
|
|
598
|
+
transaction_count: transactionCount,
|
|
599
|
+
receipt_count: receiptCount,
|
|
600
|
+
trace_count: traceCount,
|
|
636
601
|
duration: endClock(),
|
|
637
602
|
}, ["chain", "block_range"]);
|
|
638
|
-
|
|
639
|
-
traceCache.clear();
|
|
640
|
-
transactionsCache.clear();
|
|
641
|
-
blockReceiptsCache.clear();
|
|
642
|
-
transactionReceiptsCache.clear();
|
|
643
|
-
childAddressesCache.clear();
|
|
644
|
-
return latestBlock;
|
|
603
|
+
return closestToTipBlock;
|
|
645
604
|
},
|
|
646
605
|
};
|
|
647
606
|
};
|