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
@@ -2,12 +2,12 @@ import { BuildError } from '../internal/errors.js';
2
2
  import { _eth_getBlockByNumber } from '../rpc/actions.js';
3
3
  import { createRpc } from '../rpc/index.js';
4
4
  import { defaultBlockFilterInclude, defaultLogFilterInclude, defaultTraceFilterInclude, defaultTransactionFilterInclude, defaultTransactionReceiptInclude, defaultTransferFilterInclude, } from '../runtime/filter.js';
5
- import { buildAbiEvents, buildAbiFunctions, buildTopics } from '../utils/abi.js';
5
+ import { buildTopics, toSafeName } from '../utils/abi.js';
6
6
  import { hyperliquidEvm, chains as viemChains } from '../utils/chains.js';
7
7
  import { dedupe } from '../utils/dedupe.js';
8
8
  import { getFinalityBlockCount } from '../utils/finality.js';
9
9
  import { toLowerCase } from '../utils/lowercase.js';
10
- import { BlockNotFoundError, hexToNumber } from "viem";
10
+ import { BlockNotFoundError, hexToNumber, toEventSelector, toFunctionSelector, } from "viem";
11
11
  import { buildLogFactory } from "./factory.js";
12
12
  const flattenSources = (config) => {
13
13
  return Object.entries(config).flatMap(([name, source]) => {
@@ -31,7 +31,7 @@ const flattenSources = (config) => {
31
31
  }
32
32
  });
33
33
  };
34
- export async function buildIndexingFunctions({ common, config, rawIndexingFunctions, configBuild: { chains, rpcs }, }) {
34
+ export async function buildIndexingFunctions({ common, config, indexingFunctions, configBuild: { chains, rpcs }, }) {
35
35
  const context = { logger: common.logger.child({ action: "build" }) };
36
36
  const logs = [];
37
37
  const perChainLatestBlockNumber = new Map();
@@ -94,9 +94,11 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
94
94
  sourceNames.add(source);
95
95
  }
96
96
  // Validate and build indexing functions
97
- let indexingFunctionCount = 0;
98
- const indexingFunctions = {};
99
- for (const { name: eventName, fn } of rawIndexingFunctions) {
97
+ if (indexingFunctions.length === 0) {
98
+ throw new Error("Validation failed: Found 0 registered indexing functions.");
99
+ }
100
+ const eventNames = new Set();
101
+ for (const { name: eventName } of indexingFunctions) {
100
102
  const eventNameComponents = eventName.includes(".")
101
103
  ? eventName.split(".")
102
104
  : eventName.split(":");
@@ -120,9 +122,10 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
120
122
  else {
121
123
  throw new Error(`Validation failed: Invalid event '${eventName}', expected format '{sourceName}:{eventName}' or '{sourceName}.{functionName}'.`);
122
124
  }
123
- if (eventName in indexingFunctions) {
125
+ if (eventNames.has(eventName)) {
124
126
  throw new Error(`Validation failed: Multiple indexing functions registered for event '${eventName}'.`);
125
127
  }
128
+ eventNames.add(eventName);
126
129
  // Validate that the indexing function uses a sourceName that is present in the config.
127
130
  const matchedSourceName = Object.keys({
128
131
  ...(config.contracts ?? {}),
@@ -134,11 +137,6 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
134
137
  .map((n) => `'${n}'`)
135
138
  .join(", ")}].`);
136
139
  }
137
- indexingFunctions[eventName] = fn;
138
- indexingFunctionCount += 1;
139
- }
140
- if (indexingFunctionCount === 0) {
141
- logs.push({ level: "warn", msg: "No registered indexing functions" });
142
140
  }
143
141
  // common validation for all sources
144
142
  for (const source of [
@@ -191,12 +189,102 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
191
189
  }
192
190
  }
193
191
  }
194
- const contractSources = (await Promise.all(flattenSources(config.contracts ?? {}).map(async (source) => {
192
+ const perChainEventCallbacks = new Map();
193
+ const perChainSetupCallbacks = new Map();
194
+ const perChainContracts = new Map();
195
+ for (const chain of chains) {
196
+ perChainEventCallbacks.set(chain.id, []);
197
+ perChainSetupCallbacks.set(chain.id, []);
198
+ perChainContracts.set(chain.id, {});
199
+ }
200
+ for (const source of flattenSources(config.contracts ?? {})) {
195
201
  const chain = chains.find((n) => n.name === source.chain);
196
- // Get indexing function that were registered for this contract
202
+ const fromBlock = await resolveBlockNumber(source.startBlock, chain);
203
+ const toBlock = await resolveBlockNumber(source.endBlock, chain);
204
+ if (indexingFunctions.some((f) => f.name === `${source.name}:setup`)) {
205
+ perChainSetupCallbacks.get(chain.id).push({
206
+ name: `${source.name}:setup`,
207
+ fn: indexingFunctions.find((f) => f.name === `${source.name}:setup`)
208
+ .fn,
209
+ chain,
210
+ block: fromBlock,
211
+ });
212
+ }
213
+ let address;
214
+ const resolvedAddress = source?.address;
215
+ if (typeof resolvedAddress === "object" &&
216
+ Array.isArray(resolvedAddress) === false) {
217
+ const factoryAddress = resolvedAddress;
218
+ const factoryFromBlock = (await resolveBlockNumber(factoryAddress.startBlock, chain)) ??
219
+ fromBlock;
220
+ const factoryToBlock = (await resolveBlockNumber(factoryAddress.endBlock, chain)) ?? toBlock;
221
+ // Note that this can throw.
222
+ const logFactory = buildLogFactory({
223
+ chainId: chain.id,
224
+ ...factoryAddress,
225
+ fromBlock: factoryFromBlock,
226
+ toBlock: factoryToBlock,
227
+ });
228
+ perChainContracts.get(chain.id)[source.name] = {
229
+ abi: source.abi,
230
+ address: undefined,
231
+ startBlock: fromBlock,
232
+ endBlock: toBlock,
233
+ };
234
+ address = logFactory;
235
+ }
236
+ else {
237
+ if (resolvedAddress !== undefined) {
238
+ for (const address of Array.isArray(resolvedAddress)
239
+ ? resolvedAddress
240
+ : [resolvedAddress]) {
241
+ if (!address.startsWith("0x"))
242
+ throw new Error(`Validation failed: Invalid prefix for address '${address}'. Got '${address.slice(0, 2)}', expected '0x'.`);
243
+ if (address.length !== 42)
244
+ throw new Error(`Validation failed: Invalid length for address '${address}'. Got ${address.length}, expected 42 characters.`);
245
+ }
246
+ }
247
+ const validatedAddress = Array.isArray(resolvedAddress)
248
+ ? dedupe(resolvedAddress).map((r) => toLowerCase(r))
249
+ : resolvedAddress !== undefined
250
+ ? toLowerCase(resolvedAddress)
251
+ : undefined;
252
+ perChainContracts.get(chain.id)[source.name] = {
253
+ abi: source.abi,
254
+ address: validatedAddress,
255
+ startBlock: fromBlock,
256
+ endBlock: toBlock,
257
+ };
258
+ address = validatedAddress;
259
+ }
260
+ const filteredEventSelectors = new Map();
261
+ if (source.filter) {
262
+ const eventFilters = Array.isArray(source.filter)
263
+ ? source.filter
264
+ : [source.filter];
265
+ for (const filter of eventFilters) {
266
+ const abiEvent = source.abi.find((item) => item.type === "event" &&
267
+ toSafeName({ abi: source.abi, item }) === filter.event);
268
+ if (!abiEvent) {
269
+ throw new Error(`Validation failed: Invalid filter for contract '${source.name}'. Got event name '${filter.event}', expected one of [${source.abi
270
+ .filter((item) => item.type === "event")
271
+ .map((item) => `'${toSafeName({ abi: source.abi, item })}'`)
272
+ .join(", ")}].`);
273
+ }
274
+ }
275
+ const topics = buildTopics(source.abi, eventFilters);
276
+ for (const { topic0, topic1, topic2, topic3 } of topics) {
277
+ const abiItem = source.abi.find((item) => item.type === "event" && toEventSelector(item) === topic0);
278
+ const indexingFunction = indexingFunctions.find((f) => f.name === `${source.name}:${abiItem.name}`);
279
+ if (indexingFunction === undefined) {
280
+ throw new Error(`Validation failed: Event selector '${toSafeName({ abi: source.abi, item: abiItem })}' is used in a filter but does not have a corresponding indexing function.`);
281
+ }
282
+ filteredEventSelectors.set(topic0, { topic0, topic1, topic2, topic3 });
283
+ }
284
+ }
197
285
  const registeredLogEvents = [];
198
286
  const registeredCallTraceEvents = [];
199
- for (const eventName of Object.keys(indexingFunctions)) {
287
+ for (const { name: eventName } of indexingFunctions) {
200
288
  // log event
201
289
  if (eventName.includes(":")) {
202
290
  const [logContractName, logEventName] = eventName.split(":");
@@ -204,228 +292,124 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
204
292
  registeredLogEvents.push(logEventName);
205
293
  }
206
294
  }
207
- // trace event
295
+ // trace event
208
296
  if (eventName.includes(".")) {
209
297
  const [functionContractName, functionName] = eventName.split(".");
298
+ if (source.includeCallTraces !== true) {
299
+ continue;
300
+ }
210
301
  if (functionContractName === source.name) {
211
302
  registeredCallTraceEvents.push(functionName);
212
303
  }
213
304
  }
214
305
  }
215
- // Note: This can probably throw for invalid ABIs. Consider adding explicit ABI validation before this line.
216
- const abiEvents = buildAbiEvents({ abi: source.abi });
217
- const abiFunctions = buildAbiFunctions({ abi: source.abi });
218
- const registeredEventSelectors = [];
219
- // Validate that the registered log events exist in the abi
220
- for (const logEvent of registeredLogEvents) {
221
- const abiEvent = abiEvents.bySafeName[logEvent];
306
+ for (const logEventName of registeredLogEvents) {
307
+ const abiEvent = source.abi.find((item) => item.type === "event" &&
308
+ toSafeName({ abi: source.abi, item }) === logEventName);
222
309
  if (abiEvent === undefined) {
223
- throw new Error(`Validation failed: Event name for event '${logEvent}' not found in the contract ABI. Got '${logEvent}', expected one of [${Object.keys(abiEvents.bySafeName)
224
- .map((eventName) => `'${eventName}'`)
310
+ throw new Error(`Validation failed: Event name for event '${logEventName}' not found in the contract ABI. Got '${logEventName}', expected one of [${source.abi
311
+ .filter((item) => item.type === "event")
312
+ .map((item) => `'${toSafeName({ abi: source.abi, item })}'`)
225
313
  .join(", ")}].`);
226
314
  }
227
- registeredEventSelectors.push(abiEvent.selector);
228
- }
229
- const registeredFunctionSelectors = [];
230
- for (const _function of registeredCallTraceEvents) {
231
- const abiFunction = abiFunctions.bySafeName[_function];
232
- if (abiFunction === undefined) {
233
- throw new Error(`Validation failed: Function name for function '${_function}' not found in the contract ABI. Got '${_function}', expected one of [${Object.keys(abiFunctions.bySafeName)
234
- .map((eventName) => `'${eventName}'`)
235
- .join(", ")}].`);
315
+ const eventName = `${source.name}:${logEventName}`;
316
+ const indexingFunction = indexingFunctions.find((f) => f.name === eventName);
317
+ let topic1;
318
+ let topic2;
319
+ let topic3;
320
+ const eventSelector = toEventSelector(abiEvent);
321
+ if (filteredEventSelectors.has(eventSelector)) {
322
+ topic1 = filteredEventSelectors.get(eventSelector).topic1;
323
+ topic2 = filteredEventSelectors.get(eventSelector).topic2;
324
+ topic3 = filteredEventSelectors.get(eventSelector).topic3;
236
325
  }
237
- registeredFunctionSelectors.push(abiFunction.selector);
238
- }
239
- const topicsArray = [];
240
- if (source.filter !== undefined) {
241
- const eventFilters = Array.isArray(source.filter)
242
- ? source.filter
243
- : [source.filter];
244
- for (const filter of eventFilters) {
245
- const abiEvent = abiEvents.bySafeName[filter.event];
246
- if (!abiEvent) {
247
- throw new Error(`Validation failed: Invalid filter for contract '${source.name}'. Got event name '${filter.event}', expected one of [${Object.keys(abiEvents.bySafeName)
248
- .map((n) => `'${n}'`)
249
- .join(", ")}].`);
250
- }
251
- }
252
- topicsArray.push(...buildTopics(source.abi, eventFilters));
253
- // event selectors that have a filter
254
- const filteredEventSelectors = topicsArray.map((t) => t.topic0);
255
- // event selectors that are registered but don't have a filter
256
- const excludedRegisteredEventSelectors = registeredEventSelectors.filter((s) => filteredEventSelectors.includes(s) === false);
257
- for (const selector of filteredEventSelectors) {
258
- if (registeredEventSelectors.includes(selector) === false) {
259
- throw new Error(`Validation failed: Event selector '${abiEvents.bySelector[selector]?.safeName}' is used in a filter but does not have a corresponding indexing function.`);
260
- }
261
- }
262
- if (excludedRegisteredEventSelectors.length > 0) {
263
- topicsArray.push({
264
- topic0: excludedRegisteredEventSelectors,
265
- topic1: null,
266
- topic2: null,
267
- topic3: null,
268
- });
326
+ else {
327
+ topic1 = null;
328
+ topic2 = null;
329
+ topic3 = null;
269
330
  }
270
- }
271
- else {
272
- topicsArray.push({
273
- topic0: registeredEventSelectors,
274
- topic1: null,
275
- topic2: null,
276
- topic3: null,
277
- });
278
- }
279
- const fromBlock = await resolveBlockNumber(source.startBlock, chain);
280
- const toBlock = await resolveBlockNumber(source.endBlock, chain);
281
- const contractMetadata = {
282
- type: "contract",
283
- abi: source.abi,
284
- abiEvents,
285
- abiFunctions,
286
- name: source.name,
287
- chain,
288
- };
289
- const resolvedAddress = source?.address;
290
- if (typeof resolvedAddress === "object" &&
291
- !Array.isArray(resolvedAddress)) {
292
- const factoryFromBlock = (await resolveBlockNumber(resolvedAddress.startBlock, chain)) ??
293
- fromBlock;
294
- const factoryToBlock = (await resolveBlockNumber(resolvedAddress.endBlock, chain)) ??
295
- toBlock;
296
- // Note that this can throw.
297
- const logFactory = buildLogFactory({
331
+ const filter = {
332
+ type: "log",
298
333
  chainId: chain.id,
299
- ...resolvedAddress,
300
- fromBlock: factoryFromBlock,
301
- toBlock: factoryToBlock,
302
- });
303
- const logSources = topicsArray.map((topics) => ({
304
- ...contractMetadata,
305
- filter: {
306
- type: "log",
307
- chainId: chain.id,
308
- address: logFactory,
309
- topic0: topics.topic0,
310
- topic1: topics.topic1,
311
- topic2: topics.topic2,
312
- topic3: topics.topic3,
313
- fromBlock,
314
- toBlock,
315
- hasTransactionReceipt: source.includeTransactionReceipts ?? false,
316
- include: defaultLogFilterInclude.concat(source.includeTransactionReceipts
317
- ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
318
- : []),
334
+ sourceId: source.name,
335
+ address,
336
+ topic0: eventSelector,
337
+ topic1,
338
+ topic2,
339
+ topic3,
340
+ fromBlock,
341
+ toBlock,
342
+ hasTransactionReceipt: source.includeTransactionReceipts ?? false,
343
+ include: defaultLogFilterInclude.concat(source.includeTransactionReceipts
344
+ ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
345
+ : []),
346
+ };
347
+ const eventCallback = {
348
+ filter,
349
+ name: eventName,
350
+ fn: indexingFunction.fn,
351
+ chain,
352
+ type: "contract",
353
+ abiItem: abiEvent,
354
+ metadata: {
355
+ safeName: logEventName,
356
+ abi: source.abi,
319
357
  },
320
- }));
321
- if (source.includeCallTraces) {
322
- return [
323
- ...logSources,
324
- {
325
- ...contractMetadata,
326
- filter: {
327
- type: "trace",
328
- chainId: chain.id,
329
- fromAddress: undefined,
330
- toAddress: logFactory,
331
- callType: "CALL",
332
- functionSelector: registeredFunctionSelectors,
333
- includeReverted: false,
334
- fromBlock,
335
- toBlock,
336
- hasTransactionReceipt: source.includeTransactionReceipts ?? false,
337
- include: defaultTraceFilterInclude.concat(source.includeTransactionReceipts
338
- ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
339
- : []),
340
- },
341
- },
342
- ];
343
- }
344
- return logSources;
358
+ };
359
+ perChainEventCallbacks.get(chain.id).push(eventCallback);
345
360
  }
346
- else if (resolvedAddress !== undefined) {
347
- for (const address of Array.isArray(resolvedAddress)
348
- ? resolvedAddress
349
- : [resolvedAddress]) {
350
- if (!address.startsWith("0x"))
351
- throw new Error(`Validation failed: Invalid prefix for address '${address}'. Got '${address.slice(0, 2)}', expected '0x'.`);
352
- if (address.length !== 42)
353
- throw new Error(`Validation failed: Invalid length for address '${address}'. Got ${address.length}, expected 42 characters.`);
361
+ for (const functionEventName of registeredCallTraceEvents) {
362
+ const abiFunction = source.abi.find((item) => item.type === "function" &&
363
+ toSafeName({ abi: source.abi, item }) === functionEventName);
364
+ if (abiFunction === undefined) {
365
+ throw new Error(`Validation failed: Function name for function '${functionEventName}' not found in the contract ABI. Got '${functionEventName}', expected one of [${source.abi
366
+ .filter((item) => item.type === "function")
367
+ .map((item) => `'${toSafeName({ abi: source.abi, item })}'`)
368
+ .join(", ")}].`);
354
369
  }
355
- }
356
- const validatedAddress = Array.isArray(resolvedAddress)
357
- ? dedupe(resolvedAddress).map((r) => toLowerCase(r))
358
- : resolvedAddress !== undefined
359
- ? toLowerCase(resolvedAddress)
360
- : undefined;
361
- const logSources = topicsArray.map((topics) => ({
362
- ...contractMetadata,
363
- filter: {
364
- type: "log",
370
+ const eventName = `${source.name}.${functionEventName}`;
371
+ const indexingFunction = indexingFunctions.find((f) => f.name === eventName);
372
+ const filter = {
373
+ type: "trace",
365
374
  chainId: chain.id,
366
- address: validatedAddress,
367
- topic0: topics.topic0,
368
- topic1: topics.topic1,
369
- topic2: topics.topic2,
370
- topic3: topics.topic3,
375
+ sourceId: source.name,
376
+ fromAddress: undefined,
377
+ toAddress: address,
378
+ callType: "CALL",
379
+ functionSelector: toFunctionSelector(abiFunction),
380
+ includeReverted: false,
371
381
  fromBlock,
372
382
  toBlock,
373
383
  hasTransactionReceipt: source.includeTransactionReceipts ?? false,
374
- include: defaultLogFilterInclude.concat(source.includeTransactionReceipts
384
+ include: defaultTraceFilterInclude.concat(source.includeTransactionReceipts
375
385
  ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
376
386
  : []),
377
- },
378
- }));
379
- if (source.includeCallTraces) {
380
- return [
381
- ...logSources,
382
- {
383
- ...contractMetadata,
384
- filter: {
385
- type: "trace",
386
- chainId: chain.id,
387
- fromAddress: undefined,
388
- toAddress: Array.isArray(validatedAddress)
389
- ? validatedAddress
390
- : validatedAddress === undefined
391
- ? undefined
392
- : [validatedAddress],
393
- callType: "CALL",
394
- functionSelector: registeredFunctionSelectors,
395
- includeReverted: false,
396
- fromBlock,
397
- toBlock,
398
- hasTransactionReceipt: source.includeTransactionReceipts ?? false,
399
- include: defaultTraceFilterInclude.concat(source.includeTransactionReceipts
400
- ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
401
- : []),
402
- },
387
+ };
388
+ const eventCallback = {
389
+ filter,
390
+ name: eventName,
391
+ fn: indexingFunction.fn,
392
+ chain,
393
+ type: "contract",
394
+ abiItem: abiFunction,
395
+ metadata: {
396
+ safeName: functionEventName,
397
+ abi: source.abi,
403
398
  },
404
- ];
405
- }
406
- else
407
- return logSources;
408
- })))
409
- .flat() // Remove sources with no registered indexing functions
410
- .filter((source) => {
411
- const hasNoRegisteredIndexingFunctions = source.filter.type === "trace"
412
- ? Array.isArray(source.filter.functionSelector) &&
413
- source.filter.functionSelector.length === 0
414
- : Array.isArray(source.filter.topic0) &&
415
- source.filter.topic0?.length === 0;
416
- if (hasNoRegisteredIndexingFunctions) {
399
+ };
400
+ perChainEventCallbacks.get(chain.id).push(eventCallback);
401
+ }
402
+ if (registeredLogEvents.length === 0 &&
403
+ registeredCallTraceEvents.length === 0) {
417
404
  logs.push({
418
405
  level: "warn",
419
406
  msg: "No registered indexing functions",
420
- chain: source.chain.name,
421
- chain_id: source.chain.id,
422
407
  name: source.name,
423
- type: source.filter.type,
408
+ type: "contract",
424
409
  });
425
410
  }
426
- return hasNoRegisteredIndexingFunctions === false;
427
- });
428
- const accountSources = (await Promise.all(flattenSources(config.accounts ?? {}).map(async (source) => {
411
+ }
412
+ for (const source of flattenSources(config.accounts ?? {})) {
429
413
  const chain = chains.find((n) => n.name === source.chain);
430
414
  const fromBlock = await resolveBlockNumber(source.startBlock, chain);
431
415
  const toBlock = await resolveBlockNumber(source.endBlock, chain);
@@ -433,12 +417,12 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
433
417
  if (resolvedAddress === undefined) {
434
418
  throw new Error(`Validation failed: Account '${source.name}' must specify an 'address'.`);
435
419
  }
420
+ let address;
436
421
  if (typeof resolvedAddress === "object" &&
437
422
  !Array.isArray(resolvedAddress)) {
438
423
  const factoryFromBlock = (await resolveBlockNumber(resolvedAddress.startBlock, chain)) ??
439
424
  fromBlock;
440
- const factoryToBlock = (await resolveBlockNumber(resolvedAddress.endBlock, chain)) ??
441
- toBlock;
425
+ const factoryToBlock = (await resolveBlockNumber(resolvedAddress.endBlock, chain)) ?? toBlock;
442
426
  // Note that this can throw.
443
427
  const logFactory = buildLogFactory({
444
428
  chainId: chain.id,
@@ -446,184 +430,111 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
446
430
  fromBlock: factoryFromBlock,
447
431
  toBlock: factoryToBlock,
448
432
  });
449
- return [
450
- {
451
- type: "account",
452
- name: source.name,
453
- chain,
454
- filter: {
455
- type: "transaction",
456
- chainId: chain.id,
457
- fromAddress: undefined,
458
- toAddress: logFactory,
459
- includeReverted: false,
460
- fromBlock,
461
- toBlock,
462
- hasTransactionReceipt: true,
463
- include: defaultTransactionFilterInclude,
464
- },
465
- },
466
- {
467
- type: "account",
468
- name: source.name,
469
- chain,
470
- filter: {
471
- type: "transaction",
472
- chainId: chain.id,
473
- fromAddress: logFactory,
474
- toAddress: undefined,
475
- includeReverted: false,
476
- fromBlock,
477
- toBlock,
478
- hasTransactionReceipt: true,
479
- include: defaultTransactionFilterInclude,
480
- },
481
- },
482
- {
483
- type: "account",
484
- name: source.name,
485
- chain,
486
- filter: {
487
- type: "transfer",
488
- chainId: chain.id,
489
- fromAddress: undefined,
490
- toAddress: logFactory,
491
- includeReverted: false,
492
- fromBlock,
493
- toBlock,
494
- hasTransactionReceipt: source.includeTransactionReceipts ?? false,
495
- include: defaultTransferFilterInclude.concat(source.includeTransactionReceipts
496
- ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
497
- : []),
498
- },
499
- },
500
- {
501
- type: "account",
502
- name: source.name,
503
- chain,
504
- filter: {
505
- type: "transfer",
506
- chainId: chain.id,
507
- fromAddress: logFactory,
508
- toAddress: undefined,
509
- includeReverted: false,
510
- fromBlock,
511
- toBlock,
512
- hasTransactionReceipt: source.includeTransactionReceipts ?? false,
513
- include: defaultTransferFilterInclude.concat(source.includeTransactionReceipts
514
- ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
515
- : []),
516
- },
517
- },
518
- ];
519
- }
520
- for (const address of Array.isArray(resolvedAddress)
521
- ? resolvedAddress
522
- : [resolvedAddress]) {
523
- if (!address.startsWith("0x"))
524
- throw new Error(`Validation failed: Invalid prefix for address '${address}'. Got '${address.slice(0, 2)}', expected '0x'.`);
525
- if (address.length !== 42)
526
- throw new Error(`Validation failed: Invalid length for address '${address}'. Got ${address.length}, expected 42 characters.`);
527
- }
528
- const validatedAddress = Array.isArray(resolvedAddress)
529
- ? dedupe(resolvedAddress).map((r) => toLowerCase(r))
530
- : resolvedAddress !== undefined
531
- ? toLowerCase(resolvedAddress)
532
- : undefined;
533
- return [
433
+ address = logFactory;
434
+ }
435
+ else {
436
+ for (const address of Array.isArray(resolvedAddress)
437
+ ? resolvedAddress
438
+ : [resolvedAddress]) {
439
+ if (!address.startsWith("0x"))
440
+ throw new Error(`Validation failed: Invalid prefix for address '${address}'. Got '${address.slice(0, 2)}', expected '0x'.`);
441
+ if (address.length !== 42)
442
+ throw new Error(`Validation failed: Invalid length for address '${address}'. Got ${address.length}, expected 42 characters.`);
443
+ }
444
+ const validatedAddress = Array.isArray(resolvedAddress)
445
+ ? dedupe(resolvedAddress).map((r) => toLowerCase(r))
446
+ : resolvedAddress !== undefined
447
+ ? toLowerCase(resolvedAddress)
448
+ : undefined;
449
+ address = validatedAddress;
450
+ }
451
+ const filters = [
534
452
  {
535
- type: "account",
536
- name: source.name,
537
- chain,
538
- filter: {
539
- type: "transaction",
540
- chainId: chain.id,
541
- fromAddress: undefined,
542
- toAddress: validatedAddress,
543
- includeReverted: false,
544
- fromBlock,
545
- toBlock,
546
- hasTransactionReceipt: true,
547
- include: defaultTransactionFilterInclude,
548
- },
453
+ type: "transaction",
454
+ chainId: chain.id,
455
+ sourceId: source.name,
456
+ fromAddress: undefined,
457
+ toAddress: address,
458
+ includeReverted: false,
459
+ fromBlock,
460
+ toBlock,
461
+ hasTransactionReceipt: true,
462
+ include: defaultTransactionFilterInclude,
549
463
  },
550
464
  {
551
- type: "account",
552
- name: source.name,
553
- chain,
554
- filter: {
555
- type: "transaction",
556
- chainId: chain.id,
557
- fromAddress: validatedAddress,
558
- toAddress: undefined,
559
- includeReverted: false,
560
- fromBlock,
561
- toBlock,
562
- hasTransactionReceipt: true,
563
- include: defaultTransactionFilterInclude,
564
- },
465
+ type: "transaction",
466
+ chainId: chain.id,
467
+ sourceId: source.name,
468
+ fromAddress: address,
469
+ toAddress: undefined,
470
+ includeReverted: false,
471
+ fromBlock,
472
+ toBlock,
473
+ hasTransactionReceipt: true,
474
+ include: defaultTransactionFilterInclude,
565
475
  },
566
476
  {
567
- type: "account",
568
- name: source.name,
569
- chain,
570
- filter: {
571
- type: "transfer",
572
- chainId: chain.id,
573
- fromAddress: undefined,
574
- toAddress: validatedAddress,
575
- includeReverted: false,
576
- fromBlock,
577
- toBlock,
578
- hasTransactionReceipt: source.includeTransactionReceipts ?? false,
579
- include: defaultTransferFilterInclude.concat(source.includeTransactionReceipts
580
- ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
581
- : []),
582
- },
477
+ type: "transfer",
478
+ chainId: chain.id,
479
+ sourceId: source.name,
480
+ fromAddress: undefined,
481
+ toAddress: address,
482
+ includeReverted: false,
483
+ fromBlock,
484
+ toBlock,
485
+ hasTransactionReceipt: source.includeTransactionReceipts ?? false,
486
+ include: defaultTransferFilterInclude.concat(source.includeTransactionReceipts
487
+ ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
488
+ : []),
583
489
  },
584
490
  {
585
- type: "account",
586
- name: source.name,
587
- chain,
588
- filter: {
589
- type: "transfer",
590
- chainId: chain.id,
591
- fromAddress: validatedAddress,
592
- toAddress: undefined,
593
- includeReverted: false,
594
- fromBlock,
595
- toBlock,
596
- hasTransactionReceipt: source.includeTransactionReceipts ?? false,
597
- include: defaultTransferFilterInclude.concat(source.includeTransactionReceipts
598
- ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
599
- : []),
600
- },
491
+ type: "transfer",
492
+ chainId: chain.id,
493
+ sourceId: source.name,
494
+ fromAddress: address,
495
+ toAddress: undefined,
496
+ includeReverted: false,
497
+ fromBlock,
498
+ toBlock,
499
+ hasTransactionReceipt: source.includeTransactionReceipts ?? false,
500
+ include: defaultTransferFilterInclude.concat(source.includeTransactionReceipts
501
+ ? defaultTransactionReceiptInclude.map((value) => `transactionReceipt.${value}`)
502
+ : []),
601
503
  },
602
504
  ];
603
- })))
604
- .flat()
605
- .filter((source) => {
606
- const eventName = source.filter.type === "transaction"
607
- ? source.filter.fromAddress === undefined
608
- ? `${source.name}:transaction:to`
609
- : `${source.name}:transaction:from`
610
- : source.filter.fromAddress === undefined
611
- ? `${source.name}:transfer:to`
612
- : `${source.name}:transfer:from`;
613
- const hasRegisteredIndexingFunction = indexingFunctions[eventName] !== undefined;
614
- if (!hasRegisteredIndexingFunction) {
505
+ let hasRegisteredIndexingFunction = false;
506
+ for (const filter of filters) {
507
+ const eventName = filter.type === "transaction"
508
+ ? filter.fromAddress === undefined
509
+ ? `${source.name}:transaction:to`
510
+ : `${source.name}:transaction:from`
511
+ : filter.fromAddress === undefined
512
+ ? `${source.name}:transfer:to`
513
+ : `${source.name}:transfer:from`;
514
+ const indexingFunction = indexingFunctions.find((f) => f.name === eventName);
515
+ if (indexingFunction) {
516
+ hasRegisteredIndexingFunction = true;
517
+ const eventCallback = {
518
+ filter,
519
+ name: eventName,
520
+ fn: indexingFunction.fn,
521
+ chain,
522
+ type: "account",
523
+ direction: filter.fromAddress === undefined ? "to" : "from",
524
+ };
525
+ perChainEventCallbacks.get(chain.id).push(eventCallback);
526
+ }
527
+ }
528
+ if (hasRegisteredIndexingFunction === false) {
615
529
  logs.push({
616
- level: "debug",
530
+ level: "warn",
617
531
  msg: "No registered indexing functions",
618
- chain: source.chain.name,
619
- chain_id: source.chain.id,
620
- name: eventName,
621
- type: source.filter.type,
532
+ name: source.name,
533
+ type: "account",
622
534
  });
623
535
  }
624
- return hasRegisteredIndexingFunction;
625
- });
626
- const blockSources = (await Promise.all(flattenSources(config.blocks ?? {}).map(async (source) => {
536
+ }
537
+ for (const source of flattenSources(config.blocks ?? {})) {
627
538
  const chain = chains.find((n) => n.name === source.chain);
628
539
  const intervalMaybeNan = source.interval ?? 1;
629
540
  const interval = Number.isNaN(intervalMaybeNan) ? 0 : intervalMaybeNan;
@@ -632,50 +543,57 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
632
543
  }
633
544
  const fromBlock = await resolveBlockNumber(source.startBlock, chain);
634
545
  const toBlock = await resolveBlockNumber(source.endBlock, chain);
635
- return {
636
- type: "block",
637
- name: source.name,
638
- chain,
639
- filter: {
546
+ const eventName = `${source.name}:block`;
547
+ const indexingFunction = indexingFunctions.find((f) => f.name === eventName);
548
+ if (indexingFunction) {
549
+ const filter = {
640
550
  type: "block",
641
551
  chainId: chain.id,
552
+ sourceId: source.name,
642
553
  interval: interval,
643
554
  offset: (fromBlock ?? 0) % interval,
644
555
  fromBlock,
645
556
  toBlock,
646
557
  hasTransactionReceipt: false,
647
558
  include: defaultBlockFilterInclude,
648
- },
649
- };
650
- })))
651
- .flat()
652
- .filter((source) => {
653
- const hasRegisteredIndexingFunction = indexingFunctions[`${source.name}:block`] !== undefined;
654
- if (!hasRegisteredIndexingFunction) {
559
+ };
560
+ const eventCallback = {
561
+ filter,
562
+ name: eventName,
563
+ fn: indexingFunction.fn,
564
+ chain,
565
+ type: "block",
566
+ };
567
+ perChainEventCallbacks.get(chain.id).push(eventCallback);
568
+ }
569
+ else {
655
570
  logs.push({
656
571
  level: "warn",
657
572
  msg: "No registered indexing functions",
658
- chain: source.chain.name,
659
- chain_id: source.chain.id,
660
573
  name: source.name,
661
574
  type: "block",
662
575
  });
663
576
  }
664
- return hasRegisteredIndexingFunction;
665
- });
666
- const sources = [...contractSources, ...accountSources, ...blockSources];
577
+ }
667
578
  // Filter out any chains that don't have any sources registered.
668
579
  const chainsWithSources = [];
669
580
  const rpcsWithSources = [];
670
581
  const finalizedBlocksWithSources = [];
582
+ const eventCallbacksWithSources = [];
583
+ const setupCallbacksWithSources = [];
584
+ const contractsWithSources = [];
671
585
  for (let i = 0; i < chains.length; i++) {
672
586
  const chain = chains[i];
673
587
  const rpc = rpcs[i];
674
- const hasSources = sources.some((source) => source.chain.name === chain.name);
675
- if (hasSources) {
588
+ const hasIndexingFunctions = perChainEventCallbacks.get(chain.id).length > 0 ||
589
+ perChainSetupCallbacks.get(chain.id).length > 0;
590
+ if (hasIndexingFunctions) {
676
591
  chainsWithSources.push(chain);
677
592
  rpcsWithSources.push(rpc);
678
593
  finalizedBlocksWithSources.push(finalizedBlocks[i]);
594
+ eventCallbacksWithSources.push(perChainEventCallbacks.get(chain.id));
595
+ setupCallbacksWithSources.push(perChainSetupCallbacks.get(chain.id));
596
+ contractsWithSources.push(perChainContracts.get(chain.id));
679
597
  }
680
598
  else {
681
599
  logs.push({
@@ -686,15 +604,16 @@ export async function buildIndexingFunctions({ common, config, rawIndexingFuncti
686
604
  });
687
605
  }
688
606
  }
689
- if (Object.keys(indexingFunctions).length === 0) {
690
- throw new Error("Validation failed: Found 0 registered indexing functions.");
607
+ if (chainsWithSources.length === 0) {
608
+ throw new Error("Validation failed: Found 0 chains with registered indexing functions.");
691
609
  }
692
610
  return {
693
611
  chains: chainsWithSources,
694
612
  rpcs: rpcsWithSources,
695
613
  finalizedBlocks: finalizedBlocksWithSources,
696
- sources,
697
- indexingFunctions,
614
+ eventCallbacks: eventCallbacksWithSources,
615
+ setupCallbacks: setupCallbacksWithSources,
616
+ contracts: contractsWithSources,
698
617
  logs,
699
618
  };
700
619
  }
@@ -773,21 +692,22 @@ export function buildConfig({ common, config, }) {
773
692
  }));
774
693
  return { chains, rpcs, logs };
775
694
  }
776
- export async function safeBuildIndexingFunctions({ common, config, rawIndexingFunctions, configBuild, }) {
695
+ export async function safeBuildIndexingFunctions({ common, config, indexingFunctions, configBuild, }) {
777
696
  try {
778
697
  const result = await buildIndexingFunctions({
779
698
  common,
780
699
  config,
781
- rawIndexingFunctions,
700
+ indexingFunctions,
782
701
  configBuild,
783
702
  });
784
703
  return {
785
704
  status: "success",
786
- sources: result.sources,
787
705
  chains: result.chains,
788
706
  rpcs: result.rpcs,
789
707
  finalizedBlocks: result.finalizedBlocks,
790
- indexingFunctions: result.indexingFunctions,
708
+ eventCallbacks: result.eventCallbacks,
709
+ setupCallbacks: result.setupCallbacks,
710
+ contracts: result.contracts,
791
711
  logs: result.logs,
792
712
  };
793
713
  }