envio 3.8.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 (64) hide show
  1. package/README.md +2 -2
  2. package/index.d.ts +158 -104
  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 +76 -70
  9. package/src/ChainState.res.mjs +76 -65
  10. package/src/ChainState.resi +14 -9
  11. package/src/Config.res +23 -82
  12. package/src/Config.res.mjs +19 -63
  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/EventConfigBuilder.res +0 -2
  18. package/src/EventConfigBuilder.res.mjs +0 -2
  19. package/src/FetchState.res +59 -67
  20. package/src/FetchState.res.mjs +34 -44
  21. package/src/InMemoryStore.res +17 -30
  22. package/src/InMemoryStore.res.mjs +11 -23
  23. package/src/IndexerState.res +21 -1
  24. package/src/IndexerState.res.mjs +9 -3
  25. package/src/IndexerState.resi +1 -0
  26. package/src/Main.res +25 -15
  27. package/src/Main.res.mjs +19 -14
  28. package/src/MemoryStorage.res +39 -66
  29. package/src/MemoryStorage.res.mjs +28 -65
  30. package/src/Metrics.res +15 -1
  31. package/src/Metrics.res.mjs +5 -1
  32. package/src/Persistence.res +30 -16
  33. package/src/Persistence.res.mjs +8 -5
  34. package/src/PgStorage.res +183 -65
  35. package/src/PgStorage.res.mjs +92 -58
  36. package/src/Rollback.res +14 -10
  37. package/src/Rollback.res.mjs +4 -2
  38. package/src/SimulateItems.res +5 -12
  39. package/src/SimulateItems.res.mjs +6 -10
  40. package/src/TestIndexer.res +93 -111
  41. package/src/TestIndexer.res.mjs +61 -88
  42. package/src/Utils.res +7 -0
  43. package/src/Utils.res.mjs +9 -2
  44. package/src/Writing.res +2 -0
  45. package/src/Writing.res.mjs +2 -1
  46. package/src/bindings/ClickHouse.res +6 -0
  47. package/src/bindings/ClickHouse.res.mjs +4 -0
  48. package/src/bindings/NodeJs.res +9 -0
  49. package/src/bindings/NodeJs.res.mjs +8 -1
  50. package/src/bindings/Postgres.res +9 -0
  51. package/src/bindings/Postgres.res.mjs +3 -0
  52. package/src/db/EntityHistory.res +12 -18
  53. package/src/db/EntityHistory.res.mjs +3 -14
  54. package/src/db/InternalTable.res +171 -65
  55. package/src/db/InternalTable.res.mjs +176 -73
  56. package/src/db/Table.res +24 -0
  57. package/src/db/Table.res.mjs +20 -1
  58. package/src/sources/AddressSet.res +0 -22
  59. package/src/sources/AddressStore.res +48 -88
  60. package/src/sources/AddressStore.res.mjs +11 -22
  61. package/src/sources/SvmHyperSyncClient.res +17 -21
  62. package/src/sources/SvmHyperSyncSource.res +4 -9
  63. package/src/sources/SvmHyperSyncSource.res.mjs +5 -13
  64. package/svm.schema.json +10 -4
@@ -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, Primitive_option.some(sources.transactionStore), Primitive_option.some(sources.blockStore))];
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) {
@@ -721,6 +719,10 @@ function toMetrics(cs) {
721
719
  startBlock: cs.fetchState.startBlock,
722
720
  endBlock: cs.fetchState.endBlock,
723
721
  numAddresses: cs.addressStore.size(),
722
+ addressesByContract: cs.addressStore.contractCounts().map(param => [
723
+ param.contractName,
724
+ param.count
725
+ ]),
724
726
  isReady: cs.timestampCaughtUpToHeadOrEndblock !== undefined,
725
727
  sourceBlockNumber: cs.fetchState.knownHeight,
726
728
  progressBlockNumber: cs.committedProgressBlockNumber,
@@ -856,35 +858,44 @@ function markReady(cs, readyAt) {
856
858
  }
857
859
 
858
860
  function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTargetBlockNumber, isReorgChain) {
859
- if (newProgressBlockNumber === undefined) {
860
- if (isReorgChain) {
861
- cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, rollbackTargetBlockNumber);
862
- cs.transactionStore.rollback(rollbackTargetBlockNumber);
863
- cs.blockStore.rollback(rollbackTargetBlockNumber);
864
- cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
865
- cs.processingBlockNumber = Primitive_int.min(cs.processingBlockNumber, rollbackTargetBlockNumber);
866
- return;
867
- } else {
868
- return;
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 (newProgressBlockNumber !== undefined) {
871
+ let newTotalEventsProcessed = cs.numEventsProcessed - Stdlib_Option.getOrThrow(eventsProcessedDiff, "Missing events-processed diff for rolled-back chain");
872
+ let safeCheckpointTracking = cs.safeCheckpointTracking;
873
+ if (safeCheckpointTracking !== undefined) {
874
+ cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
869
875
  }
876
+ let rolledBackAddresses = rollbackTo(newProgressBlockNumber);
877
+ cs.transactionStore.rollback(newProgressBlockNumber);
878
+ cs.blockStore.rollback(newProgressBlockNumber);
879
+ cs.committedProgressBlockNumber = newProgressBlockNumber;
880
+ cs.processingBlockNumber = newProgressBlockNumber;
881
+ cs.numEventsProcessed = newTotalEventsProcessed;
882
+ return rolledBackAddresses;
870
883
  }
871
- let newTotalEventsProcessed = cs.numEventsProcessed - Stdlib_Option.getOrThrow(eventsProcessedDiff, "Missing events-processed diff for rolled-back chain");
872
- let safeCheckpointTracking = cs.safeCheckpointTracking;
873
- if (safeCheckpointTracking !== undefined) {
874
- cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
884
+ if (!isReorgChain) {
885
+ return [];
875
886
  }
876
- cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, newProgressBlockNumber);
877
- cs.transactionStore.rollback(newProgressBlockNumber);
878
- cs.blockStore.rollback(newProgressBlockNumber);
879
- cs.committedProgressBlockNumber = newProgressBlockNumber;
880
- cs.processingBlockNumber = newProgressBlockNumber;
881
- cs.numEventsProcessed = newTotalEventsProcessed;
887
+ let rolledBackAddresses$1 = rollbackTo(rollbackTargetBlockNumber);
888
+ cs.transactionStore.rollback(rollbackTargetBlockNumber);
889
+ cs.blockStore.rollback(rollbackTargetBlockNumber);
890
+ cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
891
+ cs.processingBlockNumber = Primitive_int.min(cs.processingBlockNumber, rollbackTargetBlockNumber);
892
+ return rolledBackAddresses$1;
882
893
  }
883
894
 
884
895
  export {
885
- configAddresses,
896
+ configStorageRows,
897
+ seedConfigAddresses,
886
898
  make,
887
- makeFromConfig,
888
899
  makeFromDbState,
889
900
  logger,
890
901
  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
@@ -158,4 +163,4 @@ let rollback: (
158
163
  ~eventsProcessedDiff: option<float>,
159
164
  ~rollbackTargetBlockNumber: int,
160
165
  ~isReorgChain: bool,
161
- ) => unit
166
+ ) => array<AddressRows.key>
package/src/Config.res CHANGED
@@ -107,6 +107,9 @@ type t = {
107
107
  // they can express fits an INTEGER.
108
108
  chainIdMode: ChainId.mode,
109
109
  chainMap: ChainMap.t<chain>,
110
+ // Derived from every chain's contracts, so an id means the same contract on
111
+ // every chain and on every restart.
112
+ contractMapping: ContractMapping.t,
110
113
  defaultChain: option<chain>,
111
114
  ecosystem: Ecosystem.t,
112
115
  enableRawEvents: bool,
@@ -123,76 +126,9 @@ type t = {
123
126
  isDev: bool,
124
127
  userEntitiesByName: dict<Internal.entityConfig>,
125
128
  userEntities: array<Internal.entityConfig>,
126
- allEntities: array<Internal.entityConfig>,
127
129
  allEnums: array<Table.enumConfig<Table.enum>>,
128
130
  }
129
131
 
130
- module EnvioAddresses = {
131
- let name = "envio_addresses"
132
- let index = -1
133
-
134
- let makeId = (~chainId: ChainId.t, ~address) => {
135
- chainId->ChainId.toString ++ "-" ++ address->Address.toString
136
- }
137
-
138
- type t = {
139
- id: string,
140
- @as("chain_id") chainId: ChainId.t,
141
- @as("registration_block") registrationBlock: int,
142
- // Vestigial: always written as -1 and never read back. The column stays so
143
- // an existing schema doesn't need a migration.
144
- @as("registration_log_index") registrationLogIndex: int,
145
- @as("contract_name") contractName: string,
146
- }
147
-
148
- // Extract the raw contract address from the composite id ({chainId}-{address}).
149
- // Inverse of makeId. Keep in sync with makeId above and the SUBSTRING SQL in
150
- // InternalTable.Chains.makeGetInitialStateQuery.
151
- let getAddress = (entity: t): Address.t => {
152
- let sepIdx = entity.id->String.indexOf("-")
153
- entity.id
154
- ->String.slice(~start=sepIdx + 1, ~end=entity.id->String.length)
155
- ->Address.unsafeFromString
156
- }
157
-
158
- let schema = S.schema(s => {
159
- id: s.matches(S.string),
160
- chainId: s.matches(ChainId.schema),
161
- registrationBlock: s.matches(S.int),
162
- registrationLogIndex: s.matches(S.int),
163
- contractName: s.matches(S.string),
164
- })
165
-
166
- let table = Table.mkTable(
167
- name,
168
- ~fields=[
169
- Table.mkField("id", String, ~isPrimaryKey=true, ~fieldSchema=S.string),
170
- Table.mkField("chain_id", ChainId, ~fieldSchema=ChainId.schema),
171
- Table.mkField("registration_block", Int32, ~fieldSchema=S.int),
172
- // -1 sentinel when registered from a block handler (no log index)
173
- Table.mkField("registration_log_index", Int32, ~fieldSchema=S.int),
174
- Table.mkField("contract_name", String, ~fieldSchema=S.string),
175
- ],
176
- )
177
-
178
- external castToInternal: t => Internal.entity = "%identity"
179
-
180
- let entityConfig = {
181
- Internal.name,
182
- index,
183
- schema,
184
- table,
185
- // Internal address tracking is Postgres-only; the global config is
186
- // always required to have Postgres enabled (Storage::resolve forbids
187
- // a Postgres-disabled global), so this is safe regardless of mode.
188
- storage: {postgres: true, clickhouse: false},
189
- // The table already keys rows by chain through the composite `id`, so the
190
- // per-chain mode must not append a second chain-id column to it.
191
- crossChain: true,
192
- internal: true,
193
- }->Internal.fromGenericEntityConfig
194
- }
195
-
196
132
  type rpcSourceFor = | @as("sync") Sync | @as("fallback") Fallback | @as("realtime") Realtime
197
133
 
198
134
  let rpcSourceForSchema = S.enum([Sync, Fallback, Realtime])
@@ -630,6 +566,14 @@ let publicConfigSchema = S.schema(s =>
630
566
  }
631
567
  )
632
568
 
569
+ let contractMappingOf = (~chainConfigs: array<chain>): ContractMapping.t => {
570
+ let names = []
571
+ chainConfigs->Array.forEach(chain =>
572
+ chain.contracts->Array.forEach(contract => names->Array.push(contract.name)->ignore)
573
+ )
574
+ ContractMapping.make(~names)
575
+ }
576
+
633
577
  let fromPublic = (publicConfigJson: JSON.t) => {
634
578
  let maxAddrInPartition = Env.maxAddrInPartition
635
579
  // Parse public config
@@ -931,25 +875,24 @@ let fromPublic = (publicConfigJson: JSON.t) => {
931
875
  }
932
876
  })
933
877
 
934
- // The same address under two contract definitions (or twice under one)
935
- // would later violate the (chainId, address) primary key of
936
- // envio_addresses with an opaque Postgres error — fail fast with the
937
- // offending pair instead. parseAddress already canonicalizes casing
938
- // (checksum or lowercase), so an exact match catches case variants too.
939
- let contractNameByAddress = Dict.make()
878
+ // One address listed twice for the same contract would violate the
879
+ // (chainId, address, contract) primary key of envio_addresses with an
880
+ // opaque Postgres error — fail fast instead. Two contracts may share an
881
+ // address: each indexes it with its own events. parseAddress already
882
+ // canonicalizes casing (checksum or lowercase), so an exact match
883
+ // catches case variants too.
884
+ let seen = Utils.Set.make()
940
885
  contracts->Array.forEach(contract => {
941
886
  contract.addresses->Array.forEach(
942
887
  address => {
943
888
  let addressString = address->Address.toString
944
- switch contractNameByAddress->Dict.get(addressString) {
945
- | Some(existingContractName) =>
889
+ let key = contract.name ++ "-" ++ addressString
890
+ if seen->Utils.Set.has(key) {
946
891
  JsError.throwWithMessage(
947
- existingContractName === contract.name
948
- ? `Address ${addressString} is listed multiple times for the contract ${contract.name} on chain ${chainId->ChainId.toString}. Please remove the duplicate from your config.`
949
- : `Address ${addressString} on chain ${chainId->ChainId.toString} is configured for multiple contracts: ${existingContractName} and ${contract.name}. Indexing the same address with multiple contract definitions is not supported. Please define the events on a single contract definition instead.`,
892
+ `Address ${addressString} is listed multiple times for the contract ${contract.name} on chain ${chainId->ChainId.toString}. Please remove the duplicate from your config.`,
950
893
  )
951
- | None => contractNameByAddress->Dict.set(addressString, contract.name)
952
894
  }
895
+ seen->Utils.Set.add(key)->ignore
953
896
  },
954
897
  )
955
898
  })
@@ -1075,8 +1018,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1075
1018
  ->Option.getOr([])
1076
1019
  ->parseEntitiesFromJson(~enumConfigsByName, ~globalStorage, ~defaultCrossChain)
1077
1020
 
1078
- let allEntities = userEntities->Array.concat([EnvioAddresses.entityConfig])
1079
-
1080
1021
  // Keyed by the capitalized entity name to match the handler-context
1081
1022
  // accessor (`context.Pool_snapshots`) the generated types expose, while
1082
1023
  // entityConfig.name stays the original schema name used for the physical
@@ -1113,6 +1054,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1113
1054
  storage: globalStorage,
1114
1055
  chainIdMode: publicConfig["chainIdMode"]->Option.getOr(Int32),
1115
1056
  chainMap,
1057
+ contractMapping: contractMappingOf(~chainConfigs=chains),
1116
1058
  defaultChain: chains->Array.get(0),
1117
1059
  enableRawEvents: publicConfig["rawEvents"]->Option.getOr(false),
1118
1060
  ecosystem,
@@ -1124,7 +1066,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1124
1066
  isDev: publicConfig["isDev"]->Option.getOr(false),
1125
1067
  userEntitiesByName,
1126
1068
  userEntities,
1127
- allEntities,
1128
1069
  allEnums,
1129
1070
  }
1130
1071
  }
@@ -16,6 +16,7 @@ import * as BigDecimal from "./bindings/BigDecimal.res.mjs";
16
16
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
17
17
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
18
18
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
19
+ import * as ContractMapping from "./ContractMapping.res.mjs";
19
20
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
20
21
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
21
22
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
@@ -32,58 +33,6 @@ function chainIdColumnName(format) {
32
33
  }
33
34
  }
34
35
 
35
- let name = "envio_addresses";
36
-
37
- function makeId(chainId, address) {
38
- return ChainId.toString(chainId) + "-" + address;
39
- }
40
-
41
- function getAddress(entity) {
42
- let sepIdx = entity.id.indexOf("-");
43
- return entity.id.slice(sepIdx + 1 | 0, entity.id.length);
44
- }
45
-
46
- let schema = S$RescriptSchema.schema(s => ({
47
- id: s.m(S$RescriptSchema.string),
48
- chain_id: s.m(ChainId.schema),
49
- registration_block: s.m(S$RescriptSchema.int),
50
- registration_log_index: s.m(S$RescriptSchema.int),
51
- contract_name: s.m(S$RescriptSchema.string)
52
- }));
53
-
54
- let table = Table.mkTable(name, undefined, [
55
- Table.mkField("id", "String", S$RescriptSchema.string, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
56
- Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
57
- Table.mkField("registration_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
58
- Table.mkField("registration_log_index", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
59
- Table.mkField("contract_name", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
60
- ], undefined);
61
-
62
- let entityConfig_storage = {
63
- postgres: true,
64
- clickhouse: false
65
- };
66
-
67
- let entityConfig = {
68
- name: name,
69
- index: -1,
70
- schema: schema,
71
- table: table,
72
- storage: entityConfig_storage,
73
- crossChain: true,
74
- internal: true
75
- };
76
-
77
- let EnvioAddresses = {
78
- name: name,
79
- index: -1,
80
- makeId: makeId,
81
- getAddress: getAddress,
82
- schema: schema,
83
- table: table,
84
- entityConfig: entityConfig
85
- };
86
-
87
36
  let rpcSourceForSchema = S$RescriptSchema.$$enum([
88
37
  "sync",
89
38
  "fallback",
@@ -444,6 +393,16 @@ let publicConfigSchema = S$RescriptSchema.schema(s => ({
444
393
  entities: s.m(S$RescriptSchema.option(S$RescriptSchema.array(entityJsonSchema)))
445
394
  }));
446
395
 
396
+ function contractMappingOf(chainConfigs) {
397
+ let names = [];
398
+ chainConfigs.forEach(chain => {
399
+ chain.contracts.forEach(contract => {
400
+ names.push(contract.name);
401
+ });
402
+ });
403
+ return ContractMapping.make(names);
404
+ }
405
+
447
406
  function fromPublic(publicConfigJson) {
448
407
  let publicConfig;
449
408
  try {
@@ -622,16 +581,14 @@ function fromPublic(publicConfigJson) {
622
581
  startBlock: startBlock
623
582
  };
624
583
  });
625
- let contractNameByAddress = {};
584
+ let seen = new Set();
626
585
  contracts.forEach(contract => {
627
586
  contract.addresses.forEach(address => {
628
- let existingContractName = contractNameByAddress[address];
629
- if (existingContractName !== undefined) {
630
- return Stdlib_JsError.throwWithMessage(existingContractName === contract.name ? `Address ` + address + ` is listed multiple times for the contract ` + contract.name + ` on chain ` + ChainId.toString(chainId) + `. Please remove the duplicate from your config.` : `Address ` + address + ` on chain ` + ChainId.toString(chainId) + ` is configured for multiple contracts: ` + existingContractName + ` and ` + contract.name + `. Indexing the same address with multiple contract definitions is not supported. Please define the events on a single contract definition instead.`);
631
- } else {
632
- contractNameByAddress[address] = contract.name;
633
- return;
587
+ let key = contract.name + "-" + address;
588
+ if (seen.has(key)) {
589
+ Stdlib_JsError.throwWithMessage(`Address ` + address + ` is listed multiple times for the contract ` + contract.name + ` on chain ` + ChainId.toString(chainId) + `. Please remove the duplicate from your config.`);
634
590
  }
591
+ seen.add(key);
635
592
  });
636
593
  });
637
594
  let sourceConfig;
@@ -734,7 +691,6 @@ function fromPublic(publicConfigJson) {
734
691
  };
735
692
  let defaultCrossChain = Stdlib_Option.getOr(publicConfig.defaultCrossChain, true);
736
693
  let userEntities = parseEntitiesFromJson(Stdlib_Option.getOr(publicConfig.entities, []), enumConfigsByName, globalStorage, defaultCrossChain);
737
- let allEntities = userEntities.concat([entityConfig]);
738
694
  let userEntitiesByName = Object.fromEntries(userEntities.map(entityConfig => [
739
695
  Utils.$$String.capitalize(entityConfig.name),
740
696
  entityConfig
@@ -754,6 +710,7 @@ function fromPublic(publicConfigJson) {
754
710
  storage: globalStorage,
755
711
  chainIdMode: Stdlib_Option.getOr(publicConfig.chainIdMode, "int32"),
756
712
  chainMap: chainMap,
713
+ contractMapping: contractMappingOf(chains),
757
714
  defaultChain: chains[0],
758
715
  ecosystem: ecosystem,
759
716
  enableRawEvents: Stdlib_Option.getOr(publicConfig.rawEvents, false),
@@ -765,7 +722,6 @@ function fromPublic(publicConfigJson) {
765
722
  isDev: Stdlib_Option.getOr(publicConfig.isDev, false),
766
723
  userEntitiesByName: userEntitiesByName,
767
724
  userEntities: userEntities,
768
- allEntities: allEntities,
769
725
  allEnums: allEnums
770
726
  };
771
727
  }
@@ -1092,7 +1048,6 @@ function getPgUserEntities(config) {
1092
1048
  export {
1093
1049
  chainIdFieldName,
1094
1050
  chainIdColumnName,
1095
- EnvioAddresses,
1096
1051
  rpcSourceForSchema,
1097
1052
  rpcConfigSchema,
1098
1053
  chainContractSchema,
@@ -1118,6 +1073,7 @@ export {
1118
1073
  columnNameFormatSchema,
1119
1074
  publicConfigStorageSchema,
1120
1075
  publicConfigSchema,
1076
+ contractMappingOf,
1121
1077
  fromPublic,
1122
1078
  normalizeUserAddress,
1123
1079
  normalizeSimulateAddress,
@@ -1134,4 +1090,4 @@ export {
1134
1090
  load,
1135
1091
  getPgUserEntities,
1136
1092
  }
1137
- /* schema Not a pure module */
1093
+ /* rpcSourceForSchema Not a pure module */