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.
- package/README.md +2 -2
- package/index.d.ts +158 -104
- package/package.json +6 -6
- package/src/AddressRows.res +121 -0
- package/src/AddressRows.res.mjs +143 -0
- package/src/Batch.res +2 -0
- package/src/Batch.res.mjs +2 -1
- package/src/ChainState.res +76 -70
- package/src/ChainState.res.mjs +76 -65
- package/src/ChainState.resi +14 -9
- package/src/Config.res +23 -82
- package/src/Config.res.mjs +19 -63
- package/src/ContractMapping.res +66 -0
- package/src/ContractMapping.res.mjs +73 -0
- package/src/Core.res +20 -0
- package/src/Core.res.mjs +4 -0
- package/src/EventConfigBuilder.res +0 -2
- package/src/EventConfigBuilder.res.mjs +0 -2
- package/src/FetchState.res +59 -67
- package/src/FetchState.res.mjs +34 -44
- package/src/InMemoryStore.res +17 -30
- package/src/InMemoryStore.res.mjs +11 -23
- package/src/IndexerState.res +21 -1
- package/src/IndexerState.res.mjs +9 -3
- package/src/IndexerState.resi +1 -0
- package/src/Main.res +25 -15
- package/src/Main.res.mjs +19 -14
- package/src/MemoryStorage.res +39 -66
- package/src/MemoryStorage.res.mjs +28 -65
- package/src/Metrics.res +15 -1
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +30 -16
- package/src/Persistence.res.mjs +8 -5
- package/src/PgStorage.res +183 -65
- package/src/PgStorage.res.mjs +92 -58
- package/src/Rollback.res +14 -10
- package/src/Rollback.res.mjs +4 -2
- package/src/SimulateItems.res +5 -12
- package/src/SimulateItems.res.mjs +6 -10
- package/src/TestIndexer.res +93 -111
- package/src/TestIndexer.res.mjs +61 -88
- package/src/Utils.res +7 -0
- package/src/Utils.res.mjs +9 -2
- package/src/Writing.res +2 -0
- package/src/Writing.res.mjs +2 -1
- package/src/bindings/ClickHouse.res +6 -0
- package/src/bindings/ClickHouse.res.mjs +4 -0
- package/src/bindings/NodeJs.res +9 -0
- package/src/bindings/NodeJs.res.mjs +8 -1
- package/src/bindings/Postgres.res +9 -0
- package/src/bindings/Postgres.res.mjs +3 -0
- package/src/db/EntityHistory.res +12 -18
- package/src/db/EntityHistory.res.mjs +3 -14
- package/src/db/InternalTable.res +171 -65
- package/src/db/InternalTable.res.mjs +176 -73
- package/src/db/Table.res +24 -0
- package/src/db/Table.res.mjs +20 -1
- package/src/sources/AddressSet.res +0 -22
- package/src/sources/AddressStore.res +48 -88
- package/src/sources/AddressStore.res.mjs +11 -22
- package/src/sources/SvmHyperSyncClient.res +17 -21
- package/src/sources/SvmHyperSyncSource.res +4 -9
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -13
- package/svm.schema.json +10 -4
package/src/ChainState.res.mjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
98
|
-
let
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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,
|
|
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,
|
|
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.
|
|
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
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
cs.
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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
|
-
|
|
872
|
-
|
|
873
|
-
if (safeCheckpointTracking !== undefined) {
|
|
874
|
-
cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
|
|
884
|
+
if (!isReorgChain) {
|
|
885
|
+
return [];
|
|
875
886
|
}
|
|
876
|
-
|
|
877
|
-
cs.transactionStore.rollback(
|
|
878
|
-
cs.blockStore.rollback(
|
|
879
|
-
cs.committedProgressBlockNumber =
|
|
880
|
-
cs.processingBlockNumber =
|
|
881
|
-
|
|
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
|
-
|
|
896
|
+
configStorageRows,
|
|
897
|
+
seedConfigAddresses,
|
|
886
898
|
make,
|
|
887
|
-
makeFromConfig,
|
|
888
899
|
makeFromDbState,
|
|
889
900
|
logger,
|
|
890
901
|
blockStore,
|
package/src/ChainState.resi
CHANGED
|
@@ -3,7 +3,18 @@
|
|
|
3
3
|
|
|
4
4
|
type t
|
|
5
5
|
|
|
6
|
-
let
|
|
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
|
-
) =>
|
|
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
|
-
//
|
|
935
|
-
//
|
|
936
|
-
//
|
|
937
|
-
//
|
|
938
|
-
// (checksum or lowercase), so an exact match
|
|
939
|
-
|
|
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
|
-
|
|
945
|
-
|
|
889
|
+
let key = contract.name ++ "-" ++ addressString
|
|
890
|
+
if seen->Utils.Set.has(key) {
|
|
946
891
|
JsError.throwWithMessage(
|
|
947
|
-
|
|
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
|
}
|
package/src/Config.res.mjs
CHANGED
|
@@ -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
|
|
584
|
+
let seen = new Set();
|
|
626
585
|
contracts.forEach(contract => {
|
|
627
586
|
contract.addresses.forEach(address => {
|
|
628
|
-
let
|
|
629
|
-
if (
|
|
630
|
-
|
|
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
|
-
/*
|
|
1093
|
+
/* rpcSourceForSchema Not a pure module */
|