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.
Files changed (237) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/esm/bin/commands/createViews.js +28 -11
  3. package/dist/esm/bin/commands/createViews.js.map +1 -1
  4. package/dist/esm/bin/commands/dev.js +42 -22
  5. package/dist/esm/bin/commands/dev.js.map +1 -1
  6. package/dist/esm/bin/commands/prune.js +3 -0
  7. package/dist/esm/bin/commands/prune.js.map +1 -1
  8. package/dist/esm/bin/commands/serve.js +4 -1
  9. package/dist/esm/bin/commands/serve.js.map +1 -1
  10. package/dist/esm/bin/commands/start.js +18 -6
  11. package/dist/esm/bin/commands/start.js.map +1 -1
  12. package/dist/esm/bin/isolatedController.js +200 -0
  13. package/dist/esm/bin/isolatedController.js.map +1 -0
  14. package/dist/esm/bin/isolatedWorker.js +146 -0
  15. package/dist/esm/bin/isolatedWorker.js.map +1 -0
  16. package/dist/esm/build/config.js +322 -402
  17. package/dist/esm/build/config.js.map +1 -1
  18. package/dist/esm/build/index.js +8 -11
  19. package/dist/esm/build/index.js.map +1 -1
  20. package/dist/esm/build/pre.js +1 -4
  21. package/dist/esm/build/pre.js.map +1 -1
  22. package/dist/esm/build/schema.js +25 -3
  23. package/dist/esm/build/schema.js.map +1 -1
  24. package/dist/esm/client/index.js +306 -42
  25. package/dist/esm/client/index.js.map +1 -1
  26. package/dist/esm/database/actions.js +264 -104
  27. package/dist/esm/database/actions.js.map +1 -1
  28. package/dist/esm/database/index.js +39 -33
  29. package/dist/esm/database/index.js.map +1 -1
  30. package/dist/esm/database/queryBuilder.js +1 -0
  31. package/dist/esm/database/queryBuilder.js.map +1 -1
  32. package/dist/esm/drizzle/index.js +11 -7
  33. package/dist/esm/drizzle/index.js.map +1 -1
  34. package/dist/esm/drizzle/onchain.js +18 -0
  35. package/dist/esm/drizzle/onchain.js.map +1 -1
  36. package/dist/esm/indexing/client.js +32 -25
  37. package/dist/esm/indexing/client.js.map +1 -1
  38. package/dist/esm/indexing/index.js +110 -178
  39. package/dist/esm/indexing/index.js.map +1 -1
  40. package/dist/esm/indexing/profile.js +1 -1
  41. package/dist/esm/indexing/profile.js.map +1 -1
  42. package/dist/esm/indexing-store/cache.js +196 -274
  43. package/dist/esm/indexing-store/cache.js.map +1 -1
  44. package/dist/esm/indexing-store/historical.js +17 -13
  45. package/dist/esm/indexing-store/historical.js.map +1 -1
  46. package/dist/esm/indexing-store/index.js +10 -1
  47. package/dist/esm/indexing-store/index.js.map +1 -1
  48. package/dist/esm/indexing-store/profile.js +3 -3
  49. package/dist/esm/indexing-store/profile.js.map +1 -1
  50. package/dist/esm/indexing-store/realtime.js +27 -2
  51. package/dist/esm/indexing-store/realtime.js.map +1 -1
  52. package/dist/esm/internal/errors.js +28 -0
  53. package/dist/esm/internal/errors.js.map +1 -1
  54. package/dist/esm/internal/metrics.js +279 -82
  55. package/dist/esm/internal/metrics.js.map +1 -1
  56. package/dist/esm/internal/options.js +1 -0
  57. package/dist/esm/internal/options.js.map +1 -1
  58. package/dist/esm/internal/telemetry.js +1 -1
  59. package/dist/esm/internal/telemetry.js.map +1 -1
  60. package/dist/esm/rpc/http.js +130 -0
  61. package/dist/esm/rpc/http.js.map +1 -0
  62. package/dist/esm/rpc/index.js +38 -7
  63. package/dist/esm/rpc/index.js.map +1 -1
  64. package/dist/esm/runtime/events.js +179 -212
  65. package/dist/esm/runtime/events.js.map +1 -1
  66. package/dist/esm/runtime/filter.js +71 -0
  67. package/dist/esm/runtime/filter.js.map +1 -1
  68. package/dist/esm/runtime/fragments.js +78 -73
  69. package/dist/esm/runtime/fragments.js.map +1 -1
  70. package/dist/esm/runtime/historical.js +306 -130
  71. package/dist/esm/runtime/historical.js.map +1 -1
  72. package/dist/esm/runtime/index.js +183 -58
  73. package/dist/esm/runtime/index.js.map +1 -1
  74. package/dist/esm/runtime/isolated.js +462 -0
  75. package/dist/esm/runtime/isolated.js.map +1 -0
  76. package/dist/esm/runtime/multichain.js +80 -73
  77. package/dist/esm/runtime/multichain.js.map +1 -1
  78. package/dist/esm/runtime/omnichain.js +82 -75
  79. package/dist/esm/runtime/omnichain.js.map +1 -1
  80. package/dist/esm/runtime/realtime.js +198 -66
  81. package/dist/esm/runtime/realtime.js.map +1 -1
  82. package/dist/esm/sync-historical/index.js +416 -457
  83. package/dist/esm/sync-historical/index.js.map +1 -1
  84. package/dist/esm/sync-realtime/bloom.js +3 -3
  85. package/dist/esm/sync-realtime/bloom.js.map +1 -1
  86. package/dist/esm/sync-realtime/index.js +27 -46
  87. package/dist/esm/sync-realtime/index.js.map +1 -1
  88. package/dist/esm/sync-store/index.js +112 -63
  89. package/dist/esm/sync-store/index.js.map +1 -1
  90. package/dist/esm/utils/abi.js +20 -32
  91. package/dist/esm/utils/abi.js.map +1 -1
  92. package/dist/esm/utils/chunk.js +8 -0
  93. package/dist/esm/utils/chunk.js.map +1 -0
  94. package/dist/esm/utils/promiseAllSettledWithThrow.js +19 -0
  95. package/dist/esm/utils/promiseAllSettledWithThrow.js.map +1 -0
  96. package/dist/esm/{client/parse.js → utils/sql-parse.js} +94 -80
  97. package/dist/esm/utils/sql-parse.js.map +1 -0
  98. package/dist/types/bin/commands/createViews.d.ts.map +1 -1
  99. package/dist/types/bin/commands/dev.d.ts.map +1 -1
  100. package/dist/types/bin/commands/prune.d.ts.map +1 -1
  101. package/dist/types/bin/commands/serve.d.ts.map +1 -1
  102. package/dist/types/bin/commands/start.d.ts.map +1 -1
  103. package/dist/types/bin/isolatedController.d.ts +13 -0
  104. package/dist/types/bin/isolatedController.d.ts.map +1 -0
  105. package/dist/types/bin/isolatedWorker.d.ts +9 -0
  106. package/dist/types/bin/isolatedWorker.d.ts.map +1 -0
  107. package/dist/types/build/config.d.ts +29 -11
  108. package/dist/types/build/config.d.ts.map +1 -1
  109. package/dist/types/build/index.d.ts +3 -2
  110. package/dist/types/build/index.d.ts.map +1 -1
  111. package/dist/types/build/pre.d.ts +1 -1
  112. package/dist/types/build/pre.d.ts.map +1 -1
  113. package/dist/types/build/schema.d.ts +5 -3
  114. package/dist/types/build/schema.d.ts.map +1 -1
  115. package/dist/types/client/index.d.ts +1 -1
  116. package/dist/types/client/index.d.ts.map +1 -1
  117. package/dist/types/config/index.d.ts +3 -3
  118. package/dist/types/config/index.d.ts.map +1 -1
  119. package/dist/types/database/actions.d.ts +53 -7
  120. package/dist/types/database/actions.d.ts.map +1 -1
  121. package/dist/types/database/index.d.ts +21 -21
  122. package/dist/types/database/index.d.ts.map +1 -1
  123. package/dist/types/database/queryBuilder.d.ts.map +1 -1
  124. package/dist/types/drizzle/index.d.ts +4 -5
  125. package/dist/types/drizzle/index.d.ts.map +1 -1
  126. package/dist/types/drizzle/onchain.d.ts +6 -0
  127. package/dist/types/drizzle/onchain.d.ts.map +1 -1
  128. package/dist/types/indexing/client.d.ts.map +1 -1
  129. package/dist/types/indexing/index.d.ts +2 -5
  130. package/dist/types/indexing/index.d.ts.map +1 -1
  131. package/dist/types/indexing-store/cache.d.ts +3 -2
  132. package/dist/types/indexing-store/cache.d.ts.map +1 -1
  133. package/dist/types/indexing-store/historical.d.ts +2 -1
  134. package/dist/types/indexing-store/historical.d.ts.map +1 -1
  135. package/dist/types/indexing-store/index.d.ts +1 -0
  136. package/dist/types/indexing-store/index.d.ts.map +1 -1
  137. package/dist/types/indexing-store/realtime.d.ts +2 -1
  138. package/dist/types/indexing-store/realtime.d.ts.map +1 -1
  139. package/dist/types/internal/errors.d.ts +5 -0
  140. package/dist/types/internal/errors.d.ts.map +1 -1
  141. package/dist/types/internal/metrics.d.ts +21 -0
  142. package/dist/types/internal/metrics.d.ts.map +1 -1
  143. package/dist/types/internal/options.d.ts +2 -0
  144. package/dist/types/internal/options.d.ts.map +1 -1
  145. package/dist/types/internal/types.d.ts +66 -58
  146. package/dist/types/internal/types.d.ts.map +1 -1
  147. package/dist/types/rpc/http.d.ts +17 -0
  148. package/dist/types/rpc/http.d.ts.map +1 -0
  149. package/dist/types/rpc/index.d.ts.map +1 -1
  150. package/dist/types/runtime/events.d.ts +4 -4
  151. package/dist/types/runtime/events.d.ts.map +1 -1
  152. package/dist/types/runtime/filter.d.ts +5 -1
  153. package/dist/types/runtime/filter.d.ts.map +1 -1
  154. package/dist/types/runtime/fragments.d.ts +4 -3
  155. package/dist/types/runtime/fragments.d.ts.map +1 -1
  156. package/dist/types/runtime/historical.d.ts +29 -13
  157. package/dist/types/runtime/historical.d.ts.map +1 -1
  158. package/dist/types/runtime/index.d.ts +49 -6
  159. package/dist/types/runtime/index.d.ts.map +1 -1
  160. package/dist/types/runtime/init.d.ts +5 -5
  161. package/dist/types/runtime/init.d.ts.map +1 -1
  162. package/dist/types/runtime/isolated.d.ts +14 -0
  163. package/dist/types/runtime/isolated.d.ts.map +1 -0
  164. package/dist/types/runtime/multichain.d.ts.map +1 -1
  165. package/dist/types/runtime/omnichain.d.ts.map +1 -1
  166. package/dist/types/runtime/realtime.d.ts +21 -10
  167. package/dist/types/runtime/realtime.d.ts.map +1 -1
  168. package/dist/types/sync-historical/index.d.ts +18 -8
  169. package/dist/types/sync-historical/index.d.ts.map +1 -1
  170. package/dist/types/sync-realtime/bloom.d.ts.map +1 -1
  171. package/dist/types/sync-realtime/index.d.ts +2 -2
  172. package/dist/types/sync-realtime/index.d.ts.map +1 -1
  173. package/dist/types/sync-store/index.d.ts +9 -9
  174. package/dist/types/sync-store/index.d.ts.map +1 -1
  175. package/dist/types/utils/abi.d.ts +3 -34
  176. package/dist/types/utils/abi.d.ts.map +1 -1
  177. package/dist/types/utils/chunk.d.ts +2 -0
  178. package/dist/types/utils/chunk.d.ts.map +1 -0
  179. package/dist/types/utils/promiseAllSettledWithThrow.d.ts +8 -0
  180. package/dist/types/utils/promiseAllSettledWithThrow.d.ts.map +1 -0
  181. package/dist/types/utils/sql-parse.d.ts +21 -0
  182. package/dist/types/utils/sql-parse.d.ts.map +1 -0
  183. package/package.json +2 -2
  184. package/src/bin/commands/createViews.ts +35 -15
  185. package/src/bin/commands/dev.ts +43 -21
  186. package/src/bin/commands/prune.ts +6 -0
  187. package/src/bin/commands/serve.ts +4 -1
  188. package/src/bin/commands/start.ts +20 -5
  189. package/src/bin/isolatedController.ts +300 -0
  190. package/src/bin/isolatedWorker.ts +192 -0
  191. package/src/build/config.ts +570 -632
  192. package/src/build/index.ts +14 -14
  193. package/src/build/pre.ts +1 -4
  194. package/src/build/schema.ts +49 -4
  195. package/src/client/index.ts +386 -48
  196. package/src/config/index.ts +3 -3
  197. package/src/database/actions.ts +469 -120
  198. package/src/database/index.ts +85 -58
  199. package/src/database/queryBuilder.ts +1 -0
  200. package/src/drizzle/index.ts +15 -7
  201. package/src/drizzle/onchain.ts +19 -0
  202. package/src/indexing/client.ts +38 -25
  203. package/src/indexing/index.ts +137 -230
  204. package/src/indexing/profile.ts +1 -1
  205. package/src/indexing-store/cache.ts +285 -414
  206. package/src/indexing-store/historical.ts +20 -10
  207. package/src/indexing-store/index.ts +16 -0
  208. package/src/indexing-store/profile.ts +3 -3
  209. package/src/indexing-store/realtime.ts +28 -0
  210. package/src/internal/errors.ts +26 -0
  211. package/src/internal/metrics.ts +341 -111
  212. package/src/internal/options.ts +4 -0
  213. package/src/internal/telemetry.ts +1 -1
  214. package/src/internal/types.ts +70 -87
  215. package/src/rpc/http.ts +164 -0
  216. package/src/rpc/index.ts +39 -7
  217. package/src/runtime/events.ts +195 -240
  218. package/src/runtime/filter.ts +85 -1
  219. package/src/runtime/fragments.ts +109 -113
  220. package/src/runtime/historical.ts +467 -189
  221. package/src/runtime/index.ts +337 -69
  222. package/src/runtime/init.ts +5 -5
  223. package/src/runtime/isolated.ts +768 -0
  224. package/src/runtime/multichain.ts +137 -102
  225. package/src/runtime/omnichain.ts +138 -106
  226. package/src/runtime/realtime.ts +322 -123
  227. package/src/sync-historical/index.ts +556 -692
  228. package/src/sync-realtime/bloom.ts +7 -3
  229. package/src/sync-realtime/index.ts +31 -46
  230. package/src/sync-store/index.ts +189 -95
  231. package/src/utils/abi.ts +33 -90
  232. package/src/utils/chunk.ts +7 -0
  233. package/src/utils/promiseAllSettledWithThrow.ts +27 -0
  234. package/src/{client/parse.ts → utils/sql-parse.ts} +100 -90
  235. package/dist/esm/client/parse.js.map +0 -1
  236. package/dist/types/client/parse.d.ts +0 -14
  237. package/dist/types/client/parse.d.ts.map +0 -1
@@ -1,15 +1,18 @@
1
1
  import { ShutdownError } from '../internal/errors.js';
2
2
  import { _eth_getBlockByNumber } from '../rpc/actions.js';
3
3
  import { buildEvents, decodeEvents } from './events.js';
4
- import { isAddressFactory } from './filter.js';
5
4
  import { createHistoricalSync } from '../sync-historical/index.js';
5
+ import { createSyncStore } from '../sync-store/index.js';
6
6
  import { MAX_CHECKPOINT, ZERO_CHECKPOINT, decodeCheckpoint, encodeCheckpoint, min, } from '../utils/checkpoint.js';
7
+ import { estimate } from '../utils/estimate.js';
7
8
  import { formatPercentage } from '../utils/format.js';
8
- import { bufferAsyncGenerator, mergeAsyncGenerators, } from '../utils/generators.js';
9
- import { intervalDifference, intervalIntersectionMany, intervalSum, intervalUnion, } from '../utils/interval.js';
9
+ import { bufferAsyncGenerator, createCallbackGenerator, mergeAsyncGenerators, } from '../utils/generators.js';
10
+ import { intervalSum } from '../utils/interval.js';
10
11
  import { partition } from '../utils/partition.js';
12
+ import { promiseWithResolvers } from '../utils/promiseWithResolvers.js';
11
13
  import { startClock } from '../utils/timer.js';
12
14
  import { hexToNumber } from "viem";
15
+ import { getRequiredIntervals, getRequiredIntervalsWithFilters, } from "./index.js";
13
16
  import { initEventGenerator, initRefetchEvents } from "./init.js";
14
17
  import { getOmnichainCheckpoint } from "./omnichain.js";
15
18
  export async function* getHistoricalEventsOmnichain(params) {
@@ -19,7 +22,7 @@ export async function* getHistoricalEventsOmnichain(params) {
19
22
  while (true) {
20
23
  const eventGenerators = Array.from(params.perChainSync.entries()).map(async function* ([chain, { syncProgress, childAddresses, cachedIntervals },]) {
21
24
  const rpc = params.indexingBuild.rpcs[params.indexingBuild.chains.findIndex((c) => c.id === chain.id)];
22
- const sources = params.indexingBuild.sources.filter(({ filter }) => filter.chainId === chain.id);
25
+ const eventCallbacks = params.indexingBuild.eventCallbacks[params.indexingBuild.chains.findIndex((c) => c.id === chain.id)];
23
26
  const crashRecoveryCheckpoint = params.crashRecoveryCheckpoint?.find(({ chainId }) => chainId === chain.id)?.checkpoint;
24
27
  const to = min(syncProgress.getCheckpoint({ tag: "finalized" }), syncProgress.getCheckpoint({ tag: "end" }));
25
28
  const omnichainTo = min(getOmnichainCheckpoint({
@@ -42,7 +45,10 @@ export async function* getHistoricalEventsOmnichain(params) {
42
45
  from = crashRecoveryCheckpoint;
43
46
  }
44
47
  else {
45
- const fromBlock = await params.syncStore.getSafeCrashRecoveryBlock({
48
+ const fromBlock = await createSyncStore({
49
+ common: params.common,
50
+ qb: params.database.syncQB,
51
+ }).getSafeCrashRecoveryBlock({
46
52
  chainId: chain.id,
47
53
  timestamp: Number(decodeCheckpoint(crashRecoveryCheckpoint).blockTimestamp),
48
54
  });
@@ -63,8 +69,8 @@ export async function* getHistoricalEventsOmnichain(params) {
63
69
  const cursor = perChainCursor.get(chain);
64
70
  // Yield pending events from previous iterations. Note that it is possible to
65
71
  // previous pending events to still be pending after the catchup.
66
- const events = pendingEvents.filter((event) => event.chainId === chain.id && event.checkpoint <= omnichainTo);
67
- pendingEvents = pendingEvents.filter((event) => (event.chainId === chain.id &&
72
+ const events = pendingEvents.filter((event) => event.chain.id === chain.id && event.checkpoint <= omnichainTo);
73
+ pendingEvents = pendingEvents.filter((event) => (event.chain.id === chain.id &&
68
74
  event.checkpoint <= omnichainTo) === false);
69
75
  const blockRange = [
70
76
  Number(decodeCheckpoint(cursor).blockNumber),
@@ -97,7 +103,7 @@ export async function* getHistoricalEventsOmnichain(params) {
97
103
  indexingBuild: params.indexingBuild,
98
104
  chain,
99
105
  rpc,
100
- sources,
106
+ eventCallbacks,
101
107
  childAddresses,
102
108
  syncProgress,
103
109
  cachedIntervals,
@@ -105,12 +111,12 @@ export async function* getHistoricalEventsOmnichain(params) {
105
111
  to,
106
112
  limit: Math.round(params.common.options.syncEventsQuerySize /
107
113
  (params.indexingBuild.chains.length + 1)) + 6,
108
- syncStore: params.syncStore,
114
+ database: params.database,
109
115
  isCatchup,
110
116
  });
111
117
  for await (let { events: rawEvents, checkpoint, blockRange, } of eventGenerator) {
112
118
  const endClock = startClock();
113
- let events = decodeEvents(params.common, sources, rawEvents);
119
+ let events = decodeEvents(params.common, chain, eventCallbacks, rawEvents);
114
120
  params.common.logger.trace({
115
121
  msg: "Decoded events",
116
122
  chain: chain.name,
@@ -218,7 +224,7 @@ export async function* getHistoricalEventsMultichain(params) {
218
224
  while (true) {
219
225
  const eventGenerators = Array.from(params.perChainSync.entries()).map(async function* ([chain, { syncProgress, childAddresses, cachedIntervals },]) {
220
226
  const rpc = params.indexingBuild.rpcs[params.indexingBuild.chains.findIndex((c) => c.id === chain.id)];
221
- const sources = params.indexingBuild.sources.filter(({ filter }) => filter.chainId === chain.id);
227
+ const eventCallbacks = params.indexingBuild.eventCallbacks[params.indexingBuild.chains.findIndex((c) => c.id === chain.id)];
222
228
  const crashRecoveryCheckpoint = params.crashRecoveryCheckpoint?.find(({ chainId }) => chainId === chain.id)?.checkpoint;
223
229
  const to = min(syncProgress.getCheckpoint({ tag: "finalized" }), syncProgress.getCheckpoint({ tag: "end" }));
224
230
  let from;
@@ -234,7 +240,10 @@ export async function* getHistoricalEventsMultichain(params) {
234
240
  from = crashRecoveryCheckpoint;
235
241
  }
236
242
  else {
237
- const fromBlock = await params.syncStore.getSafeCrashRecoveryBlock({
243
+ const fromBlock = await createSyncStore({
244
+ common: params.common,
245
+ qb: params.database.syncQB,
246
+ }).getSafeCrashRecoveryBlock({
238
247
  chainId: chain.id,
239
248
  timestamp: Number(decodeCheckpoint(crashRecoveryCheckpoint).blockTimestamp),
240
249
  });
@@ -276,7 +285,7 @@ export async function* getHistoricalEventsMultichain(params) {
276
285
  indexingBuild: params.indexingBuild,
277
286
  chain,
278
287
  rpc,
279
- sources,
288
+ eventCallbacks,
280
289
  childAddresses,
281
290
  syncProgress,
282
291
  cachedIntervals,
@@ -284,12 +293,12 @@ export async function* getHistoricalEventsMultichain(params) {
284
293
  to,
285
294
  limit: Math.round(params.common.options.syncEventsQuerySize /
286
295
  (params.indexingBuild.chains.length + 1)) + 6,
287
- syncStore: params.syncStore,
296
+ database: params.database,
288
297
  isCatchup,
289
298
  });
290
299
  for await (const { events: rawEvents, checkpoint, blockRange, } of eventGenerator) {
291
300
  const endClock = startClock();
292
- let events = decodeEvents(params.common, sources, rawEvents);
301
+ let events = decodeEvents(params.common, chain, eventCallbacks, rawEvents);
293
302
  params.common.logger.trace({
294
303
  msg: "Decoded events",
295
304
  chain: chain.name,
@@ -367,25 +376,142 @@ export async function* getHistoricalEventsMultichain(params) {
367
376
  isCatchup = true;
368
377
  }
369
378
  }
379
+ export async function* getHistoricalEventsIsolated(params) {
380
+ let isCatchup = false;
381
+ let lastUnfinalizedRefetch = Date.now();
382
+ let cursor;
383
+ while (true) {
384
+ const rpc = params.indexingBuild.rpcs[params.indexingBuild.chains.findIndex((c) => c.id === params.chain.id)];
385
+ const eventCallbacks = params.indexingBuild.eventCallbacks[params.indexingBuild.chains.findIndex((c) => c.id === params.chain.id)];
386
+ const crashRecoveryCheckpoint = params.crashRecoveryCheckpoint?.find(({ chainId }) => chainId === params.chain.id)?.checkpoint;
387
+ const to = min(params.syncProgress.getCheckpoint({ tag: "finalized" }), params.syncProgress.getCheckpoint({ tag: "end" }));
388
+ let from;
389
+ if (isCatchup === false) {
390
+ // In order to speed up the "extract" phase when there is a crash recovery,
391
+ // the beginning cursor is moved forwards. This only works when `crashRecoveryCheckpoint`
392
+ // is defined.
393
+ if (crashRecoveryCheckpoint === undefined) {
394
+ from = params.syncProgress.getCheckpoint({ tag: "start" });
395
+ }
396
+ else if (Number(decodeCheckpoint(crashRecoveryCheckpoint).chainId) ===
397
+ params.chain.id) {
398
+ from = crashRecoveryCheckpoint;
399
+ }
400
+ else {
401
+ from = params.syncProgress.getCheckpoint({ tag: "start" });
402
+ }
403
+ }
404
+ else {
405
+ from = encodeCheckpoint({
406
+ ...ZERO_CHECKPOINT,
407
+ blockTimestamp: decodeCheckpoint(cursor).blockTimestamp,
408
+ chainId: decodeCheckpoint(cursor).chainId,
409
+ blockNumber: decodeCheckpoint(cursor).blockNumber + 1n,
410
+ });
411
+ if (from > to)
412
+ return;
413
+ }
414
+ params.common.logger.info({
415
+ msg: "Started backfill indexing",
416
+ chain: params.chain.name,
417
+ chain_id: params.chain.id,
418
+ block_range: JSON.stringify([
419
+ Number(decodeCheckpoint(from).blockNumber),
420
+ Number(decodeCheckpoint(to).blockNumber),
421
+ ]),
422
+ });
423
+ const eventGenerator = await initEventGenerator({
424
+ common: params.common,
425
+ indexingBuild: params.indexingBuild,
426
+ chain: params.chain,
427
+ rpc,
428
+ eventCallbacks,
429
+ childAddresses: params.childAddresses,
430
+ syncProgress: params.syncProgress,
431
+ cachedIntervals: params.cachedIntervals,
432
+ from,
433
+ to,
434
+ limit: Math.round(params.common.options.syncEventsQuerySize /
435
+ (params.indexingBuild.chains.length + 1)) + 6,
436
+ database: params.database,
437
+ isCatchup,
438
+ });
439
+ for await (const { events: rawEvents, checkpoint, blockRange, } of eventGenerator) {
440
+ const endClock = startClock();
441
+ let events = decodeEvents(params.common, params.chain, eventCallbacks, rawEvents);
442
+ params.common.logger.trace({
443
+ msg: "Decoded events",
444
+ chain: params.chain.name,
445
+ chain_id: params.chain.id,
446
+ event_count: events.length,
447
+ duration: endClock(),
448
+ });
449
+ params.common.metrics.ponder_historical_extract_duration.inc({ step: "decode" }, endClock());
450
+ // Removes events that have a checkpoint earlier than (or equal to)
451
+ // the crash recovery checkpoint.
452
+ if (crashRecoveryCheckpoint) {
453
+ const [left, right] = partition(events, (event) => event.checkpoint <= crashRecoveryCheckpoint);
454
+ events = right;
455
+ if (left.length > 0) {
456
+ params.common.logger.trace({
457
+ msg: "Filtered events before crash recovery checkpoint",
458
+ chain: params.chain.name,
459
+ chain_id: params.chain.id,
460
+ event_count: left.length,
461
+ checkpoint: crashRecoveryCheckpoint,
462
+ });
463
+ }
464
+ }
465
+ yield { chainId: params.chain.id, events, checkpoint, blockRange };
466
+ }
467
+ cursor = to;
468
+ if (Date.now() - lastUnfinalizedRefetch < 30000) {
469
+ break;
470
+ }
471
+ lastUnfinalizedRefetch = Date.now();
472
+ const context = {
473
+ logger: params.common.logger.child({ action: "refetch_finalized_block" }),
474
+ };
475
+ const endClock = startClock();
476
+ const finalizedBlock = await _eth_getBlockByNumber(rpc, { blockTag: "latest" }, context).then((latest) => _eth_getBlockByNumber(rpc, {
477
+ blockNumber: Math.max(hexToNumber(latest.number) - params.chain.finalityBlockCount, 0),
478
+ }, context));
479
+ const finalizedBlockNumber = hexToNumber(finalizedBlock.number);
480
+ params.common.logger.debug({
481
+ msg: "Refetched finalized block for backfill cutover",
482
+ chain: params.chain.name,
483
+ chain_id: params.chain.id,
484
+ finalized_block: finalizedBlockNumber,
485
+ duration: endClock(),
486
+ });
487
+ if (hexToNumber(finalizedBlock.number) -
488
+ hexToNumber(params.syncProgress.finalized.number) <=
489
+ params.chain.finalityBlockCount) {
490
+ break;
491
+ }
492
+ params.syncProgress.finalized = finalizedBlock;
493
+ isCatchup = true;
494
+ }
495
+ }
370
496
  export async function refetchHistoricalEvents(params) {
371
497
  const events = new Array(params.events.length);
372
- for (const chain of PONDER_INDEXING_BUILD.chains) {
498
+ for (const chain of params.indexingBuild.chains) {
373
499
  const { childAddresses } = params.perChainSync.get(chain);
374
500
  // Note: All filters are refetched, no matter if they are resolved or not.
375
- const sources = params.indexingBuild.sources.filter(({ filter }) => filter.chainId === chain.id);
376
- const chainEvents = params.events.filter((event) => event.chainId === chain.id);
501
+ const eventCallbacks = params.indexingBuild.eventCallbacks[params.indexingBuild.chains.findIndex((c) => c.id === chain.id)];
502
+ const chainEvents = params.events.filter((event) => event.chain.id === chain.id);
377
503
  if (chainEvents.length === 0)
378
504
  continue;
379
505
  const rawEvents = await initRefetchEvents({
380
506
  common: params.common,
381
507
  chain,
382
508
  childAddresses,
383
- sources,
509
+ eventCallbacks,
384
510
  events: chainEvents,
385
511
  syncStore: params.syncStore,
386
512
  });
387
513
  const endClock = startClock();
388
- const refetchedEvents = decodeEvents(params.common, sources, rawEvents);
514
+ const refetchedEvents = decodeEvents(params.common, chain, eventCallbacks, rawEvents);
389
515
  params.common.logger.trace({
390
516
  msg: "Decoded events",
391
517
  chain: chain.name,
@@ -397,7 +523,7 @@ export async function refetchHistoricalEvents(params) {
397
523
  let i = 0;
398
524
  let j = 0;
399
525
  while (i < params.events.length && j < refetchedEvents.length) {
400
- if (params.events[i]?.chainId === chain.id) {
526
+ if (params.events[i]?.chain.id === chain.id) {
401
527
  events[i] = refetchedEvents[j];
402
528
  i++;
403
529
  j++;
@@ -419,7 +545,7 @@ export async function refetchLocalEvents(params) {
419
545
  while (cursor <= toBlock) {
420
546
  const queryEndClock = startClock();
421
547
  const { blocks, logs, transactions, transactionReceipts, traces, cursor: queryCursor, } = await params.syncStore.getEventData({
422
- filters: params.sources.map(({ filter }) => filter),
548
+ filters: params.eventCallbacks.map(({ filter }) => filter),
423
549
  fromBlock: cursor,
424
550
  toBlock,
425
551
  chainId: params.chain.id,
@@ -427,7 +553,7 @@ export async function refetchLocalEvents(params) {
427
553
  });
428
554
  const endClock = startClock();
429
555
  const rawEvents = buildEvents({
430
- sources: params.sources,
556
+ eventCallbacks: params.eventCallbacks,
431
557
  blocks,
432
558
  logs,
433
559
  transactions,
@@ -465,6 +591,10 @@ export async function refetchLocalEvents(params) {
465
591
  return events;
466
592
  }
467
593
  export async function* getLocalEventGenerator(params) {
594
+ const syncStore = createSyncStore({
595
+ common: params.common,
596
+ qb: params.database.syncQB,
597
+ });
468
598
  const fromBlock = Number(decodeCheckpoint(params.from).blockNumber);
469
599
  const toBlock = Number(decodeCheckpoint(params.to).blockNumber);
470
600
  let cursor = fromBlock;
@@ -472,8 +602,8 @@ export async function* getLocalEventGenerator(params) {
472
602
  for await (const syncCursor of bufferAsyncGenerator(localSyncGenerator, Number.POSITIVE_INFINITY)) {
473
603
  while (cursor <= Math.min(syncCursor, toBlock)) {
474
604
  const queryEndClock = startClock();
475
- const { blocks, logs, transactions, transactionReceipts, traces, cursor: queryCursor, } = await params.syncStore.getEventData({
476
- filters: params.sources.map(({ filter }) => filter),
605
+ const { blocks, logs, transactions, transactionReceipts, traces, cursor: queryCursor, } = await syncStore.getEventData({
606
+ filters: params.eventCallbacks.map(({ filter }) => filter),
477
607
  fromBlock: cursor,
478
608
  toBlock: Math.min(syncCursor, toBlock),
479
609
  chainId: params.chain.id,
@@ -481,7 +611,7 @@ export async function* getLocalEventGenerator(params) {
481
611
  });
482
612
  const endClock = startClock();
483
613
  const events = buildEvents({
484
- sources: params.sources,
614
+ eventCallbacks: params.eventCallbacks,
485
615
  blocks,
486
616
  logs,
487
617
  transactions,
@@ -528,7 +658,7 @@ export async function* getLocalEventGenerator(params) {
528
658
  export async function* getLocalSyncGenerator(params) {
529
659
  const backfillEndClock = startClock();
530
660
  const label = { chain: params.chain.name };
531
- let cursor = hexToNumber(params.syncProgress.start.number);
661
+ let first = hexToNumber(params.syncProgress.start.number);
532
662
  const last = params.syncProgress.end === undefined
533
663
  ? params.syncProgress.finalized
534
664
  : hexToNumber(params.syncProgress.end.number) >
@@ -561,41 +691,12 @@ export async function* getLocalSyncGenerator(params) {
561
691
  hexToNumber(params.syncProgress.start.number),
562
692
  hexToNumber(last.number),
563
693
  ];
564
- const requiredIntervals = Array.from(params.cachedIntervals.entries()).flatMap(([filter, fragmentIntervals]) => {
565
- const filterIntervals = [
566
- [
567
- filter.fromBlock ?? 0,
568
- Math.min(filter.toBlock ?? Number.POSITIVE_INFINITY, totalInterval[1]),
569
- ],
570
- ];
571
- switch (filter.type) {
572
- case "log":
573
- if (isAddressFactory(filter.address)) {
574
- filterIntervals.push([
575
- filter.address.fromBlock ?? 0,
576
- Math.min(filter.address.toBlock ?? Number.POSITIVE_INFINITY, totalInterval[1]),
577
- ]);
578
- }
579
- break;
580
- case "trace":
581
- case "transaction":
582
- case "transfer":
583
- if (isAddressFactory(filter.fromAddress)) {
584
- filterIntervals.push([
585
- filter.fromAddress.fromBlock ?? 0,
586
- Math.min(filter.fromAddress.toBlock ?? Number.POSITIVE_INFINITY, totalInterval[1]),
587
- ]);
588
- }
589
- if (isAddressFactory(filter.toAddress)) {
590
- filterIntervals.push([
591
- filter.toAddress.fromBlock ?? 0,
592
- Math.min(filter.toAddress.toBlock ?? Number.POSITIVE_INFINITY, totalInterval[1]),
593
- ]);
594
- }
595
- }
596
- return intervalDifference(intervalUnion(filterIntervals), intervalIntersectionMany(fragmentIntervals.map(({ intervals }) => intervals)));
694
+ const requiredIntervals = getRequiredIntervals({
695
+ filters: params.eventCallbacks.map(({ filter }) => filter),
696
+ interval: totalInterval,
697
+ cachedIntervals: params.cachedIntervals,
597
698
  });
598
- const required = intervalSum(intervalUnion(requiredIntervals));
699
+ const required = intervalSum(requiredIntervals);
599
700
  const total = totalInterval[1] - totalInterval[0] + 1;
600
701
  params.common.metrics.ponder_historical_total_blocks.set(label, total);
601
702
  params.common.metrics.ponder_historical_cached_blocks.set(label, total - required);
@@ -627,7 +728,7 @@ export async function* getLocalSyncGenerator(params) {
627
728
  cache_rate: formatPercentage((total - required) / total),
628
729
  });
629
730
  }
630
- cursor = hexToNumber(params.syncProgress.current.number) + 1;
731
+ first = hexToNumber(params.syncProgress.current.number) + 1;
631
732
  }
632
733
  else {
633
734
  params.common.logger.info({
@@ -638,88 +739,163 @@ export async function* getLocalSyncGenerator(params) {
638
739
  });
639
740
  }
640
741
  const historicalSync = createHistoricalSync(params);
641
- while (true) {
642
- // Select a range of blocks to sync bounded by `finalizedBlock`.
643
- // It is important for devEx that the interval is not too large, because
644
- // time spent syncing ≈ time before indexing function feedback.
645
- const interval = [
646
- Math.min(cursor, hexToNumber(last.number)),
647
- Math.min(cursor + estimateRange, hexToNumber(last.number)),
648
- ];
742
+ const { callback: intervalCallback, generator: intervalGenerator } = createCallbackGenerator();
743
+ intervalCallback({
744
+ interval: [
745
+ first,
746
+ Math.min(first + estimateRange, hexToNumber(last.number)),
747
+ ],
748
+ promise: Promise.resolve(),
749
+ });
750
+ /**
751
+ * @returns `true` if any data was inserted into the database.
752
+ */
753
+ async function syncInterval({ interval, promise, }) {
649
754
  const endClock = startClock();
650
- const durationTimer = setTimeout(() => {
651
- params.common.logger.warn({
652
- msg: "Fetching backfill JSON-RPC data is taking longer than expected",
653
- chain: params.chain.name,
654
- chain_id: params.chain.id,
655
- block_range: JSON.stringify(interval),
656
- duration: endClock(),
657
- });
658
- }, 5000);
659
- let synced;
660
- try {
661
- synced = await historicalSync.sync(interval);
662
- }
663
- catch (error) {
664
- if (error instanceof ShutdownError) {
755
+ const isSyncComplete = interval[1] === hexToNumber(last.number);
756
+ const { intervals: requiredIntervals, factoryIntervals: requiredFactoryIntervals, } = getRequiredIntervalsWithFilters({
757
+ interval,
758
+ filters: params.eventCallbacks.map(({ filter }) => filter),
759
+ cachedIntervals: params.cachedIntervals,
760
+ });
761
+ let closestToTipBlock;
762
+ if (requiredIntervals.length > 0 || requiredFactoryIntervals.length > 0) {
763
+ const pwr = promiseWithResolvers();
764
+ const durationTimer = setTimeout(() => {
765
+ params.common.logger.warn({
766
+ msg: "Fetching backfill JSON-RPC data is taking longer than expected",
767
+ chain: params.chain.name,
768
+ chain_id: params.chain.id,
769
+ block_range: JSON.stringify(interval),
770
+ duration: endClock(),
771
+ });
772
+ }, params.common.options.command === "dev" ? 10000 : 50000);
773
+ closestToTipBlock = await params.database.syncQB
774
+ .transaction(async (tx) => {
775
+ const syncStore = createSyncStore({ common: params.common, qb: tx });
776
+ const logs = await historicalSync.syncBlockRangeData({
777
+ interval,
778
+ requiredIntervals,
779
+ requiredFactoryIntervals,
780
+ syncStore,
781
+ });
782
+ // Wait for the previous interval to complete `syncBlockData`.
783
+ await promise;
784
+ if (isSyncComplete === false) {
785
+ // Queue the next interval
786
+ intervalCallback({
787
+ interval: [
788
+ Math.min(interval[1] + 1, hexToNumber(last.number)),
789
+ Math.min(interval[1] + 1 + estimateRange, hexToNumber(last.number)),
790
+ ],
791
+ promise: pwr.promise,
792
+ });
793
+ }
794
+ const closestToTipBlock = await historicalSync.syncBlockData({
795
+ interval,
796
+ requiredIntervals,
797
+ logs,
798
+ syncStore,
799
+ });
800
+ if (params.chain.disableCache === false) {
801
+ await syncStore.insertIntervals({
802
+ intervals: requiredIntervals,
803
+ factoryIntervals: requiredFactoryIntervals,
804
+ chainId: params.chain.id,
805
+ });
806
+ }
807
+ return closestToTipBlock;
808
+ })
809
+ .catch((error) => {
810
+ if (error instanceof ShutdownError) {
811
+ throw error;
812
+ }
813
+ params.common.logger.warn({
814
+ msg: "Failed to fetch backfill JSON-RPC data",
815
+ chain: params.chain.name,
816
+ chain_id: params.chain.id,
817
+ block_range: JSON.stringify(interval),
818
+ duration: endClock(),
819
+ error,
820
+ });
665
821
  throw error;
666
- }
667
- params.common.logger.warn({
668
- msg: "Failed to fetch backfill JSON-RPC data",
669
- chain: params.chain.name,
670
- chain_id: params.chain.id,
671
- block_range: JSON.stringify(interval),
672
- duration: endClock(),
673
- error,
674
822
  });
675
- throw error;
676
- }
677
- clearTimeout(durationTimer);
678
- // Update cursor to record progress
679
- cursor = interval[1] + 1;
680
- // `synced` will be undefined if a cache hit occur in `historicalSync.sync()`.
681
- if (synced === undefined) {
682
- // If the all known blocks are synced, then update `syncProgress.current`, else
683
- // progress to the next iteration.
684
- if (interval[1] === hexToNumber(last.number)) {
685
- params.syncProgress.current = last;
686
- }
687
- else {
688
- continue;
689
- }
690
- }
691
- else {
692
- if (interval[1] === hexToNumber(last.number)) {
693
- params.syncProgress.current = last;
694
- }
695
- else {
696
- params.syncProgress.current = synced;
697
- }
823
+ clearTimeout(durationTimer);
698
824
  const duration = endClock();
699
- params.common.metrics.ponder_sync_block.set(label, hexToNumber(params.syncProgress.current.number));
700
- params.common.metrics.ponder_sync_block_timestamp.set(label, hexToNumber(params.syncProgress.current.timestamp));
701
- params.common.metrics.ponder_historical_completed_blocks.inc(label, interval[1] - interval[0] + 1);
702
825
  // Use the duration and interval of the last call to `sync` to update estimate
703
- // 25 <= estimate(new) <= estimate(prev) * 2 <= 100_000
704
- estimateRange = Math.min(Math.max(25, Math.round((1000 * (interval[1] - interval[0])) / duration)), estimateRange * 2, 100000);
826
+ estimateRange = estimate({
827
+ from: interval[0],
828
+ to: interval[1],
829
+ target: params.common.options.command === "dev" ? 2000 : 10000,
830
+ result: duration,
831
+ min: 25,
832
+ max: 100000,
833
+ prev: estimateRange,
834
+ maxIncrease: 1.5,
835
+ });
705
836
  params.common.logger.trace({
706
837
  msg: "Updated block range estimate for fetching backfill JSON-RPC data",
707
838
  chain: params.chain.name,
708
839
  chain_id: params.chain.id,
709
840
  range: estimateRange,
710
841
  });
842
+ // Resolve promise so the next interval can continue.
843
+ pwr.resolve();
711
844
  }
712
- yield hexToNumber(params.syncProgress.current.number);
713
- if (params.syncProgress.isEnd() || params.syncProgress.isFinalized()) {
714
- params.common.logger.info({
715
- msg: "Finished fetching backfill JSON-RPC data",
716
- chain: params.chain.name,
717
- chain_id: params.chain.id,
718
- duration: backfillEndClock(),
845
+ else {
846
+ // Wait for the previous interval to complete `syncBlockData`.
847
+ await promise;
848
+ if (isSyncComplete === false) {
849
+ // Queue the next interval
850
+ intervalCallback({
851
+ interval: [
852
+ Math.min(interval[1] + 1, hexToNumber(last.number)),
853
+ Math.min(interval[1] + 1 + estimateRange, hexToNumber(last.number)),
854
+ ],
855
+ promise: Promise.resolve(),
856
+ });
857
+ }
858
+ }
859
+ if (interval[1] === hexToNumber(last.number)) {
860
+ params.syncProgress.current = last;
861
+ }
862
+ if (closestToTipBlock) {
863
+ params.common.metrics.ponder_sync_block.set(label, hexToNumber(closestToTipBlock.number));
864
+ params.common.metrics.ponder_sync_block_timestamp.set(label, hexToNumber(closestToTipBlock.timestamp));
865
+ }
866
+ else {
867
+ params.common.metrics.ponder_sync_block.set(label, interval[1]);
868
+ }
869
+ params.common.metrics.ponder_historical_completed_blocks.inc(label, interval[1] - interval[0] + 1);
870
+ return requiredIntervals.length > 0;
871
+ }
872
+ const { callback, generator } = createCallbackGenerator();
873
+ (async () => {
874
+ for await (const { interval, promise } of intervalGenerator) {
875
+ // Note: this relies on the invariant that `syncInterval`
876
+ // will always resolve promises in the order it was called.
877
+ syncInterval({ interval, promise }).then((didInsertData) => {
878
+ const isDone = interval[1] === hexToNumber(last.number);
879
+ if (didInsertData || isDone) {
880
+ callback({ value: interval[1], done: false });
881
+ }
882
+ if (isDone) {
883
+ callback({ value: undefined, done: true });
884
+ }
719
885
  });
720
- return;
721
886
  }
887
+ })();
888
+ for await (const result of generator) {
889
+ if (result.done)
890
+ break;
891
+ yield result.value;
722
892
  }
893
+ params.common.logger.info({
894
+ msg: "Finished fetching backfill JSON-RPC data",
895
+ chain: params.chain.name,
896
+ chain_id: params.chain.id,
897
+ duration: backfillEndClock(),
898
+ });
723
899
  }
724
900
  /**
725
901
  * Merges multiple event generators into a single generator while preserving
@@ -746,7 +922,7 @@ export async function* mergeAsyncGeneratorsWithEventOrder(generators) {
746
922
  ];
747
923
  mergedResults.push({
748
924
  events: left,
749
- chainId: event.chainId,
925
+ chainId: event.chain.id,
750
926
  checkpoint: right.length > 0 ? event.checkpoint : result.value.checkpoint,
751
927
  blockRange,
752
928
  });