envio 3.7.0 → 3.9.0-resolvers.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 (118) hide show
  1. package/README.md +2 -2
  2. package/evm.schema.json +7 -0
  3. package/fuel.schema.json +7 -0
  4. package/index.d.ts +674 -161
  5. package/index.js +10 -0
  6. package/package.json +6 -7
  7. package/src/AddressRows.res +121 -0
  8. package/src/AddressRows.res.mjs +143 -0
  9. package/src/Batch.res +11 -0
  10. package/src/Batch.res.mjs +2 -1
  11. package/src/Bin.res +47 -0
  12. package/src/Bin.res.mjs +34 -1
  13. package/src/ChainState.res +108 -98
  14. package/src/ChainState.res.mjs +86 -67
  15. package/src/ChainState.resi +28 -15
  16. package/src/Config.res +61 -131
  17. package/src/Config.res.mjs +47 -79
  18. package/src/ContractMapping.res +66 -0
  19. package/src/ContractMapping.res.mjs +73 -0
  20. package/src/Core.res +24 -0
  21. package/src/Core.res.mjs +4 -0
  22. package/src/Ecosystem.res +7 -2
  23. package/src/Env.res +128 -1
  24. package/src/Env.res.mjs +123 -1
  25. package/src/Envio.res +54 -76
  26. package/src/EventConfigBuilder.res +316 -46
  27. package/src/EventConfigBuilder.res.mjs +231 -28
  28. package/src/FetchState.res +59 -67
  29. package/src/FetchState.res.mjs +34 -44
  30. package/src/HandlerRegister.res +26 -15
  31. package/src/HandlerRegister.res.mjs +27 -7
  32. package/src/Hasura.res +30 -22
  33. package/src/Hasura.res.mjs +11 -5
  34. package/src/InMemoryStore.res +23 -38
  35. package/src/InMemoryStore.res.mjs +13 -27
  36. package/src/IndexerState.res +40 -3
  37. package/src/IndexerState.res.mjs +30 -4
  38. package/src/IndexerState.resi +4 -1
  39. package/src/Internal.res +49 -30
  40. package/src/Internal.res.mjs +12 -16
  41. package/src/Main.res +27 -20
  42. package/src/Main.res.mjs +19 -14
  43. package/src/Metrics.res +15 -1
  44. package/src/Metrics.res.mjs +5 -1
  45. package/src/Persistence.res +46 -21
  46. package/src/Persistence.res.mjs +8 -5
  47. package/src/PgStorage.res +238 -79
  48. package/src/PgStorage.res.mjs +115 -73
  49. package/src/PruneStaleHistory.res +1 -1
  50. package/src/PruneStaleHistory.res.mjs +1 -1
  51. package/src/Rollback.res +62 -41
  52. package/src/Rollback.res.mjs +61 -19
  53. package/src/RollbackCommit.res +7 -9
  54. package/src/RollbackCommit.res.mjs +5 -6
  55. package/src/SimulateItems.res +254 -9
  56. package/src/SimulateItems.res.mjs +214 -47
  57. package/src/Sink.res +35 -26
  58. package/src/Sink.res.mjs +20 -16
  59. package/src/TestIndexer.res +121 -123
  60. package/src/TestIndexer.res.mjs +97 -102
  61. package/src/Utils.res +38 -0
  62. package/src/Utils.res.mjs +39 -2
  63. package/src/Writing.res +4 -2
  64. package/src/Writing.res.mjs +3 -2
  65. package/src/bindings/ClickHouse.res +311 -656
  66. package/src/bindings/ClickHouse.res.mjs +294 -395
  67. package/src/bindings/ClickHouseSink.res +303 -0
  68. package/src/bindings/ClickHouseSink.res.mjs +216 -0
  69. package/src/bindings/NodeJs.res +9 -0
  70. package/src/bindings/NodeJs.res.mjs +8 -1
  71. package/src/bindings/Postgres.res +9 -0
  72. package/src/bindings/Postgres.res.mjs +3 -0
  73. package/src/db/EntityHistory.res +29 -24
  74. package/src/db/EntityHistory.res.mjs +11 -19
  75. package/src/db/InternalTable.res +258 -83
  76. package/src/db/InternalTable.res.mjs +223 -85
  77. package/src/db/RollbackScope.res +29 -0
  78. package/src/db/RollbackScope.res.mjs +31 -0
  79. package/src/db/Table.res +28 -1
  80. package/src/db/Table.res.mjs +26 -2
  81. package/src/resolvers/ResolverProcess.res +809 -0
  82. package/src/resolvers/ResolverProcess.res.mjs +667 -0
  83. package/src/resolvers/ResolverQuery.res +317 -0
  84. package/src/resolvers/ResolverQuery.res.mjs +220 -0
  85. package/src/resolvers/collisions.js +108 -0
  86. package/src/resolvers/db.js +363 -0
  87. package/src/resolvers/dispatch.js +165 -0
  88. package/src/resolvers/errors.js +30 -0
  89. package/src/resolvers/graphqlSelection.js +292 -0
  90. package/src/resolvers/hasuraAction.js +84 -0
  91. package/src/resolvers/hasuraApply.js +323 -0
  92. package/src/resolvers/hasuraMetadata.js +119 -0
  93. package/src/resolvers/index.js +95 -0
  94. package/src/resolvers/manifest.js +370 -0
  95. package/src/resolvers/server.js +270 -0
  96. package/src/sources/AddressSet.res +0 -22
  97. package/src/sources/AddressStore.res +48 -88
  98. package/src/sources/AddressStore.res.mjs +11 -22
  99. package/src/sources/Evm.res +2 -0
  100. package/src/sources/Evm.res.mjs +2 -0
  101. package/src/sources/EvmHyperSyncSource.res +3 -2
  102. package/src/sources/Fuel.res +2 -0
  103. package/src/sources/Fuel.res.mjs +2 -0
  104. package/src/sources/SimulateSource.res +8 -4
  105. package/src/sources/SimulateSource.res.mjs +7 -3
  106. package/src/sources/Svm.res +36 -7
  107. package/src/sources/Svm.res.mjs +34 -4
  108. package/src/sources/SvmHyperSyncClient.res +20 -116
  109. package/src/sources/SvmHyperSyncClient.res.mjs +5 -7
  110. package/src/sources/SvmHyperSyncSource.res +89 -37
  111. package/src/sources/SvmHyperSyncSource.res.mjs +71 -39
  112. package/src/sources/TransactionStore.res +38 -0
  113. package/src/sources/TransactionStore.res.mjs +5 -0
  114. package/src/tui/Tui.res +1 -1
  115. package/src/tui/Tui.res.mjs +1 -1
  116. package/svm.schema.json +44 -156
  117. package/src/MemoryStorage.res +0 -724
  118. package/src/MemoryStorage.res.mjs +0 -601
@@ -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,
@@ -295,19 +310,25 @@ let makeInternal = (
295
310
  chainId,
296
311
  endpointUrl: hypersyncUrl,
297
312
  apiToken,
298
- onEventRegistrations,
313
+ onEventRegistrations: onEventRegistrations->(
314
+ Utils.magic: array<Internal.onEventRegistration> => array<
315
+ Internal.svmOnEventRegistration,
316
+ >
317
+ ),
299
318
  clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
300
319
  addressStore,
301
320
  }),
302
321
  ]
303
322
  }
304
- | Config.SimulateSourceConfig({items, endBlock}) => [
323
+ | Config.SimulateSourceConfig({items, endBlock, ?transactionStore, ?blockStore}) => [
305
324
  SimulateSource.make(
306
325
  ~items,
307
326
  ~endBlock,
308
327
  ~chainId,
309
328
  ~addressStore,
310
329
  ~ecosystem=config.ecosystem.name,
330
+ ~transactionStore,
331
+ ~blockStore,
311
332
  ),
312
333
  ]
313
334
  // For tests: use ready-to-use sources directly
@@ -357,7 +378,7 @@ let makeInternal = (
357
378
  ~chainReorgCheckpoints,
358
379
  ),
359
380
  ~committedProgressBlockNumber=progressBlockNumber,
360
- ~perChainEntities=config.allEntities->EntityTables.perChain,
381
+ ~perChainEntities=config.userEntities->EntityTables.perChain,
361
382
  ~timestampCaughtUpToHeadOrEndblock,
362
383
  ~numEventsProcessed,
363
384
  ~transactionStore=TransactionStore.make(
@@ -371,33 +392,6 @@ let makeInternal = (
371
392
  )
372
393
  }
373
394
 
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
395
  /**
402
396
  * 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
397
  */
@@ -408,6 +402,7 @@ let makeFromDbState = (
408
402
  ~isInReorgThreshold,
409
403
  ~isRealtime,
410
404
  ~config,
405
+ ~contractMapping,
411
406
  ~registrationsByChainId,
412
407
  ~reducedPollingInterval=?,
413
408
  ) => {
@@ -421,7 +416,8 @@ let makeFromDbState = (
421
416
  : resumedChainState.startBlock - 1
422
417
 
423
418
  makeInternal(
424
- ~indexingAddresses=resumedChainState.indexingAddresses,
419
+ ~addressRows=resumedChainState.addressRows,
420
+ ~contractMapping,
425
421
  ~chainConfig,
426
422
  ~startBlock=resumedChainState.startBlock,
427
423
  ~endBlock=resumedChainState.endBlock,
@@ -505,7 +501,7 @@ let contractAddresses = (cs: t, ~contractName) =>
505
501
 
506
502
  // Hands over the dynamically registered addresses the database hasn't seen yet,
507
503
  // 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
504
+ // that registered it. Destructive: the caller must persist them or take the
509
505
  // indexer down. Throws with nothing consumed when a registration's block has no
510
506
  // checkpoint in the batch.
511
507
  let drainAddressesForWrite = (cs: t, ~toBlockInclusive, ~checkpointBlockNumbers) =>
@@ -852,6 +848,12 @@ let applyTransactionGroups = async (store: TransactionStore.t, g: transactionGro
852
848
  g.payloadGroups->Array.forEachWithIndex((payloads, i) => {
853
849
  let tx = txs->Array.getUnsafe(i)
854
850
  payloads->Array.forEach(payload => payload->Internal.setPayloadTransaction(tx))
851
+ switch tx
852
+ ->(Utils.magic: Internal.eventTransaction => dict<unknown>)
853
+ ->Dict.get("accountActivities") {
854
+ | Some(_) => payloads->Array.forEach(payload => Svm.attachAccountActivities(payload, tx))
855
+ | None => ()
856
+ }
855
857
  })
856
858
  } else {
857
859
  g.payloadGroups->Array.forEach(payloads =>
@@ -1019,6 +1021,9 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
1019
1021
  startBlock: cs.fetchState.startBlock,
1020
1022
  endBlock: cs.fetchState.endBlock,
1021
1023
  numAddresses: cs.addressStore->AddressStore.size,
1024
+ addressesByContract: cs.addressStore
1025
+ ->AddressStore.contractCounts
1026
+ ->Array.map(({contractName, count}) => (contractName, count)),
1022
1027
  isReady: cs->isReady,
1023
1028
  sourceBlockNumber: cs.fetchState.knownHeight,
1024
1029
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -1204,27 +1209,40 @@ let markReady = (cs: t, ~readyAt) =>
1204
1209
  cs.timestampCaughtUpToHeadOrEndblock = Some(readyAt)
1205
1210
  }
1206
1211
 
1207
- // Roll a chain back to a reorg target. With a progress diff, restore fetch/
1208
- // safe-checkpoint/progress state to `newProgressBlockNumber`. A reorg chain
1209
- // with no diff entry still rewinds fetch state + stores to the target -
1210
- // otherwise the stale block hash stays in the store and re-triggers the same
1211
- // reorg.
1212
- let rollback = (
1213
- cs: t,
1214
- ~newProgressBlockNumber,
1215
- ~eventsProcessedDiff,
1216
- ~rollbackTargetBlockNumber,
1217
- ~isReorgChain,
1218
- ) => {
1219
- switch newProgressBlockNumber {
1220
- | Some(newProgressBlockNumber) =>
1221
- let newTotalEventsProcessed =
1222
- cs.numEventsProcessed -.
1223
- // Both dicts are populated together per progress-diff row, so a chain with
1224
- // a progress diff always has an events-processed diff too.
1225
- eventsProcessedDiff->Option.getOrThrow(
1226
- ~message="Missing events-processed diff for rolled-back chain",
1212
+ type progressDiff = {blockNumber: int, eventsProcessed: float}
1213
+
1214
+ type rolledBackTo =
1215
+ | RecomputedProgress(progressDiff)
1216
+ | ForkBlock(int)
1217
+ | Untouched
1218
+
1219
+ // Returns the address registrations the storage has to delete along with the
1220
+ // chain — the store is what decides which ones died, so the two halves of a
1221
+ // rollback can't disagree.
1222
+ let rollback = (cs: t, ~rolledBackTo: rolledBackTo): array<AddressRows.key> => {
1223
+ let rollbackTo = targetBlockNumber => {
1224
+ let {fetchState, rolledBackAddresses} =
1225
+ cs.fetchState->FetchState.rollback(
1226
+ ~rolledBackAddressStore=cs.addressStore,
1227
+ ~targetBlockNumber,
1227
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
+
1237
+ switch rolledBackTo {
1238
+ | RecomputedProgress({blockNumber, eventsProcessed}) =>
1239
+ // A rollback only ever takes a chain back. The diff recomputes progress from
1240
+ // the checkpoints still in the database, so a chain that an unwritten
1241
+ // rollback already took below them — to a fork block only that rollback
1242
+ // knew — would be handed their MIN back and moved forward onto blocks it
1243
+ // never re-indexed.
1244
+ let newProgressBlockNumber = Pervasives.min(blockNumber, cs.committedProgressBlockNumber)
1245
+ let newTotalEventsProcessed = cs.numEventsProcessed -. eventsProcessed
1228
1246
 
1229
1247
  switch cs.safeCheckpointTracking {
1230
1248
  | Some(safeCheckpointTracking) =>
@@ -1235,30 +1253,22 @@ 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
1248
- | None =>
1249
- if isReorgChain {
1250
- cs.fetchState =
1251
- cs.fetchState->FetchState.rollback(
1252
- ~addressStore=cs.addressStore,
1253
- ~targetBlockNumber=rollbackTargetBlockNumber,
1254
- )
1255
- cs.transactionStore->TransactionStore.rollback(rollbackTargetBlockNumber)
1256
- cs.blockStore->BlockStore.rollback(rollbackTargetBlockNumber)
1257
- cs.committedProgressBlockNumber = Pervasives.min(
1258
- cs.committedProgressBlockNumber,
1259
- rollbackTargetBlockNumber,
1260
- )
1261
- cs.processingBlockNumber = Pervasives.min(cs.processingBlockNumber, rollbackTargetBlockNumber)
1262
- }
1262
+ rolledBackAddresses
1263
+ | ForkBlock(forkBlock) =>
1264
+ // Rewinds the stores too, not just progress: otherwise the stale block hash
1265
+ // survives and re-triggers the same reorg.
1266
+ let rolledBackAddresses = rollbackTo(forkBlock)
1267
+ cs.transactionStore->TransactionStore.rollback(forkBlock)
1268
+ cs.blockStore->BlockStore.rollback(forkBlock)
1269
+ cs.committedProgressBlockNumber = Pervasives.min(cs.committedProgressBlockNumber, forkBlock)
1270
+ cs.processingBlockNumber = Pervasives.min(cs.processingBlockNumber, forkBlock)
1271
+ rolledBackAddresses
1272
+ | Untouched => []
1263
1273
  }
1264
1274
  }
@@ -2,15 +2,16 @@
2
2
 
3
3
  import * as Env from "./Env.res.mjs";
4
4
  import * as Svm from "./sources/Svm.res.mjs";
5
+ import * as Core from "./Core.res.mjs";
5
6
  import * as Batch from "./Batch.res.mjs";
6
7
  import * as Utils from "./Utils.res.mjs";
7
8
  import * as ChainId from "./ChainId.res.mjs";
8
9
  import * as Logging from "./Logging.res.mjs";
9
- import * as ChainMap from "./ChainMap.res.mjs";
10
10
  import * as EvmChain from "./sources/EvmChain.res.mjs";
11
11
  import * as FieldMask from "./sources/FieldMask.res.mjs";
12
12
  import * as BlockStore from "./sources/BlockStore.res.mjs";
13
13
  import * as FetchState from "./FetchState.res.mjs";
14
+ import * as AddressRows from "./AddressRows.res.mjs";
14
15
  import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
15
16
  import * as AddressStore from "./sources/AddressStore.res.mjs";
16
17
  import * as EntityTables from "./EntityTables.res.mjs";
@@ -20,6 +21,7 @@ import * as SourceManager from "./sources/SourceManager.res.mjs";
20
21
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
21
22
  import * as SimulateSource from "./sources/SimulateSource.res.mjs";
22
23
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
24
+ import * as ContractMapping from "./ContractMapping.res.mjs";
23
25
  import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
24
26
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
25
27
  import * as TransactionStore from "./sources/TransactionStore.res.mjs";
@@ -27,18 +29,26 @@ import * as SvmHyperSyncSource from "./sources/SvmHyperSyncSource.res.mjs";
27
29
  import * as FuelHyperSyncSource from "./sources/FuelHyperSyncSource.res.mjs";
28
30
  import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
29
31
 
30
- function configAddresses(chainConfig) {
32
+ function configStorageRows(chainConfig, ecosystem, contractMapping) {
31
33
  let addresses = [];
34
+ let contractIds = [];
32
35
  chainConfig.contracts.forEach(contract => {
36
+ let contractId = ContractMapping.idOfOrThrow(contractMapping, contract.name, undefined);
33
37
  contract.addresses.forEach(address => {
34
- addresses.push({
35
- address: address,
36
- contractName: contract.name,
37
- registrationBlock: -1
38
- });
38
+ addresses.push(address);
39
+ contractIds.push(contractId);
39
40
  });
40
41
  });
41
- return addresses;
42
+ return Core.getAddon().encodeAddresses(ecosystem, addresses).map((address, idx) => ({
43
+ chainId: chainConfig.id,
44
+ address: address,
45
+ contractId: contractIds[idx],
46
+ registrationBlock: AddressRows.configRegistrationBlock
47
+ }));
48
+ }
49
+
50
+ function seedConfigAddresses(table, chainConfigs, ecosystem, contractMapping) {
51
+ chainConfigs.forEach(chainConfig => AddressRows.Table.insertMany(table, configStorageRows(chainConfig, ecosystem, contractMapping)));
42
52
  }
43
53
 
44
54
  function validateOnEventRegistrations(chainId, registrations) {
@@ -94,17 +104,21 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, so
94
104
  };
95
105
  }
96
106
 
97
- function configContractNames(config) {
98
- let names = new Set();
99
- ChainMap.values(config.chainMap).forEach(chain => {
100
- chain.contracts.forEach(contract => {
101
- names.add(contract.name);
102
- });
107
+ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInReorgThreshold, isRealtime, config, contractMapping, registrationsByChainId, reducedPollingInterval) {
108
+ let chainId = chainConfig.id;
109
+ let logger = Logging.createChild({
110
+ chainId: chainId
103
111
  });
104
- return Array.from(names);
105
- }
106
-
107
- function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firstEventBlockOpt, progressBlockNumber, config, registrationsByChainId, logger, timestampCaughtUpToHeadOrEndblock, numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, maxReorgDepth, knownHeightOpt, isResumedOpt, reducedPollingInterval) {
112
+ let progressBlockNumber = resumedChainState.progressBlockNumber >= 0 ? resumedChainState.progressBlockNumber : resumedChainState.startBlock - 1 | 0;
113
+ let addressRows = resumedChainState.addressRows;
114
+ let startBlock = resumedChainState.startBlock;
115
+ let endBlock = resumedChainState.endBlock;
116
+ let firstEventBlockOpt = Primitive_option.some(resumedChainState.firstEventBlockNumber);
117
+ let timestampCaughtUpToHeadOrEndblock = resumedChainState.timestampCaughtUpToHeadOrEndblock;
118
+ let numEventsProcessed = resumedChainState.numEventsProcessed;
119
+ let maxReorgDepth = resumedChainState.maxReorgDepth;
120
+ let knownHeightOpt = resumedChainState.sourceBlockNumber;
121
+ let isResumedOpt = true;
108
122
  let firstEventBlock = firstEventBlockOpt !== undefined ? Primitive_option.valFromOption(firstEventBlockOpt) : undefined;
109
123
  let knownHeight = knownHeightOpt !== undefined ? knownHeightOpt : 0;
110
124
  let isResumed = isResumedOpt !== undefined ? isResumedOpt : false;
@@ -120,7 +134,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
120
134
  }
121
135
  });
122
136
  let lowercaseAddresses = config.lowercaseAddresses;
123
- let addressStore = AddressStore.make(config.ecosystem.name, !lowercaseAddresses, AddressStore.contractsOf(onEventRegistrations, configContractNames(config)));
137
+ let addressStore = AddressStore.make(config.ecosystem.name, !lowercaseAddresses, AddressStore.contractsOf(onEventRegistrations, contractMapping));
124
138
  let match$1 = config.ecosystem.name;
125
139
  let tmp;
126
140
  switch (match$1) {
@@ -132,13 +146,13 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
132
146
  tmp = undefined;
133
147
  break;
134
148
  }
135
- let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, indexingAddresses, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
149
+ let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, addressRows, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp), isResumed);
136
150
  let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
137
151
  if (reorgCheckpoint.chain_id === chainConfig.id) {
138
152
  return reorgCheckpoint;
139
153
  }
140
154
  });
141
- let chainId = chainConfig.id;
155
+ let chainId$1 = chainConfig.id;
142
156
  let sources = chainConfig.sourceConfig;
143
157
  let sources$1;
144
158
  switch (sources.TAG) {
@@ -155,11 +169,11 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
155
169
  headers: headers
156
170
  };
157
171
  });
158
- sources$1 = EvmChain.makeSources(chainId, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
172
+ sources$1 = EvmChain.makeSources(chainId$1, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
159
173
  break;
160
174
  case "FuelSourceConfig" :
161
175
  sources$1 = [FuelHyperSyncSource.make({
162
- chainId: chainId,
176
+ chainId: chainId$1,
163
177
  endpointUrl: sources.hypersync,
164
178
  apiToken: Env.envioApiToken,
165
179
  onEventRegistrations: onEventRegistrations,
@@ -170,18 +184,18 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
170
184
  let rpc = sources.rpc;
171
185
  let hypersync = sources.hypersync;
172
186
  sources$1 = hypersync !== undefined ? [SvmHyperSyncSource.make({
173
- chainId: chainId,
187
+ chainId: chainId$1,
174
188
  endpointUrl: hypersync,
175
189
  apiToken: Env.envioApiToken,
176
190
  onEventRegistrations: onEventRegistrations,
177
191
  clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
178
192
  addressStore: addressStore
179
193
  })] : (
180
- rpc !== undefined ? [Svm.makeRPCSource(chainId, rpc, undefined)] : Stdlib_JsError.throwWithMessage(`Chain ` + ChainId.toString(chainId) + ` has no SVM data source`)
194
+ rpc !== undefined ? [Svm.makeRPCSource(chainId$1, rpc, undefined)] : Stdlib_JsError.throwWithMessage(`Chain ` + ChainId.toString(chainId$1) + ` has no SVM data source`)
181
195
  );
182
196
  break;
183
197
  case "SimulateSourceConfig" :
184
- sources$1 = [SimulateSource.make(sources.items, sources.endBlock, chainId, addressStore, config.ecosystem.name)];
198
+ sources$1 = [SimulateSource.make(sources.items, sources.endBlock, chainId$1, addressStore, config.ecosystem.name, Primitive_option.some(sources.transactionStore), Primitive_option.some(sources.blockStore))];
185
199
  break;
186
200
  case "CustomSources" :
187
201
  sources$1 = sources._0;
@@ -197,23 +211,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
197
211
  }
198
212
  let firstEventBlock$1 = fetchState.firstEventBlock;
199
213
  let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
200
- return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), config.shouldRollbackOnReorg, maxReorgDepth, numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(blockStore), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.allEntities), logger);
201
- }
202
-
203
- function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
204
- let logger = Logging.createChild({
205
- chainId: chainConfig.id
206
- });
207
- return makeInternal(chainConfig, configAddresses(chainConfig), chainConfig.startBlock, chainConfig.endBlock, undefined, -1, config, registrationsByChainId, logger, undefined, 0, false, false, [], chainConfig.maxReorgDepth, knownHeight, undefined, undefined);
208
- }
209
-
210
- function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInReorgThreshold, isRealtime, config, registrationsByChainId, reducedPollingInterval) {
211
- let chainId = chainConfig.id;
212
- let logger = Logging.createChild({
213
- chainId: chainId
214
- });
215
- let progressBlockNumber = resumedChainState.progressBlockNumber >= 0 ? resumedChainState.progressBlockNumber : resumedChainState.startBlock - 1 | 0;
216
- return makeInternal(chainConfig, resumedChainState.indexingAddresses, resumedChainState.startBlock, resumedChainState.endBlock, Primitive_option.some(resumedChainState.firstEventBlockNumber), progressBlockNumber, config, registrationsByChainId, logger, resumedChainState.timestampCaughtUpToHeadOrEndblock, resumedChainState.numEventsProcessed, isInReorgThreshold, isRealtime, reorgCheckpoints, resumedChainState.maxReorgDepth, resumedChainState.sourceBlockNumber, true, reducedPollingInterval);
214
+ return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), config.shouldRollbackOnReorg, maxReorgDepth, numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(blockStore), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.userEntities), logger);
217
215
  }
218
216
 
219
217
  function logger(cs) {
@@ -567,6 +565,11 @@ async function applyTransactionGroups(store, g) {
567
565
  payloads.forEach(payload => {
568
566
  payload.transaction = tx;
569
567
  });
568
+ let match = tx["accountActivities"];
569
+ if (match !== undefined) {
570
+ payloads.forEach(payload => Svm.attachAccountActivities(payload, tx));
571
+ return;
572
+ }
570
573
  });
571
574
  return;
572
575
  }
@@ -716,6 +719,10 @@ function toMetrics(cs) {
716
719
  startBlock: cs.fetchState.startBlock,
717
720
  endBlock: cs.fetchState.endBlock,
718
721
  numAddresses: cs.addressStore.size(),
722
+ addressesByContract: cs.addressStore.contractCounts().map(param => [
723
+ param.contractName,
724
+ param.count
725
+ ]),
719
726
  isReady: cs.timestampCaughtUpToHeadOrEndblock !== undefined,
720
727
  sourceBlockNumber: cs.fetchState.knownHeight,
721
728
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -850,36 +857,48 @@ function markReady(cs, readyAt) {
850
857
  }
851
858
  }
852
859
 
853
- function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTargetBlockNumber, isReorgChain) {
854
- if (newProgressBlockNumber === undefined) {
855
- if (isReorgChain) {
856
- cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, rollbackTargetBlockNumber);
857
- cs.transactionStore.rollback(rollbackTargetBlockNumber);
858
- cs.blockStore.rollback(rollbackTargetBlockNumber);
859
- cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
860
- cs.processingBlockNumber = Primitive_int.min(cs.processingBlockNumber, rollbackTargetBlockNumber);
861
- return;
862
- } else {
863
- return;
864
- }
860
+ function rollback(cs, rolledBackTo) {
861
+ let rollbackTo = targetBlockNumber => {
862
+ let match = FetchState.rollback(cs.fetchState, cs.addressStore, targetBlockNumber);
863
+ cs.fetchState = match.fetchState;
864
+ return match.rolledBackAddresses.map(param => ({
865
+ chainId: cs.chainConfig.id,
866
+ address: param.address,
867
+ contractId: param.contractId
868
+ }));
869
+ };
870
+ if (typeof rolledBackTo !== "object") {
871
+ return [];
865
872
  }
866
- let newTotalEventsProcessed = cs.numEventsProcessed - Stdlib_Option.getOrThrow(eventsProcessedDiff, "Missing events-processed diff for rolled-back chain");
867
- let safeCheckpointTracking = cs.safeCheckpointTracking;
868
- if (safeCheckpointTracking !== undefined) {
869
- cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
873
+ if (rolledBackTo.TAG === "RecomputedProgress") {
874
+ let match = rolledBackTo._0;
875
+ let newProgressBlockNumber = Primitive_int.min(match.blockNumber, cs.committedProgressBlockNumber);
876
+ let newTotalEventsProcessed = cs.numEventsProcessed - match.eventsProcessed;
877
+ let safeCheckpointTracking = cs.safeCheckpointTracking;
878
+ if (safeCheckpointTracking !== undefined) {
879
+ cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
880
+ }
881
+ let rolledBackAddresses = rollbackTo(newProgressBlockNumber);
882
+ cs.transactionStore.rollback(newProgressBlockNumber);
883
+ cs.blockStore.rollback(newProgressBlockNumber);
884
+ cs.committedProgressBlockNumber = newProgressBlockNumber;
885
+ cs.processingBlockNumber = newProgressBlockNumber;
886
+ cs.numEventsProcessed = newTotalEventsProcessed;
887
+ return rolledBackAddresses;
870
888
  }
871
- cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, newProgressBlockNumber);
872
- cs.transactionStore.rollback(newProgressBlockNumber);
873
- cs.blockStore.rollback(newProgressBlockNumber);
874
- cs.committedProgressBlockNumber = newProgressBlockNumber;
875
- cs.processingBlockNumber = newProgressBlockNumber;
876
- cs.numEventsProcessed = newTotalEventsProcessed;
889
+ let forkBlock = rolledBackTo._0;
890
+ let rolledBackAddresses$1 = rollbackTo(forkBlock);
891
+ cs.transactionStore.rollback(forkBlock);
892
+ cs.blockStore.rollback(forkBlock);
893
+ cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, forkBlock);
894
+ cs.processingBlockNumber = Primitive_int.min(cs.processingBlockNumber, forkBlock);
895
+ return rolledBackAddresses$1;
877
896
  }
878
897
 
879
898
  export {
880
- configAddresses,
899
+ configStorageRows,
900
+ seedConfigAddresses,
881
901
  make,
882
- makeFromConfig,
883
902
  makeFromDbState,
884
903
  logger,
885
904
  blockStore,
@@ -3,7 +3,18 @@
3
3
 
4
4
  type t
5
5
 
6
- let configAddresses: Config.chain => array<Internal.indexingAddress>
6
+ let configStorageRows: (
7
+ Config.chain,
8
+ ~ecosystem: Ecosystem.name,
9
+ ~contractMapping: ContractMapping.t,
10
+ ) => array<AddressRows.row>
11
+
12
+ let seedConfigAddresses: (
13
+ AddressRows.Table.t,
14
+ ~chainConfigs: array<Config.chain>,
15
+ ~ecosystem: Ecosystem.name,
16
+ ~contractMapping: ContractMapping.t,
17
+ ) => unit
7
18
 
8
19
  let make: (
9
20
  ~chainConfig: Config.chain,
@@ -26,13 +37,6 @@ let make: (
26
37
  ~logger: Pino.t,
27
38
  ) => t
28
39
 
29
- let makeFromConfig: (
30
- Config.chain,
31
- ~config: Config.t,
32
- ~registrationsByChainId: HandlerRegister.registrationsByChainId,
33
- ~knownHeight: int,
34
- ) => t
35
-
36
40
  let makeFromDbState: (
37
41
  Config.chain,
38
42
  ~resumedChainState: Persistence.initialChainState,
@@ -40,6 +44,7 @@ let makeFromDbState: (
40
44
  ~isInReorgThreshold: bool,
41
45
  ~isRealtime: bool,
42
46
  ~config: Config.t,
47
+ ~contractMapping: ContractMapping.t,
43
48
  ~registrationsByChainId: HandlerRegister.registrationsByChainId,
44
49
  ~reducedPollingInterval: int=?,
45
50
  ) => t
@@ -152,10 +157,18 @@ let enterReorgThreshold: t => unit
152
157
  let advanceAfterBatch: (t, ~batch: Batch.t, ~enteringReorgThreshold: bool) => unit
153
158
  let applyBatchProgress: (t, ~batch: Batch.t, ~blockTimestampName: string) => unit
154
159
  let markReady: (t, ~readyAt: Date.t) => unit
155
- let rollback: (
156
- t,
157
- ~newProgressBlockNumber: option<int>,
158
- ~eventsProcessedDiff: option<float>,
159
- ~rollbackTargetBlockNumber: int,
160
- ~isReorgChain: bool,
161
- ) => unit
160
+ // What a rollback's progress diff says about one chain: the block its surviving
161
+ // checkpoints put it back to, and the events the deleted ones carried away. One
162
+ // record because one diff row yields both — they are never known separately.
163
+ type progressDiff = {blockNumber: int, eventsProcessed: float}
164
+
165
+ // Where a rollback leaves one chain.
166
+ type rolledBackTo =
167
+ | RecomputedProgress(progressDiff)
168
+ // The reorg chain with no checkpoint left at or below the target, so only the
169
+ // fork block says where it goes.
170
+ | ForkBlock(int)
171
+ // A chain this rollback doesn't reach.
172
+ | Untouched
173
+
174
+ let rollback: (t, ~rolledBackTo: rolledBackTo) => array<AddressRows.key>