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,17 +1,22 @@
1
1
  import type { Common } from "@/internal/common.js";
2
2
  import type {
3
3
  Chain,
4
+ Factory,
4
5
  FactoryId,
5
6
  Filter,
6
7
  Fragment,
7
8
  LightBlock,
8
- Source,
9
9
  } from "@/internal/types.js";
10
10
  import type { SyncBlock } from "@/internal/types.js";
11
11
  import { _eth_getBlockByNumber } from "@/rpc/actions.js";
12
12
  import type { Rpc } from "@/rpc/index.js";
13
- import { isAddressFactory } from "@/runtime/filter.js";
14
- import { getFragments } from "@/runtime/fragments.js";
13
+ import {
14
+ getFilterFactories,
15
+ getFilterFromBlock,
16
+ getFilterToBlock,
17
+ isAddressFactory,
18
+ } from "@/runtime/filter.js";
19
+ import { getFragments, recoverFilter } from "@/runtime/fragments.js";
15
20
  import type { SyncStore } from "@/sync-store/index.js";
16
21
  import {
17
22
  MAX_CHECKPOINT,
@@ -20,8 +25,11 @@ import {
20
25
  } from "@/utils/checkpoint.js";
21
26
  import {
22
27
  type Interval,
28
+ intervalBounds,
29
+ intervalDifference,
23
30
  intervalIntersection,
24
31
  intervalIntersectionMany,
32
+ intervalUnion,
25
33
  sortIntervals,
26
34
  } from "@/utils/interval.js";
27
35
  import { type Address, hexToNumber, toHex } from "viem";
@@ -41,15 +49,25 @@ export type SyncProgress = {
41
49
  export type ChildAddresses = Map<FactoryId, Map<Address, number>>;
42
50
 
43
51
  export type CachedIntervals = Map<
44
- Filter,
52
+ Filter | Factory,
45
53
  { fragment: Fragment; intervals: Interval[] }[]
46
54
  >;
47
55
 
56
+ export type IntervalWithFilter = {
57
+ interval: Interval;
58
+ filter: Filter;
59
+ };
60
+
61
+ export type IntervalWithFactory = {
62
+ interval: Interval;
63
+ factory: Factory;
64
+ };
65
+
48
66
  export async function getLocalSyncProgress(params: {
49
67
  common: Common;
50
- sources: Source[];
51
68
  chain: Chain;
52
69
  rpc: Rpc;
70
+ filters: Filter[];
53
71
  finalizedBlock: LightBlock;
54
72
  cachedIntervals: CachedIntervals;
55
73
  }): Promise<SyncProgress> {
@@ -96,36 +114,12 @@ export async function getLocalSyncProgress(params: {
96
114
  );
97
115
  },
98
116
  } as SyncProgress;
99
- const filters = params.sources.map(({ filter }) => filter);
100
117
 
101
118
  // Earliest `fromBlock` among all `filters`
102
- const start = Math.min(
103
- ...filters.flatMap((filter) => {
104
- const fromBlocks: number[] = [filter.fromBlock ?? 0];
105
- switch (filter.type) {
106
- case "log":
107
- if (isAddressFactory(filter.address)) {
108
- fromBlocks.push(filter.address.fromBlock ?? 0);
109
- }
110
- break;
111
- case "transaction":
112
- case "trace":
113
- case "transfer":
114
- if (isAddressFactory(filter.fromAddress)) {
115
- fromBlocks.push(filter.fromAddress.fromBlock ?? 0);
116
- }
117
-
118
- if (isAddressFactory(filter.toAddress)) {
119
- fromBlocks.push(filter.toAddress.fromBlock ?? 0);
120
- }
121
- }
122
-
123
- return fromBlocks;
124
- }),
125
- );
119
+ const start = Math.min(...params.filters.map(getFilterFromBlock));
126
120
 
127
121
  const cached = getCachedBlock({
128
- filters,
122
+ filters: params.filters,
129
123
  cachedIntervals: params.cachedIntervals,
130
124
  });
131
125
 
@@ -158,12 +152,12 @@ export async function getLocalSyncProgress(params: {
158
152
  syncProgress.current = diagnostics[1];
159
153
  }
160
154
 
161
- if (filters.some((filter) => filter.toBlock === undefined)) {
155
+ if (params.filters.some((filter) => filter.toBlock === undefined)) {
162
156
  return syncProgress;
163
157
  }
164
158
 
165
159
  // Latest `toBlock` among all `filters`
166
- const end = Math.max(...filters.map((filter) => filter.toBlock!));
160
+ const end = Math.max(...params.filters.map((filter) => filter.toBlock!));
167
161
 
168
162
  if (end > hexToNumber(params.finalizedBlock.number)) {
169
163
  syncProgress.end = {
@@ -184,35 +178,35 @@ export async function getLocalSyncProgress(params: {
184
178
  }
185
179
 
186
180
  export async function getChildAddresses(params: {
187
- sources: Source[];
181
+ filters: Filter[];
188
182
  syncStore: SyncStore;
189
183
  }): Promise<ChildAddresses> {
190
184
  const childAddresses: ChildAddresses = new Map();
191
- for (const source of params.sources) {
192
- switch (source.filter.type) {
185
+ for (const filter of params.filters) {
186
+ switch (filter.type) {
193
187
  case "log":
194
- if (isAddressFactory(source.filter.address)) {
188
+ if (isAddressFactory(filter.address)) {
195
189
  const _childAddresses = await params.syncStore.getChildAddresses({
196
- factory: source.filter.address,
190
+ factory: filter.address,
197
191
  });
198
- childAddresses.set(source.filter.address.id, _childAddresses);
192
+ childAddresses.set(filter.address.id, _childAddresses);
199
193
  }
200
194
  break;
201
195
  case "transaction":
202
196
  case "transfer":
203
197
  case "trace":
204
- if (isAddressFactory(source.filter.fromAddress)) {
198
+ if (isAddressFactory(filter.fromAddress)) {
205
199
  const _childAddresses = await params.syncStore.getChildAddresses({
206
- factory: source.filter.fromAddress,
200
+ factory: filter.fromAddress,
207
201
  });
208
- childAddresses.set(source.filter.fromAddress.id, _childAddresses);
202
+ childAddresses.set(filter.fromAddress.id, _childAddresses);
209
203
  }
210
204
 
211
- if (isAddressFactory(source.filter.toAddress)) {
205
+ if (isAddressFactory(filter.toAddress)) {
212
206
  const _childAddresses = await params.syncStore.getChildAddresses({
213
- factory: source.filter.toAddress,
207
+ factory: filter.toAddress,
214
208
  });
215
- childAddresses.set(source.filter.toAddress.id, _childAddresses);
209
+ childAddresses.set(filter.toAddress.id, _childAddresses);
216
210
  }
217
211
 
218
212
  break;
@@ -223,18 +217,18 @@ export async function getChildAddresses(params: {
223
217
 
224
218
  export async function getCachedIntervals(params: {
225
219
  chain: Chain;
220
+ filters: Filter[];
226
221
  syncStore: SyncStore;
227
- sources: Source[];
228
222
  }): Promise<CachedIntervals> {
229
223
  /**
230
- * Intervals that have been completed for all filters in `args.sources`.
224
+ * Intervals that have been completed for all filters in `args.filters`.
231
225
  *
232
226
  * Note: `intervalsCache` is not updated after a new interval is synced.
233
227
  */
234
228
  let cachedIntervals: CachedIntervals;
235
229
  if (params.chain.disableCache) {
236
230
  cachedIntervals = new Map();
237
- for (const { filter } of params.sources) {
231
+ for (const filter of params.filters) {
238
232
  cachedIntervals.set(filter, []);
239
233
  for (const { fragment } of getFragments(filter)) {
240
234
  cachedIntervals.get(filter)!.push({ fragment, intervals: [] });
@@ -242,14 +236,253 @@ export async function getCachedIntervals(params: {
242
236
  }
243
237
  } else {
244
238
  cachedIntervals = await params.syncStore.getIntervals({
245
- filters: params.sources.map(({ filter }) => filter),
239
+ filters: params.filters,
246
240
  });
247
241
  }
248
242
 
249
243
  return cachedIntervals;
250
244
  }
251
245
 
252
- /** Returns the closest-to-tip block that has been synced for all `sources`. */
246
+ /**
247
+ * Returns the intervals that need to be synced to complete the `interval`
248
+ * for all `filters`.
249
+ *
250
+ * @param params.filters - The filters to sync.
251
+ * @param params.interval - The interval to sync.
252
+ * @param params.cachedIntervals - The cached intervals for the filters.
253
+ * @returns The intervals that need to be synced.
254
+ */
255
+ export const getRequiredIntervals = (params: {
256
+ filters: Filter[];
257
+ interval: Interval;
258
+ cachedIntervals: CachedIntervals;
259
+ }): Interval[] => {
260
+ const requiredIntervals: Interval[] = [];
261
+ for (const filter of params.filters) {
262
+ const filterTotalIntervals = intervalIntersection(
263
+ [params.interval],
264
+ [[filter.fromBlock ?? 0, filter.toBlock ?? Number.POSITIVE_INFINITY]],
265
+ );
266
+ let filterCachedIntervals = params.cachedIntervals.get(filter)!;
267
+
268
+ const factories = getFilterFactories(filter);
269
+
270
+ const missingFactoryIntervals: Interval[] = [];
271
+ for (const factory of factories) {
272
+ const factoryTotalIntervals = intervalIntersection(
273
+ [params.interval],
274
+ [[factory.fromBlock ?? 0, factory.toBlock ?? Number.POSITIVE_INFINITY]],
275
+ );
276
+
277
+ missingFactoryIntervals.push(
278
+ ...intervalDifference(
279
+ factoryTotalIntervals,
280
+ intervalIntersectionMany(
281
+ params.cachedIntervals
282
+ .get(factory)!
283
+ .map(({ intervals }) => intervals),
284
+ ),
285
+ ),
286
+ );
287
+ }
288
+
289
+ if (missingFactoryIntervals.length > 0) {
290
+ const firstMissingFactoryBlock = sortIntervals(
291
+ missingFactoryIntervals,
292
+ )[0]![0];
293
+
294
+ // Note: When a filter with a factory is missing blocks,
295
+ // all blocks after the first missing block are also missing.
296
+
297
+ filterCachedIntervals = filterCachedIntervals.map(
298
+ ({ fragment, intervals }) => {
299
+ return {
300
+ fragment,
301
+ intervals: intervalDifference(intervals, [
302
+ [firstMissingFactoryBlock, params.interval[1]],
303
+ ]),
304
+ };
305
+ },
306
+ );
307
+ }
308
+
309
+ const missingIntervals = intervalDifference(
310
+ filterTotalIntervals,
311
+ intervalIntersectionMany(
312
+ filterCachedIntervals.map(({ intervals }) => intervals),
313
+ ),
314
+ );
315
+
316
+ requiredIntervals.push(...missingIntervals, ...missingFactoryIntervals);
317
+ }
318
+
319
+ return intervalUnion(requiredIntervals);
320
+ };
321
+
322
+ /**
323
+ * Returns the intervals that need to be synced to complete the `interval`
324
+ * for all `filters`.
325
+ *
326
+ * Note: This function dynamically builds filters using `recoverFilter`.
327
+ * Fragments are used to create a minimal filter, to avoid refetching data
328
+ * even if a filter is only partially synced.
329
+ *
330
+ * @param params.filters - The filters to sync.
331
+ * @param params.interval - The interval to sync.
332
+ * @param params.cachedIntervals - The cached intervals for the filters.
333
+ * @returns The intervals that need to be synced.
334
+ */
335
+ export const getRequiredIntervalsWithFilters = (params: {
336
+ filters: Filter[];
337
+ interval: Interval;
338
+ cachedIntervals: CachedIntervals;
339
+ }): {
340
+ intervals: IntervalWithFilter[];
341
+ factoryIntervals: IntervalWithFactory[];
342
+ } => {
343
+ const requiredIntervals: IntervalWithFilter[] = [];
344
+ const requiredFactoryIntervals: IntervalWithFactory[] = [];
345
+
346
+ // Determine the requests that need to be made, and which intervals need to be inserted.
347
+ // Fragments are used to create a minimal filter, to avoid refetching data even if a filter
348
+ // is only partially synced.
349
+
350
+ for (const filter of params.filters) {
351
+ const filterTotalIntervals = intervalIntersection(
352
+ [params.interval],
353
+ [[filter.fromBlock ?? 0, filter.toBlock ?? Number.POSITIVE_INFINITY]],
354
+ );
355
+ let filterCachedIntervals = params.cachedIntervals.get(filter)!;
356
+
357
+ const factories = getFilterFactories(filter);
358
+
359
+ const missingFactoryIntervals: Interval[] = [];
360
+ for (const factory of factories) {
361
+ const factoryTotalIntervals = intervalIntersection(
362
+ [params.interval],
363
+ [[factory.fromBlock ?? 0, factory.toBlock ?? Number.POSITIVE_INFINITY]],
364
+ );
365
+
366
+ missingFactoryIntervals.push(
367
+ ...intervalDifference(
368
+ factoryTotalIntervals,
369
+ intervalIntersectionMany(
370
+ params.cachedIntervals
371
+ .get(factory)!
372
+ .map(({ intervals }) => intervals),
373
+ ),
374
+ ),
375
+ );
376
+ }
377
+
378
+ if (missingFactoryIntervals.length > 0) {
379
+ const firstMissingFactoryBlock = sortIntervals(
380
+ missingFactoryIntervals,
381
+ )[0]![0];
382
+
383
+ // Note: When a filter with a factory is missing blocks,
384
+ // all blocks after the first missing block are also missing.
385
+
386
+ filterCachedIntervals = filterCachedIntervals.map(
387
+ ({ fragment, intervals }) => {
388
+ return {
389
+ fragment,
390
+ intervals: intervalDifference(intervals, [
391
+ [firstMissingFactoryBlock, params.interval[1]],
392
+ ]),
393
+ };
394
+ },
395
+ );
396
+ }
397
+
398
+ const requiredFragmentIntervals: {
399
+ fragment: Fragment;
400
+ intervals: Interval[];
401
+ }[] = [];
402
+
403
+ for (const {
404
+ fragment,
405
+ intervals: fragmentIntervals,
406
+ } of filterCachedIntervals) {
407
+ const missingFragmentIntervals = intervalDifference(
408
+ filterTotalIntervals,
409
+ fragmentIntervals,
410
+ );
411
+
412
+ if (missingFragmentIntervals.length > 0) {
413
+ requiredFragmentIntervals.push({
414
+ fragment,
415
+ intervals: missingFragmentIntervals,
416
+ });
417
+ }
418
+ }
419
+
420
+ if (requiredFragmentIntervals.length > 0) {
421
+ const requiredInterval = intervalBounds(
422
+ requiredFragmentIntervals.flatMap(({ intervals }) => intervals),
423
+ );
424
+
425
+ const requiredFilter = recoverFilter(
426
+ filter,
427
+ requiredFragmentIntervals.map(({ fragment }) => fragment),
428
+ );
429
+
430
+ requiredIntervals.push({
431
+ filter: requiredFilter,
432
+ interval: requiredInterval,
433
+ });
434
+ }
435
+
436
+ for (const factory of factories) {
437
+ const factoryTotalIntervals = intervalIntersection(
438
+ [params.interval],
439
+ [[factory.fromBlock ?? 0, factory.toBlock ?? Number.POSITIVE_INFINITY]],
440
+ );
441
+
442
+ const requiredFactoryFragmentIntervals: {
443
+ fragment: Fragment;
444
+ intervals: Interval[];
445
+ }[] = [];
446
+
447
+ for (const {
448
+ fragment,
449
+ intervals: fragmentIntervals,
450
+ } of params.cachedIntervals.get(factory)!) {
451
+ const missingFragmentIntervals = intervalDifference(
452
+ factoryTotalIntervals,
453
+ fragmentIntervals,
454
+ );
455
+
456
+ if (missingFragmentIntervals.length > 0) {
457
+ requiredFactoryFragmentIntervals.push({
458
+ fragment,
459
+ intervals: missingFragmentIntervals,
460
+ });
461
+ }
462
+ }
463
+
464
+ if (requiredFactoryFragmentIntervals.length > 0) {
465
+ const requiredInterval = intervalBounds(
466
+ requiredFactoryFragmentIntervals.flatMap(
467
+ ({ intervals }) => intervals,
468
+ ),
469
+ );
470
+
471
+ requiredFactoryIntervals.push({
472
+ factory,
473
+ interval: requiredInterval,
474
+ });
475
+ }
476
+ }
477
+ }
478
+
479
+ return {
480
+ intervals: requiredIntervals,
481
+ factoryIntervals: requiredFactoryIntervals,
482
+ };
483
+ };
484
+
485
+ /** Returns the closest-to-tip block that has been synced for all `filters`. */
253
486
  export const getCachedBlock = ({
254
487
  filters,
255
488
  cachedIntervals,
@@ -258,35 +491,70 @@ export const getCachedBlock = ({
258
491
  cachedIntervals: CachedIntervals;
259
492
  }): number | undefined => {
260
493
  const latestCompletedBlocks = filters.map((filter) => {
261
- const requiredInterval = [
494
+ const filterTotalInterval = [
262
495
  filter.fromBlock ?? 0,
263
496
  filter.toBlock ?? Number.POSITIVE_INFINITY,
264
497
  ] satisfies Interval;
265
- const fragmentIntervals = cachedIntervals.get(filter)!;
498
+ let filterCachedIntervals = cachedIntervals.get(filter)!;
499
+
500
+ const factories = getFilterFactories(filter);
266
501
 
267
- const completedIntervals = sortIntervals(
268
- intervalIntersection(
269
- [requiredInterval],
270
- intervalIntersectionMany(
271
- fragmentIntervals.map(({ intervals }) => intervals),
502
+ const missingFactoryIntervals: Interval[] = [];
503
+ for (const factory of factories) {
504
+ const factoryTotalInterval = [
505
+ factory.fromBlock ?? 0,
506
+ factory.toBlock ?? Number.POSITIVE_INFINITY,
507
+ ] satisfies Interval;
508
+
509
+ missingFactoryIntervals.push(
510
+ ...intervalDifference(
511
+ [factoryTotalInterval],
512
+ intervalIntersectionMany(
513
+ cachedIntervals.get(factory)!.map(({ intervals }) => intervals),
514
+ ),
272
515
  ),
516
+ );
517
+ }
518
+
519
+ if (missingFactoryIntervals.length > 0) {
520
+ const firstMissingFactoryBlock = sortIntervals(
521
+ missingFactoryIntervals,
522
+ )[0]![0];
523
+
524
+ // Note: When a filter with a factory is missing blocks,
525
+ // all blocks after the first missing block are also missing.
526
+
527
+ filterCachedIntervals = filterCachedIntervals.map(
528
+ ({ fragment, intervals }) => {
529
+ return {
530
+ fragment,
531
+ intervals: intervalDifference(intervals, [
532
+ [firstMissingFactoryBlock, filterTotalInterval[1]],
533
+ ]),
534
+ };
535
+ },
536
+ );
537
+ }
538
+
539
+ let missingIntervals = intervalDifference(
540
+ [filterTotalInterval],
541
+ intervalIntersectionMany(
542
+ filterCachedIntervals.map(({ intervals }) => intervals),
273
543
  ),
274
544
  );
275
545
 
276
- if (completedIntervals.length === 0) {
277
- // Use `fromBlock` - 1 as completed block if no intervals are complete.
278
- if ((filter.fromBlock ?? 0) === 0) return undefined;
279
- return filter.fromBlock! - 1;
546
+ if (missingIntervals.length === 0 && missingFactoryIntervals.length === 0) {
547
+ return getFilterToBlock(filter);
280
548
  }
281
549
 
282
- const earliestCompletedInterval = completedIntervals[0]!;
283
- if (earliestCompletedInterval[0] !== (filter.fromBlock ?? 0)) {
284
- // Use `fromBlock` - 1 as completed block if the earliest
285
- // completed interval does not start at `fromBlock`.
286
- if ((filter.fromBlock ?? 0) === 0) return undefined;
287
- return filter.fromBlock! - 1;
288
- }
289
- return earliestCompletedInterval[1];
550
+ missingIntervals = sortIntervals([
551
+ ...missingIntervals,
552
+ ...missingFactoryIntervals,
553
+ ]);
554
+
555
+ if (missingIntervals[0]![0] === 0) return undefined;
556
+ // First missing block - 1 is the last completed block
557
+ return missingIntervals[0]![0] - 1;
290
558
  });
291
559
 
292
560
  if (latestCompletedBlocks.every((block) => block !== undefined)) {
@@ -1,12 +1,12 @@
1
+ import type { Database } from "@/database/index.js";
1
2
  import type { Common } from "@/internal/common.js";
2
3
  import type {
3
4
  Chain,
5
+ EventCallback,
4
6
  IndexingBuild,
5
7
  RawEvent,
6
- Source,
7
8
  } from "@/internal/types.js";
8
9
  import type { Rpc } from "@/rpc/index.js";
9
- import type { SyncStore } from "@/sync-store/index.js";
10
10
  import { getLocalEventGenerator, refetchLocalEvents } from "./historical.js";
11
11
  import {
12
12
  type CachedIntervals,
@@ -19,18 +19,18 @@ export async function initEventGenerator(params: {
19
19
  common: Common;
20
20
  indexingBuild: Pick<
21
21
  IndexingBuild,
22
- "sources" | "chains" | "rpcs" | "finalizedBlocks"
22
+ "eventCallbacks" | "chains" | "rpcs" | "finalizedBlocks"
23
23
  >;
24
24
  chain: Chain;
25
25
  rpc: Rpc;
26
- sources: Source[];
26
+ eventCallbacks: EventCallback[];
27
27
  childAddresses: ChildAddresses;
28
28
  syncProgress: SyncProgress;
29
29
  cachedIntervals: CachedIntervals;
30
30
  from: string;
31
31
  to: string;
32
32
  limit: number;
33
- syncStore: SyncStore;
33
+ database: Database;
34
34
  isCatchup: boolean;
35
35
  }) {
36
36
  return getLocalEventGenerator(params);