envio 3.8.0 → 3.9.0-resolvers.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/evm.schema.json +7 -0
- package/fuel.schema.json +7 -0
- package/index.d.ts +433 -109
- package/index.js +10 -0
- package/package.json +6 -7
- package/src/AddressRows.res +121 -0
- package/src/AddressRows.res.mjs +143 -0
- package/src/Batch.res +11 -0
- package/src/Batch.res.mjs +2 -1
- package/src/Bin.res +47 -0
- package/src/Bin.res.mjs +34 -1
- package/src/ChainState.res +102 -100
- package/src/ChainState.res.mjs +81 -67
- package/src/ChainState.resi +28 -15
- package/src/Config.res +43 -122
- package/src/Config.res.mjs +33 -75
- package/src/ContractMapping.res +66 -0
- package/src/ContractMapping.res.mjs +73 -0
- package/src/Core.res +24 -0
- package/src/Core.res.mjs +4 -0
- package/src/Ecosystem.res +7 -2
- package/src/Env.res +128 -1
- package/src/Env.res.mjs +123 -1
- package/src/EventConfigBuilder.res +169 -28
- package/src/EventConfigBuilder.res.mjs +124 -17
- package/src/FetchState.res +59 -67
- package/src/FetchState.res.mjs +34 -44
- package/src/HandlerRegister.res +6 -4
- package/src/HandlerRegister.res.mjs +8 -5
- package/src/InMemoryStore.res +22 -32
- package/src/InMemoryStore.res.mjs +12 -24
- package/src/IndexerState.res +40 -3
- package/src/IndexerState.res.mjs +30 -4
- package/src/IndexerState.resi +4 -1
- package/src/Internal.res +16 -17
- package/src/Main.res +25 -15
- package/src/Main.res.mjs +19 -14
- package/src/Metrics.res +15 -1
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +42 -19
- package/src/Persistence.res.mjs +8 -5
- package/src/PgStorage.res +232 -78
- package/src/PgStorage.res.mjs +114 -72
- package/src/PruneStaleHistory.res +1 -1
- package/src/PruneStaleHistory.res.mjs +1 -1
- package/src/Rollback.res +62 -41
- package/src/Rollback.res.mjs +61 -19
- package/src/RollbackCommit.res +7 -9
- package/src/RollbackCommit.res.mjs +5 -6
- package/src/SimulateItems.res +5 -12
- package/src/SimulateItems.res.mjs +6 -10
- package/src/Sink.res +35 -26
- package/src/Sink.res.mjs +20 -16
- package/src/TestIndexer.res +95 -113
- package/src/TestIndexer.res.mjs +63 -90
- package/src/Utils.res +38 -0
- package/src/Utils.res.mjs +39 -2
- package/src/Writing.res +4 -2
- package/src/Writing.res.mjs +3 -2
- package/src/bindings/ClickHouse.res +311 -683
- package/src/bindings/ClickHouse.res.mjs +294 -416
- package/src/bindings/ClickHouseSink.res +303 -0
- package/src/bindings/ClickHouseSink.res.mjs +216 -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 +29 -24
- package/src/db/EntityHistory.res.mjs +11 -19
- package/src/db/InternalTable.res +247 -83
- package/src/db/InternalTable.res.mjs +216 -85
- package/src/db/RollbackScope.res +29 -0
- package/src/db/RollbackScope.res.mjs +31 -0
- package/src/db/Table.res +28 -1
- package/src/db/Table.res.mjs +26 -2
- package/src/resolvers/ResolverProcess.res +809 -0
- package/src/resolvers/ResolverProcess.res.mjs +667 -0
- package/src/resolvers/ResolverQuery.res +317 -0
- package/src/resolvers/ResolverQuery.res.mjs +220 -0
- package/src/resolvers/collisions.js +108 -0
- package/src/resolvers/db.js +363 -0
- package/src/resolvers/dispatch.js +165 -0
- package/src/resolvers/errors.js +30 -0
- package/src/resolvers/graphqlSelection.js +292 -0
- package/src/resolvers/hasuraAction.js +84 -0
- package/src/resolvers/hasuraApply.js +323 -0
- package/src/resolvers/hasuraMetadata.js +119 -0
- package/src/resolvers/index.js +95 -0
- package/src/resolvers/manifest.js +370 -0
- package/src/resolvers/server.js +270 -0
- 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/Evm.res +2 -0
- package/src/sources/Evm.res.mjs +2 -0
- package/src/sources/Fuel.res +2 -0
- package/src/sources/Fuel.res.mjs +2 -0
- package/src/sources/Svm.res +2 -0
- package/src/sources/Svm.res.mjs +2 -0
- package/src/sources/SvmHyperSyncClient.res +13 -107
- package/src/sources/SvmHyperSyncClient.res.mjs +2 -5
- package/src/sources/SvmHyperSyncSource.res +5 -10
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -13
- package/src/tui/Tui.res +1 -1
- package/src/tui/Tui.res.mjs +1 -1
- package/svm.schema.json +17 -78
- package/src/MemoryStorage.res +0 -756
- package/src/MemoryStorage.res.mjs +0 -623
|
@@ -102,7 +102,7 @@ function buildOnEventRegistrationWith(config, chainId, eventConfig, isWildcard,
|
|
|
102
102
|
case "fuel" :
|
|
103
103
|
return EventConfigBuilder.buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
|
|
104
104
|
case "svm" :
|
|
105
|
-
return EventConfigBuilder.buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, fieldSelection, startBlock);
|
|
105
|
+
return EventConfigBuilder.buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, where, fieldSelection, startBlock);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -209,7 +209,7 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
|
|
|
209
209
|
fieldSelection = EventConfigBuilder.resolveInlineFieldSelection(fields$1, contractName, eventName, registration.config.enableRawEvents);
|
|
210
210
|
break;
|
|
211
211
|
case "fuel" :
|
|
212
|
-
fieldSelection = Stdlib_JsError.throwWithMessage(`The fields option of the "` + eventName + `" event registration on contract "` + contractName + `" is
|
|
212
|
+
fieldSelection = Stdlib_JsError.throwWithMessage(`The fields option of the "` + eventName + `" event registration on contract "` + contractName + `" is not supported on Fuel. Select the fields in your config instead.`);
|
|
213
213
|
break;
|
|
214
214
|
case "svm" :
|
|
215
215
|
fieldSelection = EventConfigBuilder.resolveSvmInlineFieldSelection(fields$1, contractName, eventName);
|
|
@@ -243,13 +243,16 @@ function addOnEventRegistration(registration, contractName, eventName, handler,
|
|
|
243
243
|
if (Utils.$$Array.isEmpty(names)) {
|
|
244
244
|
return empty;
|
|
245
245
|
} else {
|
|
246
|
-
return
|
|
246
|
+
return Utils.$$Array.quotedJoin(names);
|
|
247
247
|
}
|
|
248
248
|
};
|
|
249
|
+
let match$2 = registration.config.ecosystem;
|
|
250
|
+
let eventNoun = match$2.eventNoun;
|
|
251
|
+
let contractNoun = match$2.contractNoun;
|
|
249
252
|
if (Utils.$$Array.isEmpty(eventNames)) {
|
|
250
|
-
return Stdlib_JsError.throwWithMessage(`
|
|
253
|
+
return Stdlib_JsError.throwWithMessage(Utils.$$String.capitalize(contractNoun) + ` "` + contractName + `" is not configured on any chain, so its handler for "` + eventName + `" would never run. Add it to your config, or remove the registration. Configured ` + contractNoun + `s: ` + listOr(match$1[0], "none") + `.`);
|
|
251
254
|
} else {
|
|
252
|
-
return Stdlib_JsError.throwWithMessage(`
|
|
255
|
+
return Stdlib_JsError.throwWithMessage(Utils.$$String.capitalize(eventNoun) + ` "` + eventName + `" is not configured on ` + contractNoun + ` "` + contractName + `", so its handler would never run. Add it to your config, or remove the registration. Configured ` + eventNoun + `s on "` + contractName + `": ` + listOr(eventNames, "none") + `.`);
|
|
253
256
|
}
|
|
254
257
|
}
|
|
255
258
|
|
package/src/InMemoryStore.res
CHANGED
|
@@ -127,14 +127,18 @@ let dropCommittedEffects = (
|
|
|
127
127
|
|
|
128
128
|
let prepareRollbackDiff = async (
|
|
129
129
|
state: IndexerState.t,
|
|
130
|
+
~rollbackScope: RollbackScope.t,
|
|
130
131
|
~rollbackTargetCheckpointId,
|
|
131
132
|
~rollbackDiffCheckpointId,
|
|
132
|
-
~
|
|
133
|
+
~progressedChains,
|
|
134
|
+
~rolledBackAddresses,
|
|
133
135
|
) => {
|
|
134
136
|
state->IndexerState.beginRollbackDiff(
|
|
135
137
|
~targetCheckpointId=rollbackTargetCheckpointId,
|
|
136
138
|
~diffCheckpointId=rollbackDiffCheckpointId,
|
|
137
|
-
~
|
|
139
|
+
~scope=rollbackScope,
|
|
140
|
+
~progressedChains,
|
|
141
|
+
~rolledBackAddresses,
|
|
138
142
|
)
|
|
139
143
|
let persistence = state->IndexerState.persistence
|
|
140
144
|
let committedCheckpointId = state->IndexerState.committedCheckpointId
|
|
@@ -150,6 +154,7 @@ let prepareRollbackDiff = async (
|
|
|
150
154
|
->Array.map(async entityConfig => {
|
|
151
155
|
let (removals, restoredEntities) = await persistence.storage.getRollbackData(
|
|
152
156
|
~entityConfig,
|
|
157
|
+
~scope=rollbackScope,
|
|
153
158
|
~rollbackTargetCheckpointId,
|
|
154
159
|
)
|
|
155
160
|
|
|
@@ -194,12 +199,6 @@ let prepareRollbackDiff = async (
|
|
|
194
199
|
// registered them: the store is where they already live, and it knows which
|
|
195
200
|
// ones the database hasn't seen yet.
|
|
196
201
|
let setBatchDcs = (state: IndexerState.t, ~batch: Batch.t) => {
|
|
197
|
-
let inMemTable = state->getInMemTable(
|
|
198
|
-
~entityConfig=InternalTable.EnvioAddresses.entityConfig,
|
|
199
|
-
~scope=CrossChain,
|
|
200
|
-
)
|
|
201
|
-
let committedCheckpointId = state->IndexerState.committedCheckpointId
|
|
202
|
-
|
|
203
202
|
batch.progressedChainsById->Utils.Dict.forEach(progressedChain => {
|
|
204
203
|
let chainId = progressedChain.fetchState.chainId
|
|
205
204
|
let chainState = state->IndexerState.getChainState(~chainId)
|
|
@@ -219,31 +218,22 @@ let setBatchDcs = (state: IndexerState.t, ~batch: Batch.t) => {
|
|
|
219
218
|
}
|
|
220
219
|
}
|
|
221
220
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
->Array.forEach(dc => {
|
|
228
|
-
let entity: InternalTable.EnvioAddresses.t = {
|
|
229
|
-
id: InternalTable.EnvioAddresses.makeId(~chainId, ~address=dc.address),
|
|
230
|
-
chainId,
|
|
231
|
-
contractName: dc.contractName,
|
|
232
|
-
registrationBlock: dc.registrationBlock,
|
|
233
|
-
// Only ever written, never read back. Kept on the table so the column
|
|
234
|
-
// doesn't need a migration.
|
|
235
|
-
registrationLogIndex: -1,
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
inMemTable->InMemoryTable.Entity.set(
|
|
239
|
-
~committedCheckpointId,
|
|
240
|
-
Set({
|
|
241
|
-
entityId: entity.id->EntityId.unsafeOfString,
|
|
242
|
-
checkpointId: checkpointIds->Array.getUnsafe(dc.checkpointIdx),
|
|
243
|
-
entity: entity->InternalTable.EnvioAddresses.castToInternal,
|
|
244
|
-
}),
|
|
221
|
+
batch.registeredAddresses->Array.pushMany(
|
|
222
|
+
chainState
|
|
223
|
+
->ChainState.drainAddressesForWrite(
|
|
224
|
+
~toBlockInclusive=progressedChain.progressBlockNumber,
|
|
225
|
+
~checkpointBlockNumbers,
|
|
245
226
|
)
|
|
246
|
-
|
|
227
|
+
->Array.map((dc): AddressRows.staged => {
|
|
228
|
+
row: {
|
|
229
|
+
chainId,
|
|
230
|
+
address: dc.address,
|
|
231
|
+
contractId: dc.contractId,
|
|
232
|
+
registrationBlock: dc.registrationBlock,
|
|
233
|
+
},
|
|
234
|
+
checkpointId: checkpointIds->Array.getUnsafe(dc.checkpointIdx),
|
|
235
|
+
}),
|
|
236
|
+
)->ignore
|
|
247
237
|
}
|
|
248
238
|
})
|
|
249
239
|
}
|
|
@@ -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";
|
|
@@ -107,14 +106,14 @@ function dropCommittedEffects(inMemTable, committedCheckpointId, keepLoadedFromD
|
|
|
107
106
|
inMemTable.changesCount = inMemTable.changesCount - keysToDelete.length;
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
async function prepareRollbackDiff(state, rollbackTargetCheckpointId, rollbackDiffCheckpointId,
|
|
111
|
-
IndexerState.beginRollbackDiff(state, rollbackTargetCheckpointId, rollbackDiffCheckpointId,
|
|
109
|
+
async function prepareRollbackDiff(state, rollbackScope, rollbackTargetCheckpointId, rollbackDiffCheckpointId, progressedChains, rolledBackAddresses) {
|
|
110
|
+
IndexerState.beginRollbackDiff(state, rollbackTargetCheckpointId, rollbackDiffCheckpointId, rollbackScope, progressedChains, rolledBackAddresses);
|
|
112
111
|
let persistence = IndexerState.persistence(state);
|
|
113
112
|
let committedCheckpointId = IndexerState.committedCheckpointId(state);
|
|
114
113
|
let deletedEntities = {};
|
|
115
114
|
let setEntities = {};
|
|
116
115
|
await Promise.all(persistence.allEntities.filter(entityConfig => entityConfig.storage.postgres).map(async entityConfig => {
|
|
117
|
-
let match = await persistence.storage.getRollbackData(entityConfig, rollbackTargetCheckpointId);
|
|
116
|
+
let match = await persistence.storage.getRollbackData(entityConfig, rollbackScope, rollbackTargetCheckpointId);
|
|
118
117
|
match[0].forEach(param => {
|
|
119
118
|
let entityId = param.entityId;
|
|
120
119
|
Utils.Dict.push(deletedEntities, entityConfig.name, entityId);
|
|
@@ -142,8 +141,6 @@ async function prepareRollbackDiff(state, rollbackTargetCheckpointId, rollbackDi
|
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
function setBatchDcs(state, batch) {
|
|
145
|
-
let inMemTable = getInMemTable(state, Config.EnvioAddresses.entityConfig, "crossChain");
|
|
146
|
-
let committedCheckpointId = IndexerState.committedCheckpointId(state);
|
|
147
144
|
Utils.Dict.forEach(batch.progressedChainsById, progressedChain => {
|
|
148
145
|
let chainId = progressedChain.fetchState.chainId;
|
|
149
146
|
let chainState = IndexerState.getChainState(state, chainId);
|
|
@@ -158,24 +155,15 @@ function setBatchDcs(state, batch) {
|
|
|
158
155
|
checkpointBlockNumbers.push(batch.checkpointBlockNumbers[idx]);
|
|
159
156
|
}
|
|
160
157
|
}
|
|
161
|
-
ChainState.drainAddressesForWrite(chainState, progressedChain.progressBlockNumber, checkpointBlockNumbers).
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
contract_name: entity_contract_name
|
|
171
|
-
};
|
|
172
|
-
InMemoryTable.Entity.set(inMemTable, committedCheckpointId, {
|
|
173
|
-
type: "SET",
|
|
174
|
-
entityId: entity_id,
|
|
175
|
-
entity: entity,
|
|
176
|
-
checkpointId: checkpointIds[dc.checkpointIdx]
|
|
177
|
-
});
|
|
178
|
-
});
|
|
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
|
+
})));
|
|
179
167
|
});
|
|
180
168
|
}
|
|
181
169
|
|
package/src/IndexerState.res
CHANGED
|
@@ -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,9 +789,12 @@ let drainBatchRun = (state: t): Batch.t => {
|
|
|
786
789
|
checkpointBlockNumbers,
|
|
787
790
|
checkpointBlockHashes,
|
|
788
791
|
checkpointEventsProcessed,
|
|
792
|
+
registeredAddresses,
|
|
789
793
|
}
|
|
790
794
|
}
|
|
791
795
|
|
|
796
|
+
let pendingRollback = (state: t): option<Persistence.rollback> => state.rollback
|
|
797
|
+
|
|
792
798
|
// Take the pending rollback diff to write, clearing it from the store.
|
|
793
799
|
let takeRollback = (state: t): option<Persistence.rollback> => {
|
|
794
800
|
let rollback = state.rollback
|
|
@@ -797,23 +803,54 @@ let takeRollback = (state: t): option<Persistence.rollback> => {
|
|
|
797
803
|
}
|
|
798
804
|
|
|
799
805
|
// Advance the committed (durably persisted) frontier after a successful write.
|
|
800
|
-
|
|
806
|
+
// Written rows leave the buffer only once the transaction that holds them has
|
|
807
|
+
// committed. A failed write keeps them, and re-inserting a row the database
|
|
808
|
+
// already has is a no-op. Rows staged while the write was in flight belong to
|
|
809
|
+
// later checkpoints — ids only ever grow — so this can't drop one unwritten.
|
|
810
|
+
let markCommitted = (state: t, ~upToCheckpointId) => {
|
|
811
|
+
state.committedCheckpointId = upToCheckpointId
|
|
812
|
+
}
|
|
801
813
|
|
|
802
814
|
// Reset the in-memory tables and arm the rollback diff that the next write commits.
|
|
803
815
|
let beginRollbackDiff = (
|
|
804
816
|
state: t,
|
|
805
817
|
~targetCheckpointId,
|
|
806
818
|
~diffCheckpointId,
|
|
807
|
-
~
|
|
819
|
+
~scope,
|
|
820
|
+
~progressedChains: array<InternalTable.Chains.progressedChain>,
|
|
821
|
+
~rolledBackAddresses,
|
|
808
822
|
) => {
|
|
809
823
|
let perChainEntities = state.allEntities->EntityTables.perChain
|
|
810
824
|
state.entities = EntityTables.make(state.allEntities->EntityTables.crossChain)
|
|
811
825
|
state->chainStates->Utils.Dict.forEach(cs => cs->ChainState.resetEntities(~perChainEntities))
|
|
812
826
|
state.effectState->EffectState.resetForRollback
|
|
827
|
+
// The address store reports only what each rollback killed, and never
|
|
828
|
+
// re-reports a registration it already tombstoned. A rollback that lands
|
|
829
|
+
// before the previous one has been written must therefore carry the earlier
|
|
830
|
+
// keys too, or their rows outlive every rollback that could delete them.
|
|
831
|
+
let rolledBackAddresses = switch state.rollback {
|
|
832
|
+
| Some({rolledBackAddresses: pending}) => pending->Array.concat(rolledBackAddresses)
|
|
833
|
+
| None => rolledBackAddresses
|
|
834
|
+
}
|
|
835
|
+
// Same for the chains: this rollback recomputes progress from the checkpoints,
|
|
836
|
+
// so a chain the pending diff already moved can land on exactly the block it
|
|
837
|
+
// is now at and go unreported here. Its stored progress still needs
|
|
838
|
+
// correcting, so the pending rows carry over — this rollback's row for a
|
|
839
|
+
// chain wins, being the later reading of the same chain state.
|
|
840
|
+
let progressedChains = switch state.rollback {
|
|
841
|
+
| Some({progressedChains: pending}) =>
|
|
842
|
+
let byChainId = Dict.make()
|
|
843
|
+
pending->Array.forEach(chain => byChainId->ChainId.Dict.set(chain.chainId, chain))
|
|
844
|
+
progressedChains->Array.forEach(chain => byChainId->ChainId.Dict.set(chain.chainId, chain))
|
|
845
|
+
byChainId->Dict.valuesToArray
|
|
846
|
+
| None => progressedChains
|
|
847
|
+
}
|
|
813
848
|
state.rollback = Some({
|
|
814
849
|
targetCheckpointId,
|
|
815
850
|
diffCheckpointId,
|
|
816
|
-
|
|
851
|
+
scope,
|
|
852
|
+
progressedChains,
|
|
853
|
+
rolledBackAddresses,
|
|
817
854
|
})
|
|
818
855
|
}
|
|
819
856
|
|
package/src/IndexerState.res.mjs
CHANGED
|
@@ -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,10 +653,15 @@ 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
|
|
|
661
|
+
function pendingRollback(state) {
|
|
662
|
+
return state.rollback;
|
|
663
|
+
}
|
|
664
|
+
|
|
658
665
|
function takeRollback(state) {
|
|
659
666
|
let rollback = state.rollback;
|
|
660
667
|
state.rollback = undefined;
|
|
@@ -665,15 +672,33 @@ function markCommitted(state, upToCheckpointId) {
|
|
|
665
672
|
state.committedCheckpointId = upToCheckpointId;
|
|
666
673
|
}
|
|
667
674
|
|
|
668
|
-
function beginRollbackDiff(state, targetCheckpointId, diffCheckpointId,
|
|
675
|
+
function beginRollbackDiff(state, targetCheckpointId, diffCheckpointId, scope, progressedChains, rolledBackAddresses) {
|
|
669
676
|
let perChainEntities = EntityTables.perChain(state.allEntities);
|
|
670
677
|
state.entities = EntityTables.make(EntityTables.crossChain(state.allEntities));
|
|
671
678
|
Utils.Dict.forEach(CrossChainState.chainStates(state.crossChainState), cs => ChainState.resetEntities(cs, perChainEntities));
|
|
672
679
|
EffectState.resetForRollback(state.effectState);
|
|
680
|
+
let match = state.rollback;
|
|
681
|
+
let rolledBackAddresses$1 = match !== undefined ? match.rolledBackAddresses.concat(rolledBackAddresses) : rolledBackAddresses;
|
|
682
|
+
let match$1 = state.rollback;
|
|
683
|
+
let progressedChains$1;
|
|
684
|
+
if (match$1 !== undefined) {
|
|
685
|
+
let byChainId = {};
|
|
686
|
+
match$1.progressedChains.forEach(chain => {
|
|
687
|
+
byChainId[chain.chainId] = chain;
|
|
688
|
+
});
|
|
689
|
+
progressedChains.forEach(chain => {
|
|
690
|
+
byChainId[chain.chainId] = chain;
|
|
691
|
+
});
|
|
692
|
+
progressedChains$1 = Object.values(byChainId);
|
|
693
|
+
} else {
|
|
694
|
+
progressedChains$1 = progressedChains;
|
|
695
|
+
}
|
|
673
696
|
state.rollback = {
|
|
674
697
|
targetCheckpointId: targetCheckpointId,
|
|
675
698
|
diffCheckpointId: diffCheckpointId,
|
|
676
|
-
|
|
699
|
+
scope: scope,
|
|
700
|
+
rolledBackAddresses: rolledBackAddresses$1,
|
|
701
|
+
progressedChains: progressedChains$1
|
|
677
702
|
};
|
|
678
703
|
}
|
|
679
704
|
|
|
@@ -806,6 +831,7 @@ export {
|
|
|
806
831
|
recordRollbackSuccess,
|
|
807
832
|
queueProcessedBatch,
|
|
808
833
|
drainBatchRun,
|
|
834
|
+
pendingRollback,
|
|
809
835
|
takeRollback,
|
|
810
836
|
markCommitted,
|
|
811
837
|
beginRollbackDiff,
|
package/src/IndexerState.resi
CHANGED
|
@@ -136,13 +136,16 @@ let recordRollbackSuccess: (t, ~timeSeconds: float, ~rollbackedProcessedEvents:
|
|
|
136
136
|
// Store domain operations.
|
|
137
137
|
let queueProcessedBatch: (t, ~batch: Batch.t) => unit
|
|
138
138
|
let drainBatchRun: t => Batch.t
|
|
139
|
+
let pendingRollback: t => option<Persistence.rollback>
|
|
139
140
|
let takeRollback: t => option<Persistence.rollback>
|
|
140
141
|
let markCommitted: (t, ~upToCheckpointId: Internal.checkpointId) => unit
|
|
141
142
|
let beginRollbackDiff: (
|
|
142
143
|
t,
|
|
143
144
|
~targetCheckpointId: Internal.checkpointId,
|
|
144
145
|
~diffCheckpointId: Internal.checkpointId,
|
|
145
|
-
~
|
|
146
|
+
~scope: RollbackScope.t,
|
|
147
|
+
~progressedChains: array<InternalTable.Chains.progressedChain>,
|
|
148
|
+
~rolledBackAddresses: array<AddressRows.key>,
|
|
146
149
|
) => unit
|
|
147
150
|
let recordWriteFailure: (t, exn) => unit
|
|
148
151
|
let beginWriteFiber: (t, promise<unit>) => unit
|
package/src/Internal.res
CHANGED
|
@@ -573,15 +573,6 @@ type svmInstructionEventConfig = {
|
|
|
573
573
|
programId: SvmTypes.Pubkey.t,
|
|
574
574
|
/** Hex-encoded discriminator. `None` matches every instruction in the program. */
|
|
575
575
|
discriminator: option<string>,
|
|
576
|
-
/** Length of the discriminator in bytes (0 / 1 / 2 / 4 / 8). Drives the
|
|
577
|
-
`dN` selector at query time and the dispatch-key precomputation in the
|
|
578
|
-
router. */
|
|
579
|
-
discriminatorByteLen: int,
|
|
580
|
-
/** Disjunctive normal form: outer array is OR of AND-groups, inner array is
|
|
581
|
-
AND across positions. Empty outer array means "no account filter". */
|
|
582
|
-
accountFilters: array<svmAccountFilterGroup>,
|
|
583
|
-
/** `None` matches both outer and inner (CPI-invoked) instructions. */
|
|
584
|
-
isInner: option<bool>,
|
|
585
576
|
/** Positional account names from the Borsh schema, in declared order.
|
|
586
577
|
`[]` means no schema is attached for this instruction. */
|
|
587
578
|
accounts: array<string>,
|
|
@@ -597,10 +588,11 @@ type svmInstructionEventConfig = {
|
|
|
597
588
|
// Per-(event, chain) registration produced when user handler code registers an
|
|
598
589
|
// event (`onEvent`) or a dynamic contract registers. References its definition
|
|
599
590
|
// by value as `.eventConfig` and adds the handler binding plus the
|
|
600
|
-
// registration/`where`-derived fetch state. Not `private`: Fuel
|
|
601
|
-
//
|
|
602
|
-
//
|
|
603
|
-
//
|
|
591
|
+
// registration/`where`-derived fetch state. Not `private`: Fuel registrations
|
|
592
|
+
// add no ecosystem-specific fields (so the alias must stay directly
|
|
593
|
+
// constructable), and the ecosystem→base casts in sources are sound by
|
|
594
|
+
// ecosystem homogeneity — an EVM chain only ever holds
|
|
595
|
+
// `evmOnEventRegistration`s.
|
|
604
596
|
type onEventRegistration = {
|
|
605
597
|
// Chain-scoped sequential index — the registration's position in the
|
|
606
598
|
// chain's onEventRegistrations array, assigned when registration finishes
|
|
@@ -639,11 +631,18 @@ type evmOnEventRegistration = {
|
|
|
639
631
|
resolvedWhere: resolvedWhere,
|
|
640
632
|
}
|
|
641
633
|
|
|
642
|
-
// Fuel
|
|
643
|
-
//
|
|
644
|
-
// aliases of the base registration.
|
|
634
|
+
// Fuel registrations add no ecosystem-specific fetch state, so it's a bare
|
|
635
|
+
// alias of the base registration.
|
|
645
636
|
type fuelOnEventRegistration = onEventRegistration
|
|
646
|
-
|
|
637
|
+
|
|
638
|
+
type svmOnEventRegistration = {
|
|
639
|
+
...onEventRegistration,
|
|
640
|
+
/** Disjunctive normal form: outer array is OR of AND-groups, inner array is
|
|
641
|
+
AND across positions. Empty outer array means "no account filter". */
|
|
642
|
+
accountFilters: array<svmAccountFilterGroup>,
|
|
643
|
+
/** `None` matches both outer and inner (CPI-invoked) instructions. */
|
|
644
|
+
isInner: option<bool>,
|
|
645
|
+
}
|
|
647
646
|
|
|
648
647
|
type svmProgramConfig = {
|
|
649
648
|
name: string,
|
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
|
|
91
|
+
let getInitialState = (): option<Persistence.initialState> => {
|
|
92
92
|
switch getGlobalPersistence() {
|
|
93
93
|
| Some(persistence) =>
|
|
94
94
|
switch persistence.storageStatus {
|
|
95
|
-
| Ready(initialState) => initialState
|
|
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),
|
|
188
|
-
//
|
|
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
|
|
191
|
-
| Some(
|
|
192
|
-
|
|
193
|
-
chainState
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
}
|
|
@@ -577,6 +585,7 @@ let migrate = async (~reset) => {
|
|
|
577
585
|
await persistence->Persistence.init(
|
|
578
586
|
~reset,
|
|
579
587
|
~chainConfigs=config.chainMap->ChainMap.values,
|
|
588
|
+
~contractMapping=config.contractMapping,
|
|
580
589
|
~envioInfo=getEnvioInfo(),
|
|
581
590
|
~resetCommand="envio local db-migrate setup",
|
|
582
591
|
~runCommand=None,
|
|
@@ -628,6 +637,7 @@ let start = async (
|
|
|
628
637
|
await persistence->Persistence.init(
|
|
629
638
|
~reset,
|
|
630
639
|
~chainConfigs=config.chainMap->ChainMap.values,
|
|
640
|
+
~contractMapping=config.contractMapping,
|
|
631
641
|
~envioInfo=getEnvioInfo(),
|
|
632
642
|
~resetCommand=isDevelopmentMode ? "envio dev -r" : "envio start -r",
|
|
633
643
|
~runCommand=Some(isDevelopmentMode ? "envio dev" : "envio start"),
|
package/src/Main.res.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import * as Process from "process";
|
|
|
13
13
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
14
14
|
import * as PgStorage from "./PgStorage.res.mjs";
|
|
15
15
|
import * as ChainState from "./ChainState.res.mjs";
|
|
16
|
+
import * as AddressRows from "./AddressRows.res.mjs";
|
|
16
17
|
import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
|
|
17
18
|
import * as IndexerLoop from "./IndexerLoop.res.mjs";
|
|
18
19
|
import * as Persistence from "./Persistence.res.mjs";
|
|
@@ -24,6 +25,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
24
25
|
import * as Helpers from "yargs/helpers";
|
|
25
26
|
import * as RollbackCommit from "./RollbackCommit.res.mjs";
|
|
26
27
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
28
|
+
import * as ContractMapping from "./ContractMapping.res.mjs";
|
|
27
29
|
import * as HandlerRegister from "./HandlerRegister.res.mjs";
|
|
28
30
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
29
31
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
@@ -94,7 +96,7 @@ function setGlobalPersistence(persistence) {
|
|
|
94
96
|
EnvioGlobal.value.persistence = Primitive_option.some(persistence);
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
function
|
|
99
|
+
function getInitialState() {
|
|
98
100
|
let persistence = EnvioGlobal.value.persistence;
|
|
99
101
|
if (persistence === undefined) {
|
|
100
102
|
return;
|
|
@@ -103,10 +105,14 @@ function getInitialChainState(chainId) {
|
|
|
103
105
|
if (typeof initialState !== "object" || initialState.TAG === "Initializing") {
|
|
104
106
|
return;
|
|
105
107
|
} else {
|
|
106
|
-
return initialState._0
|
|
108
|
+
return initialState._0;
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
function getInitialChainState(chainId) {
|
|
113
|
+
return Stdlib_Option.flatMap(getInitialState(), initialState => initialState.chains.find(c => c.id === chainId));
|
|
114
|
+
}
|
|
115
|
+
|
|
110
116
|
function buildChainsObject(config) {
|
|
111
117
|
let chainIds = [];
|
|
112
118
|
let chains = Object.create(null);
|
|
@@ -178,18 +184,16 @@ function buildChainsObject(config) {
|
|
|
178
184
|
let chainState = IndexerState.getChainState(Primitive_option.valFromOption(state), chainConfig.id);
|
|
179
185
|
return ChainState.contractAddresses(chainState, contract.name);
|
|
180
186
|
}
|
|
181
|
-
let
|
|
182
|
-
if (
|
|
187
|
+
let initialState = getInitialState();
|
|
188
|
+
if (initialState === undefined) {
|
|
189
|
+
return contract.addresses;
|
|
190
|
+
}
|
|
191
|
+
let chainState$1 = initialState.chains.find(c => c.id === chainConfig.id);
|
|
192
|
+
if (chainState$1 !== undefined) {
|
|
193
|
+
return AddressRows.renderOfContract(chainState$1.addressRows, config.ecosystem.name, !config.lowercaseAddresses, ContractMapping.idOfOrThrow(initialState.contractMapping, contract.name, undefined));
|
|
194
|
+
} else {
|
|
183
195
|
return contract.addresses;
|
|
184
196
|
}
|
|
185
|
-
let addresses = contract.addresses.slice();
|
|
186
|
-
chainState$1.indexingAddresses.forEach(dc => {
|
|
187
|
-
if (dc.contractName === contract.name) {
|
|
188
|
-
addresses.push(dc.address);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
return addresses;
|
|
193
197
|
}
|
|
194
198
|
});
|
|
195
199
|
Object.defineProperty(chainObj, contract.name, {
|
|
@@ -463,7 +467,7 @@ function getEnvioInfo() {
|
|
|
463
467
|
async function migrate(reset) {
|
|
464
468
|
let config = Config.load();
|
|
465
469
|
let persistence = PgStorage.makePersistenceFromConfig(config, undefined);
|
|
466
|
-
await Persistence.init(persistence, ChainMap.values(config.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), "envio local db-migrate setup", undefined, reset);
|
|
470
|
+
await Persistence.init(persistence, ChainMap.values(config.chainMap), config.contractMapping, Config.stripSensitiveData(Config.getPublicConfigJson()), "envio local db-migrate setup", undefined, reset);
|
|
467
471
|
return await persistence.storage.close();
|
|
468
472
|
}
|
|
469
473
|
|
|
@@ -490,7 +494,7 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
|
|
|
490
494
|
let isDevelopmentMode = !isTest && config.isDev;
|
|
491
495
|
let persistence$1 = persistence !== undefined ? persistence : PgStorage.makePersistenceFromConfig(config, undefined);
|
|
492
496
|
EnvioGlobal.value.persistence = Primitive_option.some(persistence$1);
|
|
493
|
-
await Persistence.init(persistence$1, ChainMap.values(config.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), isDevelopmentMode ? "envio dev -r" : "envio start -r", isDevelopmentMode ? "envio dev" : "envio start", reset);
|
|
497
|
+
await Persistence.init(persistence$1, ChainMap.values(config.chainMap), config.contractMapping, Config.stripSensitiveData(Config.getPublicConfigJson()), isDevelopmentMode ? "envio dev -r" : "envio start -r", isDevelopmentMode ? "envio dev" : "envio start", reset);
|
|
494
498
|
let registrationsByChainId = await HandlerLoader.registerAllHandlers(config);
|
|
495
499
|
let config$1;
|
|
496
500
|
if (isTest) {
|
|
@@ -552,6 +556,7 @@ export {
|
|
|
552
556
|
setIndexerState,
|
|
553
557
|
getGlobalPersistence,
|
|
554
558
|
setGlobalPersistence,
|
|
559
|
+
getInitialState,
|
|
555
560
|
getInitialChainState,
|
|
556
561
|
buildChainsObject,
|
|
557
562
|
getGlobalIndexer,
|
package/src/Metrics.res
CHANGED
|
@@ -12,6 +12,8 @@ type chainMetrics = {
|
|
|
12
12
|
startBlock: int,
|
|
13
13
|
endBlock: option<int>,
|
|
14
14
|
numAddresses: int,
|
|
15
|
+
// Per-contract registration counts, in the chain's contract-id order.
|
|
16
|
+
addressesByContract: array<(string, int)>,
|
|
15
17
|
isReady: bool,
|
|
16
18
|
// Raw source height, unlike knownHeight which is clamped to endBlock.
|
|
17
19
|
sourceBlockNumber: int,
|
|
@@ -694,11 +696,23 @@ let renderMetrics = (b: builder, metrics: t) => {
|
|
|
694
696
|
)
|
|
695
697
|
b->series(
|
|
696
698
|
~name="envio_indexing_addresses",
|
|
697
|
-
~help="The number of
|
|
699
|
+
~help="The number of address registrations on chain, static and dynamic. An address shared by N contracts counts N times.",
|
|
698
700
|
~kind="gauge",
|
|
699
701
|
~entries=chains,
|
|
700
702
|
~value=m => m.numAddresses->Int.toFloat,
|
|
701
703
|
)
|
|
704
|
+
b->series(
|
|
705
|
+
~name="envio_indexing_contract_addresses",
|
|
706
|
+
~help="The number of address registrations per contract on chain, static and dynamic. An address shared by N contracts counts N times.",
|
|
707
|
+
~kind="gauge",
|
|
708
|
+
~entries=metrics.chains->Array.flatMap(m =>
|
|
709
|
+
m.addressesByContract->Array.map(((contract, count)) => (
|
|
710
|
+
`{chainId="${m.chainId->ChainId.toString}",contract="${contract->escapeLabelValue}"}`,
|
|
711
|
+
count,
|
|
712
|
+
))
|
|
713
|
+
),
|
|
714
|
+
~value=count => count->Int.toFloat,
|
|
715
|
+
)
|
|
702
716
|
}
|
|
703
717
|
|
|
704
718
|
let contentType = "text/plain; version=0.0.4; charset=utf-8"
|