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
@@ -130,11 +130,13 @@ let prepareRollbackDiff = async (
130
130
  ~rollbackTargetCheckpointId,
131
131
  ~rollbackDiffCheckpointId,
132
132
  ~progressBlockNumberByChainId,
133
+ ~rolledBackAddresses,
133
134
  ) => {
134
135
  state->IndexerState.beginRollbackDiff(
135
136
  ~targetCheckpointId=rollbackTargetCheckpointId,
136
137
  ~diffCheckpointId=rollbackDiffCheckpointId,
137
138
  ~progressBlockNumberByChainId,
139
+ ~rolledBackAddresses,
138
140
  )
139
141
  let persistence = state->IndexerState.persistence
140
142
  let committedCheckpointId = state->IndexerState.committedCheckpointId
@@ -148,7 +150,7 @@ let prepareRollbackDiff = async (
148
150
  let _ = await persistence.allEntities
149
151
  ->Array.filter(entityConfig => entityConfig.storage.postgres)
150
152
  ->Array.map(async entityConfig => {
151
- let (removals, restoredEntitiesResult) = await persistence.storage.getRollbackData(
153
+ let (removals, restoredEntities) = await persistence.storage.getRollbackData(
152
154
  ~entityConfig,
153
155
  ~rollbackTargetCheckpointId,
154
156
  )
@@ -166,11 +168,6 @@ let prepareRollbackDiff = async (
166
168
  )
167
169
  })
168
170
 
169
- let restoredEntities =
170
- restoredEntitiesResult
171
- ->S.parseOrThrow(entityConfig.table->Table.pgRowsSchema)
172
- ->(Utils.magic: array<unknown> => array<Internal.entity>)
173
-
174
171
  restoredEntities->Array.forEach((entity: Internal.entity) => {
175
172
  let scope = entity->takeRowScope(~entityConfig)
176
173
  setEntities->Utils.Dict.push(entityConfig.name, entity.id)
@@ -199,12 +196,6 @@ let prepareRollbackDiff = async (
199
196
  // registered them: the store is where they already live, and it knows which
200
197
  // ones the database hasn't seen yet.
201
198
  let setBatchDcs = (state: IndexerState.t, ~batch: Batch.t) => {
202
- let inMemTable = state->getInMemTable(
203
- ~entityConfig=InternalTable.EnvioAddresses.entityConfig,
204
- ~scope=CrossChain,
205
- )
206
- let committedCheckpointId = state->IndexerState.committedCheckpointId
207
-
208
199
  batch.progressedChainsById->Utils.Dict.forEach(progressedChain => {
209
200
  let chainId = progressedChain.fetchState.chainId
210
201
  let chainState = state->IndexerState.getChainState(~chainId)
@@ -224,31 +215,22 @@ let setBatchDcs = (state: IndexerState.t, ~batch: Batch.t) => {
224
215
  }
225
216
  }
226
217
 
227
- chainState
228
- ->ChainState.drainAddressesForWrite(
229
- ~toBlockInclusive=progressedChain.progressBlockNumber,
230
- ~checkpointBlockNumbers,
231
- )
232
- ->Array.forEach(dc => {
233
- let entity: InternalTable.EnvioAddresses.t = {
234
- id: InternalTable.EnvioAddresses.makeId(~chainId, ~address=dc.address),
235
- chainId,
236
- contractName: dc.contractName,
237
- registrationBlock: dc.registrationBlock,
238
- // Only ever written, never read back. Kept on the table so the column
239
- // doesn't need a migration.
240
- registrationLogIndex: -1,
241
- }
242
-
243
- inMemTable->InMemoryTable.Entity.set(
244
- ~committedCheckpointId,
245
- Set({
246
- entityId: entity.id->EntityId.unsafeOfString,
247
- checkpointId: checkpointIds->Array.getUnsafe(dc.checkpointIdx),
248
- entity: entity->InternalTable.EnvioAddresses.castToInternal,
249
- }),
218
+ batch.registeredAddresses->Array.pushMany(
219
+ chainState
220
+ ->ChainState.drainAddressesForWrite(
221
+ ~toBlockInclusive=progressedChain.progressBlockNumber,
222
+ ~checkpointBlockNumbers,
250
223
  )
251
- })
224
+ ->Array.map((dc): AddressRows.staged => {
225
+ row: {
226
+ chainId,
227
+ address: dc.address,
228
+ contractId: dc.contractId,
229
+ registrationBlock: dc.registrationBlock,
230
+ },
231
+ checkpointId: checkpointIds->Array.getUnsafe(dc.checkpointIdx),
232
+ }),
233
+ )->ignore
252
234
  }
253
235
  })
254
236
  }
@@ -2,7 +2,6 @@
2
2
 
3
3
  import * as Table from "./db/Table.res.mjs";
4
4
  import * as Utils from "./Utils.res.mjs";
5
- import * as Config from "./Config.res.mjs";
6
5
  import * as Internal from "./Internal.res.mjs";
7
6
  import * as ChainState from "./ChainState.res.mjs";
8
7
  import * as EffectState from "./EffectState.res.mjs";
@@ -12,7 +11,6 @@ import * as InMemoryTable from "./InMemoryTable.res.mjs";
12
11
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
13
12
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
14
13
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
15
- import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
16
14
 
17
15
  function getInMemTable(state, entityConfig, scope) {
18
16
  let tmp;
@@ -108,8 +106,8 @@ function dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromD
108
106
  inMemTable.changesCount = inMemTable.changesCount - keysToDelete.length;
109
107
  }
110
108
 
111
- async function prepareRollbackDiff(state, rollbackTargetCheckpointId, rollbackDiffCheckpointId, progressBlockNumberByChainId) {
112
- IndexerState.beginRollbackDiff(state, rollbackTargetCheckpointId, rollbackDiffCheckpointId, progressBlockNumberByChainId);
109
+ async function prepareRollbackDiff(state, rollbackTargetCheckpointId, rollbackDiffCheckpointId, progressBlockNumberByChainId, rolledBackAddresses) {
110
+ IndexerState.beginRollbackDiff(state, rollbackTargetCheckpointId, rollbackDiffCheckpointId, progressBlockNumberByChainId, rolledBackAddresses);
113
111
  let persistence = IndexerState.persistence(state);
114
112
  let committedCheckpointId = IndexerState.committedCheckpointId(state);
115
113
  let deletedEntities = {};
@@ -125,8 +123,7 @@ async function prepareRollbackDiff(state, rollbackTargetCheckpointId, rollbackDi
125
123
  checkpointId: rollbackDiffCheckpointId
126
124
  });
127
125
  });
128
- let restoredEntities = S$RescriptSchema.parseOrThrow(match[1], Table.pgRowsSchema(entityConfig.table));
129
- restoredEntities.forEach(entity => {
126
+ match[1].forEach(entity => {
130
127
  let scope = takeRowScope(entity, entityConfig);
131
128
  Utils.Dict.push(setEntities, entityConfig.name, entity.id);
132
129
  InMemoryTable.Entity.set(getInMemTable(state, entityConfig, scope), committedCheckpointId, {
@@ -144,8 +141,6 @@ async function prepareRollbackDiff(state, rollbackTargetCheckpointId, rollbackDi
144
141
  }
145
142
 
146
143
  function setBatchDcs(state, batch) {
147
- let inMemTable = getInMemTable(state, Config.EnvioAddresses.entityConfig, "crossChain");
148
- let committedCheckpointId = IndexerState.committedCheckpointId(state);
149
144
  Utils.Dict.forEach(batch.progressedChainsById, progressedChain => {
150
145
  let chainId = progressedChain.fetchState.chainId;
151
146
  let chainState = IndexerState.getChainState(state, chainId);
@@ -160,24 +155,15 @@ function setBatchDcs(state, batch) {
160
155
  checkpointBlockNumbers.push(batch.checkpointBlockNumbers[idx]);
161
156
  }
162
157
  }
163
- ChainState.drainAddressesForWrite(chainState, progressedChain.progressBlockNumber, checkpointBlockNumbers).forEach(dc => {
164
- let entity_id = Config.EnvioAddresses.makeId(chainId, dc.address);
165
- let entity_registration_block = dc.registrationBlock;
166
- let entity_contract_name = dc.contractName;
167
- let entity = {
168
- id: entity_id,
169
- chain_id: chainId,
170
- registration_block: entity_registration_block,
171
- registration_log_index: -1,
172
- contract_name: entity_contract_name
173
- };
174
- InMemoryTable.Entity.set(inMemTable, committedCheckpointId, {
175
- type: "SET",
176
- entityId: entity_id,
177
- entity: entity,
178
- checkpointId: checkpointIds[dc.checkpointIdx]
179
- });
180
- });
158
+ batch.registeredAddresses.push(...ChainState.drainAddressesForWrite(chainState, progressedChain.progressBlockNumber, checkpointBlockNumbers).map(dc => ({
159
+ row: {
160
+ chainId: chainId,
161
+ address: dc.address,
162
+ contractId: dc.contractId,
163
+ registrationBlock: dc.registrationBlock
164
+ },
165
+ checkpointId: checkpointIds[dc.checkpointIdx]
166
+ })));
181
167
  });
182
168
  }
183
169
 
@@ -267,6 +267,7 @@ let makeFromDbState = (
267
267
  ~isInReorgThreshold,
268
268
  ~isRealtime,
269
269
  ~config,
270
+ ~contractMapping=initialState.contractMapping,
270
271
  ~registrationsByChainId,
271
272
  ~reducedPollingInterval?,
272
273
  ),
@@ -757,6 +758,7 @@ let drainBatchRun = (state: t): Batch.t => {
757
758
  let checkpointBlockNumbers = []
758
759
  let checkpointBlockHashes = []
759
760
  let checkpointEventsProcessed = []
761
+ let registeredAddresses = []
760
762
  all->Array.forEach(batch => {
761
763
  // Once one batch lands in rest, all later ones follow it, preserving order.
762
764
  if rest->Utils.Array.isEmpty && batch.isInReorgThreshold == isInReorgThreshold {
@@ -770,6 +772,7 @@ let drainBatchRun = (state: t): Batch.t => {
770
772
  checkpointBlockNumbers->Array.pushMany(batch.checkpointBlockNumbers)
771
773
  checkpointBlockHashes->Array.pushMany(batch.checkpointBlockHashes)
772
774
  checkpointEventsProcessed->Array.pushMany(batch.checkpointEventsProcessed)
775
+ registeredAddresses->Array.pushMany(batch.registeredAddresses)
773
776
  } else {
774
777
  rest->Array.push(batch)
775
778
  }
@@ -786,6 +789,7 @@ let drainBatchRun = (state: t): Batch.t => {
786
789
  checkpointBlockNumbers,
787
790
  checkpointBlockHashes,
788
791
  checkpointEventsProcessed,
792
+ registeredAddresses,
789
793
  }
790
794
  }
791
795
 
@@ -797,7 +801,13 @@ let takeRollback = (state: t): option<Persistence.rollback> => {
797
801
  }
798
802
 
799
803
  // Advance the committed (durably persisted) frontier after a successful write.
800
- let markCommitted = (state: t, ~upToCheckpointId) => state.committedCheckpointId = upToCheckpointId
804
+ // Written rows leave the buffer only once the transaction that holds them has
805
+ // committed. A failed write keeps them, and re-inserting a row the database
806
+ // already has is a no-op. Rows staged while the write was in flight belong to
807
+ // later checkpoints — ids only ever grow — so this can't drop one unwritten.
808
+ let markCommitted = (state: t, ~upToCheckpointId) => {
809
+ state.committedCheckpointId = upToCheckpointId
810
+ }
801
811
 
802
812
  // Reset the in-memory tables and arm the rollback diff that the next write commits.
803
813
  let beginRollbackDiff = (
@@ -805,15 +815,25 @@ let beginRollbackDiff = (
805
815
  ~targetCheckpointId,
806
816
  ~diffCheckpointId,
807
817
  ~progressBlockNumberByChainId,
818
+ ~rolledBackAddresses,
808
819
  ) => {
809
820
  let perChainEntities = state.allEntities->EntityTables.perChain
810
821
  state.entities = EntityTables.make(state.allEntities->EntityTables.crossChain)
811
822
  state->chainStates->Utils.Dict.forEach(cs => cs->ChainState.resetEntities(~perChainEntities))
812
823
  state.effectState->EffectState.resetForRollback
824
+ // The address store reports only what each rollback killed, and never
825
+ // re-reports a registration it already tombstoned. A rollback that lands
826
+ // before the previous one has been written must therefore carry the earlier
827
+ // keys too, or their rows outlive every rollback that could delete them.
828
+ let rolledBackAddresses = switch state.rollback {
829
+ | Some({rolledBackAddresses: pending}) => pending->Array.concat(rolledBackAddresses)
830
+ | None => rolledBackAddresses
831
+ }
813
832
  state.rollback = Some({
814
833
  targetCheckpointId,
815
834
  diffCheckpointId,
816
835
  progressBlockNumberByChainId,
836
+ rolledBackAddresses,
817
837
  })
818
838
  }
819
839
 
@@ -100,7 +100,7 @@ function makeFromDbState(config, persistence, initialState, registrationsByChain
100
100
  initialState.chains.forEach(resumedChainState => {
101
101
  let chainId = Config.getChain(config, resumedChainState.id);
102
102
  let chainConfig = ChainMap.get(config.chainMap, chainId);
103
- chainStates[resumedChainState.id] = ChainState.makeFromDbState(chainConfig, resumedChainState, initialState.reorgCheckpoints, isInReorgThreshold, isRealtime, config, registrationsByChainId, reducedPollingInterval);
103
+ chainStates[resumedChainState.id] = ChainState.makeFromDbState(chainConfig, resumedChainState, initialState.reorgCheckpoints, isInReorgThreshold, isRealtime, config, initialState.contractMapping, registrationsByChainId, reducedPollingInterval);
104
104
  });
105
105
  let state = make(config, persistence, chainStates, isInReorgThreshold, isRealtime, targetBufferSize, initialState.checkpointId, isDevelopmentMode, shouldUseTui, exitAfterFirstEventBlock, onError, onExit);
106
106
  CrossChainState.markCaughtUpOnResume(state.crossChainState);
@@ -625,6 +625,7 @@ function drainBatchRun(state) {
625
625
  let checkpointBlockNumbers = [];
626
626
  let checkpointBlockHashes = [];
627
627
  let checkpointEventsProcessed = [];
628
+ let registeredAddresses = [];
628
629
  all.forEach(batch => {
629
630
  if (Utils.$$Array.isEmpty(rest) && batch.isInReorgThreshold === isInReorgThreshold) {
630
631
  Utils.Dict.forEachWithKey(batch.progressedChainsById, (chainAfterBatch, key) => {
@@ -637,6 +638,7 @@ function drainBatchRun(state) {
637
638
  checkpointBlockNumbers.push(...batch.checkpointBlockNumbers);
638
639
  checkpointBlockHashes.push(...batch.checkpointBlockHashes);
639
640
  checkpointEventsProcessed.push(...batch.checkpointEventsProcessed);
641
+ registeredAddresses.push(...batch.registeredAddresses);
640
642
  } else {
641
643
  rest.push(batch);
642
644
  }
@@ -651,7 +653,8 @@ function drainBatchRun(state) {
651
653
  checkpointChainIds: checkpointChainIds,
652
654
  checkpointBlockNumbers: checkpointBlockNumbers,
653
655
  checkpointBlockHashes: checkpointBlockHashes,
654
- checkpointEventsProcessed: checkpointEventsProcessed
656
+ checkpointEventsProcessed: checkpointEventsProcessed,
657
+ registeredAddresses: registeredAddresses
655
658
  };
656
659
  }
657
660
 
@@ -665,14 +668,17 @@ function markCommitted(state, upToCheckpointId) {
665
668
  state.committedCheckpointId = upToCheckpointId;
666
669
  }
667
670
 
668
- function beginRollbackDiff(state, targetCheckpointId, diffCheckpointId, progressBlockNumberByChainId) {
671
+ function beginRollbackDiff(state, targetCheckpointId, diffCheckpointId, progressBlockNumberByChainId, rolledBackAddresses) {
669
672
  let perChainEntities = EntityTables.perChain(state.allEntities);
670
673
  state.entities = EntityTables.make(EntityTables.crossChain(state.allEntities));
671
674
  Utils.Dict.forEach(CrossChainState.chainStates(state.crossChainState), cs => ChainState.resetEntities(cs, perChainEntities));
672
675
  EffectState.resetForRollback(state.effectState);
676
+ let match = state.rollback;
677
+ let rolledBackAddresses$1 = match !== undefined ? match.rolledBackAddresses.concat(rolledBackAddresses) : rolledBackAddresses;
673
678
  state.rollback = {
674
679
  targetCheckpointId: targetCheckpointId,
675
680
  diffCheckpointId: diffCheckpointId,
681
+ rolledBackAddresses: rolledBackAddresses$1,
676
682
  progressBlockNumberByChainId: progressBlockNumberByChainId
677
683
  };
678
684
  }
@@ -143,6 +143,7 @@ let beginRollbackDiff: (
143
143
  ~targetCheckpointId: Internal.checkpointId,
144
144
  ~diffCheckpointId: Internal.checkpointId,
145
145
  ~progressBlockNumberByChainId: dict<int>,
146
+ ~rolledBackAddresses: array<AddressRows.key>,
146
147
  ) => unit
147
148
  let recordWriteFailure: (t, exn) => unit
148
149
  let beginWriteFiber: (t, promise<unit>) => unit
package/src/Internal.res CHANGED
@@ -149,8 +149,8 @@ type svmTransactionField =
149
149
  | @as("accountKeys") AccountKeys
150
150
  | @as("recentBlockhash") RecentBlockhash
151
151
  | @as("version") Version
152
- | @as("tokenBalances") TokenBalances
153
152
  | @as("allSignatures") AllSignatures
153
+ | @as("accountActivities") AccountActivities
154
154
 
155
155
  let allSvmTransactionFields: array<svmTransactionField> = [
156
156
  TransactionIndex,
@@ -163,13 +163,12 @@ let allSvmTransactionFields: array<svmTransactionField> = [
163
163
  AccountKeys,
164
164
  RecentBlockhash,
165
165
  Version,
166
- TokenBalances,
167
166
  AllSignatures,
167
+ AccountActivities,
168
168
  ]
169
- let svmTransactionFieldSchema = S.enum(allSvmTransactionFields)
170
169
 
171
- // All SVM block fields. `slot`/`time`/`hash` are always included; the rest are
172
- // selectable via `field_selection.block_fields` (see `allSvmBlockFields`).
170
+ // All SVM block fields. `slot` is always included (the item's key); the rest
171
+ // are selectable via handler `fields.block`.
173
172
  type svmBlockField =
174
173
  | @as("slot") Slot
175
174
  | @as("time") Time
@@ -178,9 +177,6 @@ type svmBlockField =
178
177
  | @as("parentSlot") ParentSlot
179
178
  | @as("parentHash") ParentHash
180
179
 
181
- let allSvmBlockFields: array<svmBlockField> = [Height, ParentSlot, ParentHash]
182
- let svmBlockFieldSchema = S.enum(allSvmBlockFields)
183
-
184
180
  // Static sets of field names whose source schemas must be wrapped with S.nullable.
185
181
  let evmNullableBlockFields = Utils.Set.fromArray(
186
182
  (
@@ -427,6 +423,9 @@ type indexingContract = {
427
423
  type fieldSelection = {
428
424
  blockFields: Utils.Set.t<string>,
429
425
  transactionFields: Utils.Set.t<string>,
426
+ instructionFields: Utils.Set.t<string>,
427
+ accountActivityFields: Utils.Set.t<string>,
428
+ logFields: Utils.Set.t<string>,
430
429
  // The sets precompiled to the store selections `ChainState` materialises with.
431
430
  blockMask: float,
432
431
  transactionMask: float,
@@ -438,11 +437,17 @@ type fieldSelection = {
438
437
  let makeFieldSelection = (
439
438
  ~blockFields: Utils.Set.t<string>,
440
439
  ~transactionFields: Utils.Set.t<string>,
440
+ ~instructionFields: Utils.Set.t<string>=Utils.Set.make(),
441
+ ~accountActivityFields: Utils.Set.t<string>=Utils.Set.make(),
442
+ ~logFields: Utils.Set.t<string>=Utils.Set.make(),
441
443
  ~blockMaskFn: Utils.Set.t<string> => float,
442
444
  ~transactionMaskFn: Utils.Set.t<string> => float,
443
445
  ): fieldSelection => {
444
446
  blockFields,
445
447
  transactionFields,
448
+ instructionFields,
449
+ accountActivityFields,
450
+ logFields,
446
451
  blockMask: blockMaskFn(blockFields),
447
452
  transactionMask: transactionMaskFn(transactionFields),
448
453
  }
@@ -459,6 +464,9 @@ let unionFields = (a, b) => a === b ? a : a->Utils.Set.union(b)
459
464
  let unionFieldSelection = (a: fieldSelection, b: fieldSelection): fieldSelection => {
460
465
  blockFields: unionFields(a.blockFields, b.blockFields),
461
466
  transactionFields: unionFields(a.transactionFields, b.transactionFields),
467
+ instructionFields: unionFields(a.instructionFields, b.instructionFields),
468
+ accountActivityFields: unionFields(a.accountActivityFields, b.accountActivityFields),
469
+ logFields: unionFields(a.logFields, b.logFields),
462
470
  blockMask: FieldMask.orMask(a.blockMask, b.blockMask),
463
471
  transactionMask: FieldMask.orMask(a.transactionMask, b.transactionMask),
464
472
  }
@@ -569,7 +577,6 @@ type svmInstructionEventConfig = {
569
577
  `dN` selector at query time and the dispatch-key precomputation in the
570
578
  router. */
571
579
  discriminatorByteLen: int,
572
- includeLogs: bool,
573
580
  /** Disjunctive normal form: outer array is OR of AND-groups, inner array is
574
581
  AND across positions. Empty outer array means "no account filter". */
575
582
  accountFilters: array<svmAccountFilterGroup>,
@@ -747,9 +754,8 @@ let getItemChainId = item =>
747
754
  | Block({onBlockRegistration: {chainId}}) => chainId
748
755
  }
749
756
 
750
- // The `fields` option of an EVM `onEvent`/`contractRegister` registration:
751
- // the block and transaction fields the handler reads. Replaces the config
752
- // `field_selection` for this registration.
757
+ // EVM `fields` bag. Parsed from the JS object as `unknown` at registration;
758
+ // this record exists so ReScript tests can construct a typed EVM selection.
753
759
  type evmFieldsSelection = {
754
760
  block?: array<string>,
755
761
  transaction?: array<string>,
@@ -758,7 +764,7 @@ type evmFieldsSelection = {
758
764
  type eventOptions<'where> = {
759
765
  wildcard?: bool,
760
766
  where?: 'where,
761
- fields?: evmFieldsSelection,
767
+ fields?: unknown,
762
768
  }
763
769
 
764
770
  type fuelSupplyParams = {
@@ -782,12 +788,23 @@ let fuelTransferParamsSchema = S.schema(s => {
782
788
 
783
789
  type entity = private {id: string}
784
790
 
791
+ // A data skipping index emitted into the history table DDL as
792
+ // `INDEX <name> <expr> TYPE <type> GRANULARITY <granularity>`.
793
+ type clickhouseSkippingIndex = {
794
+ name: string,
795
+ expr: string,
796
+ @as("type")
797
+ type_: string,
798
+ granularity?: int,
799
+ }
800
+
785
801
  // Raw ClickHouse expressions/field names from the entity's
786
802
  // @storage(clickhouse: {...}) directive, applied to the history table DDL.
787
803
  type clickhouseTableOptions = {
788
804
  partitionBy?: string,
789
805
  orderBy?: array<string>,
790
806
  ttl?: string,
807
+ skippingIndexes?: array<clickhouseSkippingIndex>,
791
808
  }
792
809
 
793
810
  // Per-entity storage resolved at parse time against the global storage
@@ -808,6 +825,9 @@ type genericEntityConfig<'entity> = {
808
825
  // entity's `@crossChain`. When false the table carries a chain-id column in
809
826
  // its primary key and every row belongs to exactly one chain.
810
827
  crossChain: bool,
828
+ // `@internal` on the entity: stored and usable in handlers as normal, but
829
+ // never exposed through the GraphQL API (no Hasura tracking).
830
+ internal: bool,
811
831
  }
812
832
  type entityConfig = genericEntityConfig<entity>
813
833
  external fromGenericEntityConfig: genericEntityConfig<'entity> => entityConfig = "%identity"
@@ -88,20 +88,10 @@ let allSvmTransactionFields = [
88
88
  "accountKeys",
89
89
  "recentBlockhash",
90
90
  "version",
91
- "tokenBalances",
92
- "allSignatures"
91
+ "allSignatures",
92
+ "accountActivities"
93
93
  ];
94
94
 
95
- let svmTransactionFieldSchema = S$RescriptSchema.$$enum(allSvmTransactionFields);
96
-
97
- let allSvmBlockFields = [
98
- "height",
99
- "parentSlot",
100
- "parentHash"
101
- ];
102
-
103
- let svmBlockFieldSchema = S$RescriptSchema.$$enum(allSvmBlockFields);
104
-
105
95
  let evmNullableBlockFields = new Set([
106
96
  "nonce",
107
97
  "difficulty",
@@ -141,10 +131,16 @@ let evmNullableTransactionFields = new Set([
141
131
  "type"
142
132
  ]);
143
133
 
144
- function makeFieldSelection(blockFields, transactionFields, blockMaskFn, transactionMaskFn) {
134
+ function makeFieldSelection(blockFields, transactionFields, instructionFieldsOpt, accountActivityFieldsOpt, logFieldsOpt, blockMaskFn, transactionMaskFn) {
135
+ let instructionFields = instructionFieldsOpt !== undefined ? Primitive_option.valFromOption(instructionFieldsOpt) : new Set();
136
+ let accountActivityFields = accountActivityFieldsOpt !== undefined ? Primitive_option.valFromOption(accountActivityFieldsOpt) : new Set();
137
+ let logFields = logFieldsOpt !== undefined ? Primitive_option.valFromOption(logFieldsOpt) : new Set();
145
138
  return {
146
139
  blockFields: blockFields,
147
140
  transactionFields: transactionFields,
141
+ instructionFields: instructionFields,
142
+ accountActivityFields: accountActivityFields,
143
+ logFields: logFields,
148
144
  blockMask: blockMaskFn(blockFields),
149
145
  transactionMask: transactionMaskFn(transactionFields)
150
146
  };
@@ -162,6 +158,9 @@ function unionFieldSelection(a, b) {
162
158
  return {
163
159
  blockFields: unionFields(a.blockFields, b.blockFields),
164
160
  transactionFields: unionFields(a.transactionFields, b.transactionFields),
161
+ instructionFields: unionFields(a.instructionFields, b.instructionFields),
162
+ accountActivityFields: unionFields(a.accountActivityFields, b.accountActivityFields),
163
+ logFields: unionFields(a.logFields, b.logFields),
165
164
  blockMask: FieldMask.orMask(a.blockMask, b.blockMask),
166
165
  transactionMask: FieldMask.orMask(a.transactionMask, b.transactionMask)
167
166
  };
@@ -329,9 +328,6 @@ export {
329
328
  allEvmTransactionFields,
330
329
  evmTransactionFieldSchema,
331
330
  allSvmTransactionFields,
332
- svmTransactionFieldSchema,
333
- allSvmBlockFields,
334
- svmBlockFieldSchema,
335
331
  evmNullableBlockFields,
336
332
  evmNullableTransactionFields,
337
333
  makeFieldSelection,
package/src/Main.res CHANGED
@@ -88,17 +88,22 @@ let getGlobalPersistence = () =>
88
88
  let setGlobalPersistence = (persistence: Persistence.t) =>
89
89
  EnvioGlobal.value.persistence = Some(persistence->(Utils.magic: Persistence.t => unknown))
90
90
 
91
- let getInitialChainState = (~chainId: ChainId.t): option<Persistence.initialChainState> => {
91
+ let getInitialState = (): option<Persistence.initialState> => {
92
92
  switch getGlobalPersistence() {
93
93
  | Some(persistence) =>
94
94
  switch persistence.storageStatus {
95
- | Ready(initialState) => initialState.chains->Array.find(c => c.id === chainId)
95
+ | Ready(initialState) => Some(initialState)
96
96
  | _ => None
97
97
  }
98
98
  | None => None
99
99
  }
100
100
  }
101
101
 
102
+ let getInitialChainState = (~chainId: ChainId.t): option<Persistence.initialChainState> =>
103
+ getInitialState()->Option.flatMap(initialState =>
104
+ initialState.chains->Array.find(c => c.id === chainId)
105
+ )
106
+
102
107
  // Importing `generated` must not trigger `Config.load()`,
103
108
  // so the exported indexer calls this lazily on first `indexer.chains` access.
104
109
  let buildChainsObject = (~config: Config.t) => {
@@ -184,20 +189,23 @@ let buildChainsObject = (~config: Config.t) => {
184
189
  chainState->ChainState.contractAddresses(~contractName=contract.name)
185
190
  }
186
191
  // Before the global state is available (eg during handler
187
- // module load after resume), combine static addresses from config
188
- // with dynamic contracts persisted in the database.
192
+ // module load after resume), read them off what persistence
193
+ // restored which already holds the config's own addresses
194
+ // alongside the dynamically registered ones.
189
195
  | None =>
190
- switch getInitialChainState(~chainId=chainConfig.id) {
191
- | Some(chainState) =>
192
- let addresses = contract.addresses->Array.copy
193
- chainState.indexingAddresses->Array.forEach(
194
- dc => {
195
- if dc.contractName === contract.name {
196
- addresses->Array.push(dc.address)->ignore
197
- }
198
- },
199
- )
200
- addresses
196
+ switch getInitialState() {
197
+ | Some(initialState) =>
198
+ switch initialState.chains->Array.find(c => c.id === chainConfig.id) {
199
+ | Some(chainState) =>
200
+ chainState.addressRows->AddressRows.renderOfContract(
201
+ ~ecosystem=(config.ecosystem.name :> string),
202
+ ~shouldChecksum=!config.lowercaseAddresses,
203
+ ~contractId=initialState.contractMapping->ContractMapping.idOfOrThrow(
204
+ contract.name,
205
+ ),
206
+ )
207
+ | None => contract.addresses
208
+ }
201
209
  | None => contract.addresses
202
210
  }
203
211
  }
@@ -239,7 +247,7 @@ let getGlobalIndexer = (): 'indexer => {
239
247
  "event": unknown,
240
248
  "wildcard": option<bool>,
241
249
  "where": option<JSON.t>,
242
- "fields": option<Internal.evmFieldsSelection>,
250
+ "fields": option<unknown>,
243
251
  }
244
252
  )
245
253
  // Detect format: if "contract" is a string, it's the TS format
@@ -295,7 +303,7 @@ let getGlobalIndexer = (): 'indexer => {
295
303
  "program": unknown,
296
304
  "instruction": unknown,
297
305
  "where": option<JSON.t>,
298
- "fields": option<Internal.evmFieldsSelection>,
306
+ "fields": option<unknown>,
299
307
  }
300
308
  )
301
309
  let (programName, instructionName) = if typeof(raw["program"]) === #string {
@@ -308,9 +316,6 @@ let getGlobalIndexer = (): 'indexer => {
308
316
  (inst["contract"], inst["_0"])
309
317
  }
310
318
  let where = raw["where"]
311
- // SVM takes its selection from the config, so `fields` is carried through
312
- // only to be rejected by the registration — dropping it here would leave a
313
- // plain-JS caller with a silently ignored option.
314
319
  let fields = raw["fields"]
315
320
  let eventOptions: option<Internal.eventOptions<_>> = switch (where, fields) {
316
321
  | (None, None) => None
@@ -580,6 +585,7 @@ let migrate = async (~reset) => {
580
585
  await persistence->Persistence.init(
581
586
  ~reset,
582
587
  ~chainConfigs=config.chainMap->ChainMap.values,
588
+ ~contractMapping=config.contractMapping,
583
589
  ~envioInfo=getEnvioInfo(),
584
590
  ~resetCommand="envio local db-migrate setup",
585
591
  ~runCommand=None,
@@ -631,6 +637,7 @@ let start = async (
631
637
  await persistence->Persistence.init(
632
638
  ~reset,
633
639
  ~chainConfigs=config.chainMap->ChainMap.values,
640
+ ~contractMapping=config.contractMapping,
634
641
  ~envioInfo=getEnvioInfo(),
635
642
  ~resetCommand=isDevelopmentMode ? "envio dev -r" : "envio start -r",
636
643
  ~runCommand=Some(isDevelopmentMode ? "envio dev" : "envio start"),