envio 3.7.0 → 3.9.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 (79) hide show
  1. package/README.md +2 -2
  2. package/index.d.ts +399 -156
  3. package/package.json +6 -6
  4. package/src/AddressRows.res +121 -0
  5. package/src/AddressRows.res.mjs +143 -0
  6. package/src/Batch.res +2 -0
  7. package/src/Batch.res.mjs +2 -1
  8. package/src/ChainState.res +82 -68
  9. package/src/ChainState.res.mjs +81 -65
  10. package/src/ChainState.resi +14 -9
  11. package/src/Config.res +41 -91
  12. package/src/Config.res.mjs +34 -68
  13. package/src/ContractMapping.res +66 -0
  14. package/src/ContractMapping.res.mjs +73 -0
  15. package/src/Core.res +20 -0
  16. package/src/Core.res.mjs +4 -0
  17. package/src/Envio.res +54 -76
  18. package/src/EventConfigBuilder.res +157 -30
  19. package/src/EventConfigBuilder.res.mjs +118 -24
  20. package/src/FetchState.res +59 -67
  21. package/src/FetchState.res.mjs +34 -44
  22. package/src/HandlerRegister.res +20 -11
  23. package/src/HandlerRegister.res.mjs +22 -5
  24. package/src/Hasura.res +30 -22
  25. package/src/Hasura.res.mjs +11 -5
  26. package/src/InMemoryStore.res +18 -36
  27. package/src/InMemoryStore.res.mjs +12 -26
  28. package/src/IndexerState.res +21 -1
  29. package/src/IndexerState.res.mjs +9 -3
  30. package/src/IndexerState.resi +1 -0
  31. package/src/Internal.res +33 -13
  32. package/src/Internal.res.mjs +12 -16
  33. package/src/Main.res +27 -20
  34. package/src/Main.res.mjs +19 -14
  35. package/src/MemoryStorage.res +78 -73
  36. package/src/MemoryStorage.res.mjs +66 -81
  37. package/src/Metrics.res +15 -1
  38. package/src/Metrics.res.mjs +5 -1
  39. package/src/Persistence.res +34 -18
  40. package/src/Persistence.res.mjs +8 -5
  41. package/src/PgStorage.res +189 -66
  42. package/src/PgStorage.res.mjs +93 -59
  43. package/src/Rollback.res +14 -10
  44. package/src/Rollback.res.mjs +4 -2
  45. package/src/SimulateItems.res +254 -9
  46. package/src/SimulateItems.res.mjs +214 -47
  47. package/src/TestIndexer.res +119 -121
  48. package/src/TestIndexer.res.mjs +95 -100
  49. package/src/Utils.res +7 -0
  50. package/src/Utils.res.mjs +9 -2
  51. package/src/Writing.res +2 -0
  52. package/src/Writing.res.mjs +2 -1
  53. package/src/bindings/ClickHouse.res +39 -6
  54. package/src/bindings/ClickHouse.res.mjs +29 -4
  55. package/src/bindings/NodeJs.res +9 -0
  56. package/src/bindings/NodeJs.res.mjs +8 -1
  57. package/src/bindings/Postgres.res +9 -0
  58. package/src/bindings/Postgres.res.mjs +3 -0
  59. package/src/db/EntityHistory.res +12 -18
  60. package/src/db/EntityHistory.res.mjs +3 -14
  61. package/src/db/InternalTable.res +182 -65
  62. package/src/db/InternalTable.res.mjs +183 -73
  63. package/src/db/Table.res +24 -0
  64. package/src/db/Table.res.mjs +20 -1
  65. package/src/sources/AddressSet.res +0 -22
  66. package/src/sources/AddressStore.res +48 -88
  67. package/src/sources/AddressStore.res.mjs +11 -22
  68. package/src/sources/EvmHyperSyncSource.res +3 -2
  69. package/src/sources/SimulateSource.res +8 -4
  70. package/src/sources/SimulateSource.res.mjs +7 -3
  71. package/src/sources/Svm.res +34 -7
  72. package/src/sources/Svm.res.mjs +32 -4
  73. package/src/sources/SvmHyperSyncClient.res +24 -30
  74. package/src/sources/SvmHyperSyncClient.res.mjs +3 -2
  75. package/src/sources/SvmHyperSyncSource.res +88 -36
  76. package/src/sources/SvmHyperSyncSource.res.mjs +71 -39
  77. package/src/sources/TransactionStore.res +38 -0
  78. package/src/sources/TransactionStore.res.mjs +5 -0
  79. package/svm.schema.json +37 -82
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.7.0",
3
+ "version": "3.9.0",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -69,10 +69,10 @@
69
69
  "tsx": "4.21.0"
70
70
  },
71
71
  "optionalDependencies": {
72
- "envio-linux-x64": "3.7.0",
73
- "envio-linux-x64-musl": "3.7.0",
74
- "envio-linux-arm64": "3.7.0",
75
- "envio-darwin-x64": "3.7.0",
76
- "envio-darwin-arm64": "3.7.0"
72
+ "envio-linux-x64": "3.9.0",
73
+ "envio-linux-x64-musl": "3.9.0",
74
+ "envio-linux-arm64": "3.9.0",
75
+ "envio-darwin-x64": "3.9.0",
76
+ "envio-darwin-arm64": "3.9.0"
77
77
  }
78
78
  }
@@ -0,0 +1,121 @@
1
+ type key = {
2
+ chainId: ChainId.t,
3
+ address: NodeJs.Buffer.t,
4
+ contractId: int,
5
+ }
6
+
7
+ type row = {
8
+ chainId: ChainId.t,
9
+ address: NodeJs.Buffer.t,
10
+ contractId: int,
11
+ registrationBlock: int,
12
+ }
13
+
14
+ type staged = {
15
+ row: row,
16
+ checkpointId: Internal.checkpointId,
17
+ }
18
+
19
+ type seedRows = {
20
+ addresses: array<NodeJs.Buffer.t>,
21
+ contractIds: array<int>,
22
+ registrationBlocks: array<int>,
23
+ }
24
+
25
+ let emptySeedRows = (): seedRows => {
26
+ addresses: [],
27
+ contractIds: [],
28
+ registrationBlocks: [],
29
+ }
30
+
31
+ // Config addresses are registered by no event. Rollback never reaches them.
32
+ let configRegistrationBlock = -1
33
+
34
+ let keyOf = (row: row): key => {
35
+ chainId: row.chainId,
36
+ address: row.address,
37
+ contractId: row.contractId,
38
+ }
39
+
40
+ let packBuffers = (addresses: array<NodeJs.Buffer.t>) => (
41
+ NodeJs.Buffer.concat(addresses),
42
+ addresses->Array.map(address => address->NodeJs.Buffer.length),
43
+ )
44
+
45
+ let packKeys = (rows: array<row>) => packBuffers(rows->Array.map(row => row.address))
46
+
47
+ let seedRowsOf = (rows: array<row>): seedRows => {
48
+ let addresses = []
49
+ let contractIds = []
50
+ let registrationBlocks = []
51
+ rows->Array.forEach(row => {
52
+ addresses->Array.push(row.address)->ignore
53
+ contractIds->Array.push(row.contractId)->ignore
54
+ registrationBlocks->Array.push(row.registrationBlock)->ignore
55
+ })
56
+ {addresses, contractIds, registrationBlocks}
57
+ }
58
+
59
+ // Rows arrive clustered per chain. Normalizing a chain id is a schema parse.
60
+ let group = (rows: array<row>): dict<seedRows> => {
61
+ let rowsByChain: dict<array<row>> = Dict.make()
62
+ let runChainId = ref(None)
63
+ let runKey = ref("")
64
+ rows->Array.forEach(row => {
65
+ if runChainId.contents !== Some(row.chainId) {
66
+ runChainId := Some(row.chainId)
67
+ runKey := row.chainId->ChainId.normalizeOrThrow->ChainId.toString
68
+ }
69
+ rowsByChain->Utils.Dict.push(runKey.contents, row)
70
+ })
71
+ rowsByChain->Utils.Dict.mapValues(seedRowsOf)
72
+ }
73
+
74
+ module Table = {
75
+ type t = dict<row>
76
+
77
+ let dictKey = (key: key) =>
78
+ `${key.chainId->ChainId.toString}|${key.contractId->Int.toString}|${key.address->NodeJs.Buffer.toBase64}`
79
+
80
+ let make = (): t => Dict.make()
81
+
82
+ let clear = (table: t) => table->Utils.Dict.clearInPlace
83
+
84
+ let insert = (table: t, row: row) => {
85
+ let key = row->keyOf->dictKey
86
+ switch table->Utils.Dict.dangerouslyGetNonOption(key) {
87
+ | Some(_) => ()
88
+ | None => table->Dict.set(key, row)
89
+ }
90
+ }
91
+
92
+ let insertMany = (table: t, rows: array<row>) => rows->Array.forEach(row => table->insert(row))
93
+
94
+ let delete = (table: t, key: key) => table->Utils.Dict.deleteInPlace(key->dictKey)
95
+
96
+ let rows = (table: t): array<row> => table->Dict.valuesToArray
97
+
98
+ let groupByChain = (table: t) => table->rows->group
99
+ }
100
+
101
+ let render = (rows: array<row>, ~ecosystem: string, ~shouldChecksum: bool) => {
102
+ let (bytes, lengths) = rows->packKeys
103
+ Core.getAddon().renderAddresses(~ecosystem, ~shouldChecksum, ~bytes, ~lengths)
104
+ }
105
+
106
+ let renderOfContract = (
107
+ rows: seedRows,
108
+ ~ecosystem: string,
109
+ ~shouldChecksum: bool,
110
+ ~contractId: int,
111
+ ) => {
112
+ let (bytes, lengths) = packBuffers(rows.addresses)
113
+ Core.getAddon().renderContractAddresses(
114
+ ~ecosystem,
115
+ ~shouldChecksum,
116
+ ~bytes,
117
+ ~lengths,
118
+ ~contractIds=rows.contractIds,
119
+ ~contractId,
120
+ )
121
+ }
@@ -0,0 +1,143 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Core from "./Core.res.mjs";
4
+ import * as Utils from "./Utils.res.mjs";
5
+ import * as ChainId from "./ChainId.res.mjs";
6
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
7
+
8
+ function emptySeedRows() {
9
+ return {
10
+ addresses: [],
11
+ contractIds: [],
12
+ registrationBlocks: []
13
+ };
14
+ }
15
+
16
+ function keyOf(row) {
17
+ return {
18
+ chainId: row.chainId,
19
+ address: row.address,
20
+ contractId: row.contractId
21
+ };
22
+ }
23
+
24
+ function packBuffers(addresses) {
25
+ return [
26
+ Buffer.concat(addresses),
27
+ addresses.map(address => address.length)
28
+ ];
29
+ }
30
+
31
+ function packKeys(rows) {
32
+ return packBuffers(rows.map(row => row.address));
33
+ }
34
+
35
+ function seedRowsOf(rows) {
36
+ let addresses = [];
37
+ let contractIds = [];
38
+ let registrationBlocks = [];
39
+ rows.forEach(row => {
40
+ addresses.push(row.address);
41
+ contractIds.push(row.contractId);
42
+ registrationBlocks.push(row.registrationBlock);
43
+ });
44
+ return {
45
+ addresses: addresses,
46
+ contractIds: contractIds,
47
+ registrationBlocks: registrationBlocks
48
+ };
49
+ }
50
+
51
+ function group(rows) {
52
+ let rowsByChain = {};
53
+ let runChainId = {
54
+ contents: undefined
55
+ };
56
+ let runKey = {
57
+ contents: ""
58
+ };
59
+ rows.forEach(row => {
60
+ if (runChainId.contents !== Primitive_option.some(row.chainId)) {
61
+ runChainId.contents = Primitive_option.some(row.chainId);
62
+ runKey.contents = ChainId.toString(ChainId.normalizeOrThrow(row.chainId));
63
+ }
64
+ Utils.Dict.push(rowsByChain, runKey.contents, row);
65
+ });
66
+ return Utils.Dict.mapValues(rowsByChain, seedRowsOf);
67
+ }
68
+
69
+ function dictKey(key) {
70
+ return ChainId.toString(key.chainId) + `|` + key.contractId.toString() + `|` + key.address.toString("base64");
71
+ }
72
+
73
+ function make() {
74
+ return {};
75
+ }
76
+
77
+ function clear(table) {
78
+ Utils.Dict.clearInPlace(table);
79
+ }
80
+
81
+ function insert(table, row) {
82
+ let key = dictKey(keyOf(row));
83
+ let match = table[key];
84
+ if (match !== undefined) {
85
+ return;
86
+ } else {
87
+ table[key] = row;
88
+ return;
89
+ }
90
+ }
91
+
92
+ function insertMany(table, rows) {
93
+ rows.forEach(row => insert(table, row));
94
+ }
95
+
96
+ function $$delete(table, key) {
97
+ Utils.Dict.deleteInPlace(table, dictKey(key));
98
+ }
99
+
100
+ function rows(table) {
101
+ return Object.values(table);
102
+ }
103
+
104
+ function groupByChain(table) {
105
+ return group(Object.values(table));
106
+ }
107
+
108
+ let Table = {
109
+ dictKey: dictKey,
110
+ make: make,
111
+ clear: clear,
112
+ insert: insert,
113
+ insertMany: insertMany,
114
+ $$delete: $$delete,
115
+ rows: rows,
116
+ groupByChain: groupByChain
117
+ };
118
+
119
+ function render(rows, ecosystem, shouldChecksum) {
120
+ let match = packKeys(rows);
121
+ return Core.getAddon().renderAddresses(ecosystem, shouldChecksum, match[0], match[1]);
122
+ }
123
+
124
+ function renderOfContract(rows, ecosystem, shouldChecksum, contractId) {
125
+ let match = packBuffers(rows.addresses);
126
+ return Core.getAddon().renderContractAddresses(ecosystem, shouldChecksum, match[0], match[1], rows.contractIds, contractId);
127
+ }
128
+
129
+ let configRegistrationBlock = -1;
130
+
131
+ export {
132
+ emptySeedRows,
133
+ configRegistrationBlock,
134
+ keyOf,
135
+ packBuffers,
136
+ packKeys,
137
+ seedRowsOf,
138
+ group,
139
+ Table,
140
+ render,
141
+ renderOfContract,
142
+ }
143
+ /* Core Not a pure module */
package/src/Batch.res CHANGED
@@ -43,6 +43,7 @@ type t = {
43
43
  checkpointBlockNumbers: array<int>,
44
44
  checkpointBlockHashes: array<Null.t<string>>,
45
45
  checkpointEventsProcessed: array<int>,
46
+ registeredAddresses: array<AddressRows.staged>,
46
47
  }
47
48
 
48
49
  let getProgressedChainsById = {
@@ -326,6 +327,7 @@ let prepareBatch = (
326
327
  checkpointBlockNumbers,
327
328
  checkpointBlockHashes,
328
329
  checkpointEventsProcessed,
330
+ registeredAddresses: [],
329
331
  }
330
332
  }
331
333
 
package/src/Batch.res.mjs CHANGED
@@ -185,7 +185,8 @@ function prepareBatch(checkpointIdBeforeBatch, chainsBeforeBatch, batchSizeTarge
185
185
  checkpointChainIds: checkpointChainIds,
186
186
  checkpointBlockNumbers: checkpointBlockNumbers,
187
187
  checkpointBlockHashes: checkpointBlockHashes,
188
- checkpointEventsProcessed: checkpointEventsProcessed
188
+ checkpointEventsProcessed: checkpointEventsProcessed,
189
+ registeredAddresses: []
189
190
  };
190
191
  }
191
192
 
@@ -62,20 +62,48 @@ type t = {
62
62
  mutable progressLatencyMs: option<int>,
63
63
  }
64
64
 
65
- let configAddresses = (chainConfig: Config.chain): array<Internal.indexingAddress> => {
65
+ // The chain's config-declared addresses as storage rows: what a clean run
66
+ // writes into `envio_addresses`. The keys are encoded by the Rust codec, so the
67
+ // bytes a clean run writes are exactly the bytes a resume seeds from.
68
+ let configStorageRows = (
69
+ chainConfig: Config.chain,
70
+ ~ecosystem: Ecosystem.name,
71
+ ~contractMapping: ContractMapping.t,
72
+ ): array<AddressRows.row> => {
66
73
  let addresses = []
74
+ let contractIds = []
67
75
  chainConfig.contracts->Array.forEach(contract => {
76
+ let contractId = contractMapping->ContractMapping.idOfOrThrow(contract.name)
68
77
  contract.addresses->Array.forEach(address => {
69
- addresses->Array.push({
70
- Internal.address,
71
- contractName: contract.name,
72
- registrationBlock: -1,
73
- })
78
+ addresses->Array.push(address)->ignore
79
+ contractIds->Array.push(contractId)->ignore
74
80
  })
75
81
  })
76
- addresses
82
+ Core.getAddon().encodeAddresses(
83
+ ~ecosystem=(ecosystem :> string),
84
+ ~addresses,
85
+ )->Array.mapWithIndex((address, idx) => {
86
+ AddressRows.chainId: chainConfig.id,
87
+ address,
88
+ contractId: contractIds->Array.getUnsafe(idx),
89
+ registrationBlock: AddressRows.configRegistrationBlock,
90
+ })
77
91
  }
78
92
 
93
+ // Seeds an in-memory address table with every chain's config-declared
94
+ // addresses, the twin of what `PgStorage.initialize` writes in one insert.
95
+ let seedConfigAddresses = (
96
+ table: AddressRows.Table.t,
97
+ ~chainConfigs: array<Config.chain>,
98
+ ~ecosystem: Ecosystem.name,
99
+ ~contractMapping: ContractMapping.t,
100
+ ) =>
101
+ chainConfigs->Array.forEach(chainConfig =>
102
+ table->AddressRows.Table.insertMany(
103
+ chainConfig->configStorageRows(~ecosystem, ~contractMapping),
104
+ )
105
+ )
106
+
79
107
  let validateOnEventRegistrations = (
80
108
  ~chainId: ChainId.t,
81
109
  registrations: array<Internal.onEventRegistration>,
@@ -142,22 +170,12 @@ let make = (
142
170
  }
143
171
  }
144
172
 
145
- // Every chain's contracts, not just one chain's: `context.chain.X.add` accepts
146
- // any config contract, whichever chain declared it, so every chain's address
147
- // store has to hold the whole set.
148
- let configContractNames = (config: Config.t) => {
149
- let names = Utils.Set.make()
150
- config.chainMap
151
- ->ChainMap.values
152
- ->Array.forEach(chain =>
153
- chain.contracts->Array.forEach(contract => names->Utils.Set.add(contract.name)->ignore)
154
- )
155
- names->Utils.Set.toArray
156
- }
157
-
158
173
  let makeInternal = (
159
174
  ~chainConfig: Config.chain,
160
- ~indexingAddresses: array<Internal.indexingAddress>,
175
+ ~addressRows: AddressRows.seedRows,
176
+ // Passed rather than read off `config`, because a resume must use the mapping
177
+ // storage holds: the ids stored rows carry, not the ids this config derives.
178
+ ~contractMapping: ContractMapping.t,
161
179
  ~startBlock,
162
180
  ~endBlock,
163
181
  ~firstEventBlock=None,
@@ -199,16 +217,13 @@ let makeInternal = (
199
217
  let addressStore = AddressStore.make(
200
218
  ~ecosystem=config.ecosystem.name,
201
219
  ~shouldChecksum=!lowercaseAddresses,
202
- ~contracts=AddressStore.contractsOf(
203
- ~onEventRegistrations,
204
- ~configContractNames=config->configContractNames,
205
- ),
220
+ ~contracts=AddressStore.contractsOf(~onEventRegistrations, ~contractMapping),
206
221
  )
207
222
 
208
223
  let fetchState = FetchState.make(
209
224
  ~maxAddrInPartition=config.maxAddrInPartition,
210
225
  ~addressStore,
211
- ~addresses=indexingAddresses,
226
+ ~addressRows,
212
227
  ~progressBlockNumber,
213
228
  ~startBlock,
214
229
  ~endBlock,
@@ -301,13 +316,15 @@ let makeInternal = (
301
316
  }),
302
317
  ]
303
318
  }
304
- | Config.SimulateSourceConfig({items, endBlock}) => [
319
+ | Config.SimulateSourceConfig({items, endBlock, ?transactionStore, ?blockStore}) => [
305
320
  SimulateSource.make(
306
321
  ~items,
307
322
  ~endBlock,
308
323
  ~chainId,
309
324
  ~addressStore,
310
325
  ~ecosystem=config.ecosystem.name,
326
+ ~transactionStore,
327
+ ~blockStore,
311
328
  ),
312
329
  ]
313
330
  // For tests: use ready-to-use sources directly
@@ -357,7 +374,7 @@ let makeInternal = (
357
374
  ~chainReorgCheckpoints,
358
375
  ),
359
376
  ~committedProgressBlockNumber=progressBlockNumber,
360
- ~perChainEntities=config.allEntities->EntityTables.perChain,
377
+ ~perChainEntities=config.userEntities->EntityTables.perChain,
361
378
  ~timestampCaughtUpToHeadOrEndblock,
362
379
  ~numEventsProcessed,
363
380
  ~transactionStore=TransactionStore.make(
@@ -371,33 +388,6 @@ let makeInternal = (
371
388
  )
372
389
  }
373
390
 
374
- let makeFromConfig = (
375
- chainConfig: Config.chain,
376
- ~config,
377
- ~registrationsByChainId,
378
- ~knownHeight,
379
- ) => {
380
- let logger = Logging.createChild(~params={"chainId": chainConfig.id})
381
-
382
- makeInternal(
383
- ~chainConfig,
384
- ~config,
385
- ~registrationsByChainId,
386
- ~startBlock=chainConfig.startBlock,
387
- ~endBlock=chainConfig.endBlock,
388
- ~reorgCheckpoints=[],
389
- ~maxReorgDepth=chainConfig.maxReorgDepth,
390
- ~progressBlockNumber=-1,
391
- ~timestampCaughtUpToHeadOrEndblock=None,
392
- ~numEventsProcessed=0.,
393
- ~logger,
394
- ~indexingAddresses=configAddresses(chainConfig),
395
- ~isInReorgThreshold=false,
396
- ~isRealtime=false,
397
- ~knownHeight,
398
- )
399
- }
400
-
401
391
  /**
402
392
  * This function allows a chain state to be created from metadata, in particular this is useful for restarting an indexer and making sure it fetches blocks from the same place.
403
393
  */
@@ -408,6 +398,7 @@ let makeFromDbState = (
408
398
  ~isInReorgThreshold,
409
399
  ~isRealtime,
410
400
  ~config,
401
+ ~contractMapping,
411
402
  ~registrationsByChainId,
412
403
  ~reducedPollingInterval=?,
413
404
  ) => {
@@ -421,7 +412,8 @@ let makeFromDbState = (
421
412
  : resumedChainState.startBlock - 1
422
413
 
423
414
  makeInternal(
424
- ~indexingAddresses=resumedChainState.indexingAddresses,
415
+ ~addressRows=resumedChainState.addressRows,
416
+ ~contractMapping,
425
417
  ~chainConfig,
426
418
  ~startBlock=resumedChainState.startBlock,
427
419
  ~endBlock=resumedChainState.endBlock,
@@ -505,7 +497,7 @@ let contractAddresses = (cs: t, ~contractName) =>
505
497
 
506
498
  // Hands over the dynamically registered addresses the database hasn't seen yet,
507
499
  // up to the block the caller is about to commit, each paired with the checkpoint
508
- // that owns its row. Destructive: the caller must persist them or take the
500
+ // that registered it. Destructive: the caller must persist them or take the
509
501
  // indexer down. Throws with nothing consumed when a registration's block has no
510
502
  // checkpoint in the batch.
511
503
  let drainAddressesForWrite = (cs: t, ~toBlockInclusive, ~checkpointBlockNumbers) =>
@@ -852,6 +844,12 @@ let applyTransactionGroups = async (store: TransactionStore.t, g: transactionGro
852
844
  g.payloadGroups->Array.forEachWithIndex((payloads, i) => {
853
845
  let tx = txs->Array.getUnsafe(i)
854
846
  payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
847
+ switch tx
848
+ ->(Utils.magic: Internal.eventTransaction => dict<unknown>)
849
+ ->Dict.get("accountActivities") {
850
+ | Some(_) => payloads->Array.forEach(payload => Svm.attachAccountActivities(payload, tx))
851
+ | None => ()
852
+ }
855
853
  })
856
854
  } else {
857
855
  g.payloadGroups->Array.forEach(payloads =>
@@ -1019,6 +1017,9 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
1019
1017
  startBlock: cs.fetchState.startBlock,
1020
1018
  endBlock: cs.fetchState.endBlock,
1021
1019
  numAddresses: cs.addressStore->AddressStore.size,
1020
+ addressesByContract: cs.addressStore
1021
+ ->AddressStore.contractCounts
1022
+ ->Array.map(({contractName, count}) => (contractName, count)),
1022
1023
  isReady: cs->isReady,
1023
1024
  sourceBlockNumber: cs.fetchState.knownHeight,
1024
1025
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -1209,13 +1210,30 @@ let markReady = (cs: t, ~readyAt) =>
1209
1210
  // with no diff entry still rewinds fetch state + stores to the target -
1210
1211
  // otherwise the stale block hash stays in the store and re-triggers the same
1211
1212
  // reorg.
1213
+ // Returns the address registrations the storage has to delete along with it —
1214
+ // the store is what decides which ones died, so the two halves of a rollback
1215
+ // can't disagree.
1212
1216
  let rollback = (
1213
1217
  cs: t,
1214
1218
  ~newProgressBlockNumber,
1215
1219
  ~eventsProcessedDiff,
1216
1220
  ~rollbackTargetBlockNumber,
1217
1221
  ~isReorgChain,
1218
- ) => {
1222
+ ): array<AddressRows.key> => {
1223
+ let rollbackTo = targetBlockNumber => {
1224
+ let {fetchState, rolledBackAddresses} =
1225
+ cs.fetchState->FetchState.rollback(
1226
+ ~rolledBackAddressStore=cs.addressStore,
1227
+ ~targetBlockNumber,
1228
+ )
1229
+ cs.fetchState = fetchState
1230
+ rolledBackAddresses->Array.map(({address, contractId}): AddressRows.key => {
1231
+ chainId: cs.chainConfig.id,
1232
+ address,
1233
+ contractId,
1234
+ })
1235
+ }
1236
+
1219
1237
  switch newProgressBlockNumber {
1220
1238
  | Some(newProgressBlockNumber) =>
1221
1239
  let newTotalEventsProcessed =
@@ -1235,23 +1253,16 @@ let rollback = (
1235
1253
  )
1236
1254
  | None => ()
1237
1255
  }
1238
- cs.fetchState =
1239
- cs.fetchState->FetchState.rollback(
1240
- ~addressStore=cs.addressStore,
1241
- ~targetBlockNumber=newProgressBlockNumber,
1242
- )
1256
+ let rolledBackAddresses = rollbackTo(newProgressBlockNumber)
1243
1257
  cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
1244
1258
  cs.blockStore->BlockStore.rollback(newProgressBlockNumber)
1245
1259
  cs.committedProgressBlockNumber = newProgressBlockNumber
1246
1260
  cs.processingBlockNumber = newProgressBlockNumber
1247
1261
  cs.numEventsProcessed = newTotalEventsProcessed
1262
+ rolledBackAddresses
1248
1263
  | None =>
1249
1264
  if isReorgChain {
1250
- cs.fetchState =
1251
- cs.fetchState->FetchState.rollback(
1252
- ~addressStore=cs.addressStore,
1253
- ~targetBlockNumber=rollbackTargetBlockNumber,
1254
- )
1265
+ let rolledBackAddresses = rollbackTo(rollbackTargetBlockNumber)
1255
1266
  cs.transactionStore->TransactionStore.rollback(rollbackTargetBlockNumber)
1256
1267
  cs.blockStore->BlockStore.rollback(rollbackTargetBlockNumber)
1257
1268
  cs.committedProgressBlockNumber = Pervasives.min(
@@ -1259,6 +1270,9 @@ let rollback = (
1259
1270
  rollbackTargetBlockNumber,
1260
1271
  )
1261
1272
  cs.processingBlockNumber = Pervasives.min(cs.processingBlockNumber, rollbackTargetBlockNumber)
1273
+ rolledBackAddresses
1274
+ } else {
1275
+ []
1262
1276
  }
1263
1277
  }
1264
1278
  }