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