envio 3.5.0-alpha.1 → 3.5.0-alpha.2
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/index.d.ts +10 -6
- package/package.json +6 -6
- package/src/BatchProcessing.res +13 -0
- package/src/BatchProcessing.res.mjs +4 -0
- package/src/ChainFetching.res +5 -5
- package/src/ChainFetching.res.mjs +3 -4
- package/src/ChainState.res +38 -21
- package/src/ChainState.res.mjs +17 -24
- package/src/ChainState.resi +1 -5
- package/src/Change.res +3 -3
- package/src/Config.res +0 -4
- package/src/Config.res.mjs +0 -10
- package/src/Core.res +3 -0
- package/src/EntityId.res +15 -0
- package/src/EntityId.res.mjs +9 -0
- package/src/EventConfigBuilder.res +1 -58
- package/src/EventConfigBuilder.res.mjs +1 -33
- package/src/FetchState.res +345 -459
- package/src/FetchState.res.mjs +269 -430
- package/src/HandlerRegister.res +45 -0
- package/src/HandlerRegister.res.mjs +43 -0
- package/src/InMemoryStore.res +11 -4
- package/src/InMemoryTable.res +12 -10
- package/src/InMemoryTable.res.mjs +6 -5
- package/src/IndexerState.res +59 -2
- package/src/IndexerState.res.mjs +48 -3
- package/src/IndexerState.resi +2 -0
- package/src/Internal.res +17 -8
- package/src/LogSelection.res +2 -1
- package/src/Metrics.res +37 -6
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +1 -1
- package/src/PgStorage.res +55 -22
- package/src/PgStorage.res.mjs +25 -17
- package/src/SimulateItems.res +2 -2
- package/src/TestIndexer.res +6 -5
- package/src/TestIndexer.res.mjs +3 -2
- package/src/UserContext.res +3 -3
- package/src/Writing.res +12 -2
- package/src/Writing.res.mjs +13 -2
- package/src/bindings/ClickHouse.res +5 -3
- package/src/bindings/ClickHouse.res.mjs +2 -5
- package/src/bindings/Vitest.res +22 -1
- package/src/bindings/Vitest.res.mjs +15 -0
- package/src/db/EntityHistory.res +16 -7
- package/src/db/EntityHistory.res.mjs +7 -6
- package/src/db/Table.res +40 -9
- package/src/db/Table.res.mjs +44 -6
- package/src/sources/AddressSet.res +48 -0
- package/src/sources/AddressSet.res.mjs +11 -0
- package/src/sources/AddressStore.res +152 -0
- package/src/sources/AddressStore.res.mjs +97 -0
- package/src/sources/EvmChain.res +3 -0
- package/src/sources/EvmChain.res.mjs +4 -2
- package/src/sources/EvmHyperSyncSource.res +6 -3
- package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
- package/src/sources/EvmRpcClient.res +6 -3
- package/src/sources/EvmRpcClient.res.mjs +3 -3
- package/src/sources/FuelHyperSync.res +4 -3
- package/src/sources/FuelHyperSync.res.mjs +3 -3
- package/src/sources/FuelHyperSync.resi +2 -1
- package/src/sources/FuelHyperSyncClient.res +12 -6
- package/src/sources/FuelHyperSyncClient.res.mjs +2 -2
- package/src/sources/FuelHyperSyncSource.res +7 -4
- package/src/sources/FuelHyperSyncSource.res.mjs +3 -3
- package/src/sources/HyperSync.res +6 -4
- package/src/sources/HyperSync.res.mjs +2 -3
- package/src/sources/HyperSync.resi +1 -1
- package/src/sources/HyperSyncClient.res +18 -9
- package/src/sources/HyperSyncClient.res.mjs +4 -4
- package/src/sources/RpcSource.res +15 -10
- package/src/sources/RpcSource.res.mjs +3 -4
- package/src/sources/SimulateSource.res +26 -11
- package/src/sources/SimulateSource.res.mjs +13 -5
- package/src/sources/Source.res +4 -2
- package/src/sources/SourceManager.res +2 -3
- package/src/sources/SourceManager.res.mjs +2 -3
- package/src/sources/Svm.res +1 -2
- package/src/sources/Svm.res.mjs +1 -1
- package/src/sources/SvmHyperSyncClient.res +12 -3
- package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
- package/src/sources/SvmHyperSyncSource.res +10 -5
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
- package/src/IndexingAddresses.res +0 -151
- package/src/IndexingAddresses.res.mjs +0 -130
- package/src/IndexingAddresses.resi +0 -39
package/index.d.ts
CHANGED
|
@@ -585,14 +585,18 @@ export type SvmOnSlotContext<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
585
585
|
BaseHandlerContext<Config, SvmChainIds<Config>>
|
|
586
586
|
>;
|
|
587
587
|
|
|
588
|
+
/** The entity's `id` type. `ID!`/`String!` ids are `string`; `Int!` is `number`
|
|
589
|
+
* and `BigInt!` is `bigint`, so id-keyed operations accept the real scalar. */
|
|
590
|
+
type EntityId<Entity> = Entity extends { readonly id: infer Id } ? Id : string;
|
|
591
|
+
|
|
588
592
|
/** Entity operations available in handler contexts. */
|
|
589
593
|
type EntityOperations<Entity> = {
|
|
590
|
-
readonly get: (id:
|
|
591
|
-
readonly getOrThrow: (id:
|
|
594
|
+
readonly get: (id: EntityId<Entity>) => Promise<Entity | undefined>;
|
|
595
|
+
readonly getOrThrow: (id: EntityId<Entity>, message?: string) => Promise<Entity>;
|
|
592
596
|
readonly getWhere: (filter: GetWhereFilter<Entity>) => Promise<Entity[]>;
|
|
593
597
|
readonly getOrCreate: (entity: Entity) => Promise<Entity>;
|
|
594
598
|
readonly set: (entity: Entity) => void;
|
|
595
|
-
readonly deleteUnsafe: (id:
|
|
599
|
+
readonly deleteUnsafe: (id: EntityId<Entity>) => void;
|
|
596
600
|
};
|
|
597
601
|
|
|
598
602
|
/** Contract registration handle. */
|
|
@@ -1510,7 +1514,7 @@ type EntityChangeValue<Entity> = {
|
|
|
1510
1514
|
/** Entities that were created or updated. */
|
|
1511
1515
|
readonly sets?: readonly Entity[];
|
|
1512
1516
|
/** IDs of entities that were deleted. */
|
|
1513
|
-
readonly deleted?: readonly
|
|
1517
|
+
readonly deleted?: readonly EntityId<Entity>[];
|
|
1514
1518
|
};
|
|
1515
1519
|
|
|
1516
1520
|
/** A dynamic contract address registration. */
|
|
@@ -1528,9 +1532,9 @@ type ConfigEntities<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1528
1532
|
/** Entity operations available on test indexer for direct entity manipulation. */
|
|
1529
1533
|
type TestIndexerEntityOperations<Entity> = {
|
|
1530
1534
|
/** Get an entity by ID. Returns undefined if not found. */
|
|
1531
|
-
readonly get: (id:
|
|
1535
|
+
readonly get: (id: EntityId<Entity>) => Promise<Entity | undefined>;
|
|
1532
1536
|
/** Get an entity by ID or throw if not found. */
|
|
1533
|
-
readonly getOrThrow: (id:
|
|
1537
|
+
readonly getOrThrow: (id: EntityId<Entity>, message?: string) => Promise<Entity>;
|
|
1534
1538
|
/** Get all entities. */
|
|
1535
1539
|
readonly getAll: () => Promise<Entity[]>;
|
|
1536
1540
|
/** Set (create or update) an entity. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.5.0-alpha.
|
|
3
|
+
"version": "3.5.0-alpha.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"tsx": "4.21.0"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|
|
72
|
-
"envio-linux-x64": "3.5.0-alpha.
|
|
73
|
-
"envio-linux-x64-musl": "3.5.0-alpha.
|
|
74
|
-
"envio-linux-arm64": "3.5.0-alpha.
|
|
75
|
-
"envio-darwin-x64": "3.5.0-alpha.
|
|
76
|
-
"envio-darwin-arm64": "3.5.0-alpha.
|
|
72
|
+
"envio-linux-x64": "3.5.0-alpha.2",
|
|
73
|
+
"envio-linux-x64-musl": "3.5.0-alpha.2",
|
|
74
|
+
"envio-linux-arm64": "3.5.0-alpha.2",
|
|
75
|
+
"envio-darwin-x64": "3.5.0-alpha.2",
|
|
76
|
+
"envio-darwin-arm64": "3.5.0-alpha.2"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/BatchProcessing.res
CHANGED
|
@@ -45,6 +45,19 @@ let rec startProcessing = async (state: IndexerState.t, ~scheduleFetch, ~schedul
|
|
|
45
45
|
// Hand off now that no batch is in flight.
|
|
46
46
|
if state->IndexerState.isResolvingReorg {
|
|
47
47
|
scheduleRollback()
|
|
48
|
+
} else if (
|
|
49
|
+
!(state->IndexerState.isStopped) &&
|
|
50
|
+
// Only a genuine fetch stall when some chain still has blocks to bring in.
|
|
51
|
+
// If every chain has buffered up to its known head, the loop is idling at
|
|
52
|
+
// the tip waiting for new blocks, not bottlenecked on fetch.
|
|
53
|
+
!(
|
|
54
|
+
state
|
|
55
|
+
->IndexerState.chainStates
|
|
56
|
+
->Dict.valuesToArray
|
|
57
|
+
->Array.every(ChainState.isFetchingAtHead)
|
|
58
|
+
)
|
|
59
|
+
) {
|
|
60
|
+
state->IndexerState.markProcessingStalledOnFetch
|
|
48
61
|
}
|
|
49
62
|
}
|
|
50
63
|
}
|
|
@@ -108,6 +108,10 @@ async function startProcessing(state, scheduleFetch, scheduleRollback) {
|
|
|
108
108
|
IndexerState.endProcessing(state);
|
|
109
109
|
if (IndexerState.isResolvingReorg(state)) {
|
|
110
110
|
return scheduleRollback();
|
|
111
|
+
} else if (!IndexerState.isStopped(state) && !Object.values(IndexerState.chainStates(state)).every(ChainState.isFetchingAtHead)) {
|
|
112
|
+
return IndexerState.markProcessingStalledOnFetch(state);
|
|
113
|
+
} else {
|
|
114
|
+
return;
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
117
|
|
package/src/ChainFetching.res
CHANGED
|
@@ -209,11 +209,11 @@ let rec onQueryResponse = async (
|
|
|
209
209
|
// kick (eg from the processing loop quiescing) collapses into this one.
|
|
210
210
|
scheduleRollback()
|
|
211
211
|
| None =>
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
// registered after the log's block)
|
|
215
|
-
//
|
|
216
|
-
let newItems =
|
|
212
|
+
// Over-fetched events (a merged partition returning an address before its
|
|
213
|
+
// effectiveStartBlock, or a wildcard param referencing an address
|
|
214
|
+
// registered after the log's block) are already dropped by the source's
|
|
215
|
+
// native address gate, so everything here is indexable.
|
|
216
|
+
let newItems = parsedQueueItems
|
|
217
217
|
let itemsWithContractRegister = []
|
|
218
218
|
for idx in 0 to newItems->Array.length - 1 {
|
|
219
219
|
let item = newItems->Array.getUnsafe(idx)
|
|
@@ -145,17 +145,16 @@ async function onQueryResponse(state, param, stateId, scheduleFetch, schedulePro
|
|
|
145
145
|
IndexerState.beginReorg(state, chain, rollbackWithReorgDetectedBlockNumber);
|
|
146
146
|
return scheduleRollback();
|
|
147
147
|
}
|
|
148
|
-
let newItems = ChainState.filterByClientAddress(chainState, parsedQueueItems);
|
|
149
148
|
let itemsWithContractRegister = [];
|
|
150
|
-
for (let idx = 0, idx_finish =
|
|
151
|
-
let item =
|
|
149
|
+
for (let idx = 0, idx_finish = parsedQueueItems.length; idx < idx_finish; ++idx) {
|
|
150
|
+
let item = parsedQueueItems[idx];
|
|
152
151
|
if (item.onEventRegistration.contractRegister !== undefined) {
|
|
153
152
|
itemsWithContractRegister.push(item);
|
|
154
153
|
}
|
|
155
154
|
}
|
|
156
155
|
let proceed = newItemsWithDcs => {
|
|
157
156
|
if (!IndexerState.isStale(state, stateId)) {
|
|
158
|
-
applyQueryResponse(state, chain,
|
|
157
|
+
applyQueryResponse(state, chain, parsedQueueItems, newItemsWithDcs, knownHeight, {
|
|
159
158
|
blockNumber: latestFetchedBlockNumber,
|
|
160
159
|
blockTimestamp: latestFetchedBlockTimestamp
|
|
161
160
|
}, query, transactionStore, blockStore);
|
package/src/ChainState.res
CHANGED
|
@@ -7,9 +7,10 @@ type t = {
|
|
|
7
7
|
// The registrations used to build this chain's sources and route native items.
|
|
8
8
|
onEventRegistrations: array<Internal.onEventRegistration>,
|
|
9
9
|
mutable fetchState: FetchState.t,
|
|
10
|
-
// The chain-wide address index. Not `mutable`:
|
|
11
|
-
//
|
|
12
|
-
|
|
10
|
+
// The chain-wide address index, kept in Rust. Not `mutable`: registration and
|
|
11
|
+
// rollback mutate it in place, so the handle is stable across fetchState
|
|
12
|
+
// versions — and it's the same handle this chain's source clients hold.
|
|
13
|
+
addressStore: AddressStore.t,
|
|
13
14
|
sourceManager: SourceManager.t,
|
|
14
15
|
chainConfig: Config.chain,
|
|
15
16
|
mutable isProgressAtHead: bool,
|
|
@@ -83,7 +84,7 @@ let make = (
|
|
|
83
84
|
~chainConfig: Config.chain,
|
|
84
85
|
~fetchState: FetchState.t,
|
|
85
86
|
~onEventRegistrations=[],
|
|
86
|
-
~
|
|
87
|
+
~addressStore: AddressStore.t,
|
|
87
88
|
~sourceManager: SourceManager.t,
|
|
88
89
|
~reorgDetection: ReorgDetection.t,
|
|
89
90
|
~committedProgressBlockNumber: int,
|
|
@@ -102,7 +103,7 @@ let make = (
|
|
|
102
103
|
logger,
|
|
103
104
|
onEventRegistrations,
|
|
104
105
|
fetchState,
|
|
105
|
-
|
|
106
|
+
addressStore,
|
|
106
107
|
sourceManager,
|
|
107
108
|
chainConfig,
|
|
108
109
|
isProgressAtHead,
|
|
@@ -166,12 +167,18 @@ let makeInternal = (
|
|
|
166
167
|
}
|
|
167
168
|
})
|
|
168
169
|
|
|
169
|
-
let
|
|
170
|
-
|
|
170
|
+
let lowercaseAddresses = config.lowercaseAddresses
|
|
171
|
+
// Created before the fetch state and the sources: `make` registers this
|
|
172
|
+
// chain's addresses into it, and every source client holds the same handle.
|
|
173
|
+
let addressStore = AddressStore.make(
|
|
174
|
+
~ecosystem=config.ecosystem.name,
|
|
175
|
+
~shouldChecksum=!lowercaseAddresses,
|
|
176
|
+
~contracts=AddressStore.contractsOf(~onEventRegistrations),
|
|
177
|
+
)
|
|
171
178
|
|
|
172
179
|
let fetchState = FetchState.make(
|
|
173
180
|
~maxAddrInPartition=config.maxAddrInPartition,
|
|
174
|
-
~
|
|
181
|
+
~addressStore,
|
|
175
182
|
~addresses=indexingAddresses,
|
|
176
183
|
~progressBlockNumber,
|
|
177
184
|
~startBlock,
|
|
@@ -187,8 +194,11 @@ let makeInternal = (
|
|
|
187
194
|
),
|
|
188
195
|
~onBlockRegistrations,
|
|
189
196
|
~firstEventBlock,
|
|
190
|
-
//
|
|
191
|
-
//
|
|
197
|
+
// Fuel and SVM route through the address store like EVM does, so the
|
|
198
|
+
// client-side path works for them — but their address counts are nowhere
|
|
199
|
+
// near the threshold, so switching would only trade a server-side filter
|
|
200
|
+
// that costs nothing today for an over-fetch. Keep them server-side until a
|
|
201
|
+
// chain actually needs it.
|
|
192
202
|
~clientFilterAddressThreshold=switch config.ecosystem.name {
|
|
193
203
|
| Evm => Some(config.clientFilterAddressThreshold)
|
|
194
204
|
| Fuel | Svm => None
|
|
@@ -205,7 +215,6 @@ let makeInternal = (
|
|
|
205
215
|
|
|
206
216
|
// Create sources lazily here - this is where API token validation happens
|
|
207
217
|
let chain = ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)
|
|
208
|
-
let lowercaseAddresses = config.lowercaseAddresses
|
|
209
218
|
let sources = switch chainConfig.sourceConfig {
|
|
210
219
|
| Config.EvmSourceConfig({hypersync, rpcs}) =>
|
|
211
220
|
let evmRpcs: array<EvmChain.rpc> = rpcs->Array.map((rpc): EvmChain.rpc => {
|
|
@@ -228,6 +237,7 @@ let makeInternal = (
|
|
|
228
237
|
~hyperSync=hypersync,
|
|
229
238
|
~rpcs=evmRpcs,
|
|
230
239
|
~lowercaseAddresses,
|
|
240
|
+
~addressStore,
|
|
231
241
|
)
|
|
232
242
|
| Config.FuelSourceConfig({hypersync}) => [
|
|
233
243
|
FuelHyperSyncSource.make({
|
|
@@ -235,6 +245,7 @@ let makeInternal = (
|
|
|
235
245
|
endpointUrl: hypersync,
|
|
236
246
|
apiToken: Env.envioApiToken,
|
|
237
247
|
onEventRegistrations,
|
|
248
|
+
addressStore,
|
|
238
249
|
}),
|
|
239
250
|
]
|
|
240
251
|
| Config.SvmSourceConfig({hypersync, rpc}) =>
|
|
@@ -255,6 +266,7 @@ let makeInternal = (
|
|
|
255
266
|
apiToken,
|
|
256
267
|
onEventRegistrations,
|
|
257
268
|
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
|
|
269
|
+
addressStore,
|
|
258
270
|
}),
|
|
259
271
|
]
|
|
260
272
|
}
|
|
@@ -274,7 +286,7 @@ let makeInternal = (
|
|
|
274
286
|
~chainConfig,
|
|
275
287
|
~fetchState,
|
|
276
288
|
~onEventRegistrations,
|
|
277
|
-
~
|
|
289
|
+
~addressStore,
|
|
278
290
|
~sourceManager=SourceManager.make(~sources, ~isRealtime, ~reducedPollingInterval?),
|
|
279
291
|
~reorgDetection=ReorgDetection.make(
|
|
280
292
|
~chainReorgCheckpoints,
|
|
@@ -415,7 +427,7 @@ let setRollbackTargetBlock = (cs: t, ~blockNumber) => cs.rollbackTargetBlock = S
|
|
|
415
427
|
// rather than reaching into it.
|
|
416
428
|
let knownHeight = (cs: t) => cs.fetchState.knownHeight
|
|
417
429
|
let contractAddresses = (cs: t, ~contractName) =>
|
|
418
|
-
cs.
|
|
430
|
+
cs.addressStore->AddressStore.contractAddresses(contractName)
|
|
419
431
|
let bufferSize = (cs: t) => cs.fetchState->FetchState.bufferSize
|
|
420
432
|
let bufferReadyCount = (cs: t) => cs.fetchState->FetchState.bufferReadyCount
|
|
421
433
|
let getProgressPercentage = (cs: t) => cs.fetchState->FetchState.getProgressPercentage
|
|
@@ -706,6 +718,9 @@ let groupBatchItems = (items: array<Internal.item>, ~includeBlocks: bool): (
|
|
|
706
718
|
}
|
|
707
719
|
}
|
|
708
720
|
}
|
|
721
|
+
// onBlock items build their block from the handler's own block number, not
|
|
722
|
+
// from the stores — which is what lets the sources keep only the blocks and
|
|
723
|
+
// transactions an event item references.
|
|
709
724
|
| Internal.Block(_) => ()
|
|
710
725
|
}
|
|
711
726
|
)
|
|
@@ -802,15 +817,12 @@ let materializePageItems = async (
|
|
|
802
817
|
))
|
|
803
818
|
}
|
|
804
819
|
|
|
805
|
-
let filterByClientAddress = (cs: t, items: array<Internal.item>): array<Internal.item> =>
|
|
806
|
-
items->FetchState.filterByClientAddress(~indexingAddresses=cs.indexingAddresses)
|
|
807
|
-
|
|
808
820
|
let handleQueryResult = (
|
|
809
821
|
cs: t,
|
|
810
822
|
~query: FetchState.query,
|
|
811
823
|
~newItems,
|
|
812
824
|
~newItemsWithDcs,
|
|
813
|
-
~latestFetchedBlock,
|
|
825
|
+
~latestFetchedBlock: FetchState.blockNumberAndTimestamp,
|
|
814
826
|
~knownHeight,
|
|
815
827
|
~transactionStore as txPage: option<TransactionStore.t>,
|
|
816
828
|
~blockStore as blockPage: option<BlockStore.t>,
|
|
@@ -830,7 +842,12 @@ let handleQueryResult = (
|
|
|
830
842
|
| [] => cs.fetchState
|
|
831
843
|
| _ =>
|
|
832
844
|
cs.fetchState->FetchState.registerDynamicContracts(
|
|
833
|
-
~
|
|
845
|
+
~addressStore=cs.addressStore,
|
|
846
|
+
// This response is applied below, after the addresses land. It was routed
|
|
847
|
+
// before they existed, so whatever it claims has to be inside the
|
|
848
|
+
// catch-up range — and an unbounded query can reach past the height that
|
|
849
|
+
// was known when it went out.
|
|
850
|
+
~claimCeiling=Pervasives.max(knownHeight, latestFetchedBlock.blockNumber),
|
|
834
851
|
newItemsWithDcs,
|
|
835
852
|
)
|
|
836
853
|
}
|
|
@@ -906,7 +923,7 @@ let toMetrics = (cs: t): Metrics.chainMetrics => {
|
|
|
906
923
|
numBatchesFetched: 0,
|
|
907
924
|
startBlock: cs.fetchState.startBlock,
|
|
908
925
|
endBlock: cs.fetchState.endBlock,
|
|
909
|
-
numAddresses: cs.
|
|
926
|
+
numAddresses: cs.addressStore->AddressStore.size,
|
|
910
927
|
isReady: cs->isReady,
|
|
911
928
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
912
929
|
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
@@ -1111,7 +1128,7 @@ let rollback = (
|
|
|
1111
1128
|
}
|
|
1112
1129
|
cs.fetchState =
|
|
1113
1130
|
cs.fetchState->FetchState.rollback(
|
|
1114
|
-
~
|
|
1131
|
+
~addressStore=cs.addressStore,
|
|
1115
1132
|
~targetBlockNumber=newProgressBlockNumber,
|
|
1116
1133
|
)
|
|
1117
1134
|
cs.transactionStore->TransactionStore.rollback(newProgressBlockNumber)
|
|
@@ -1127,7 +1144,7 @@ let rollback = (
|
|
|
1127
1144
|
)
|
|
1128
1145
|
cs.fetchState =
|
|
1129
1146
|
cs.fetchState->FetchState.rollback(
|
|
1130
|
-
~
|
|
1147
|
+
~addressStore=cs.addressStore,
|
|
1131
1148
|
~targetBlockNumber=rollbackTargetBlockNumber,
|
|
1132
1149
|
)
|
|
1133
1150
|
cs.transactionStore->TransactionStore.rollback(rollbackTargetBlockNumber)
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import * as FieldMask from "./sources/FieldMask.res.mjs";
|
|
|
11
11
|
import * as BlockStore from "./sources/BlockStore.res.mjs";
|
|
12
12
|
import * as FetchState from "./FetchState.res.mjs";
|
|
13
13
|
import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
|
|
14
|
+
import * as AddressStore from "./sources/AddressStore.res.mjs";
|
|
14
15
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
15
16
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
16
17
|
import * as SourceManager from "./sources/SourceManager.res.mjs";
|
|
@@ -20,7 +21,6 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
|
20
21
|
import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
21
22
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
22
23
|
import * as TransactionStore from "./sources/TransactionStore.res.mjs";
|
|
23
|
-
import * as IndexingAddresses from "./IndexingAddresses.res.mjs";
|
|
24
24
|
import * as SvmHyperSyncSource from "./sources/SvmHyperSyncSource.res.mjs";
|
|
25
25
|
import * as FuelHyperSyncSource from "./sources/FuelHyperSyncSource.res.mjs";
|
|
26
26
|
import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
|
|
@@ -47,7 +47,7 @@ function validateOnEventRegistrations(chainId, registrations) {
|
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
function make(chainConfig, fetchState, onEventRegistrationsOpt,
|
|
50
|
+
function make(chainConfig, fetchState, onEventRegistrationsOpt, addressStore, sourceManager, reorgDetection, committedProgressBlockNumber, safeCheckpointTrackingOpt, numEventsProcessedOpt, timestampCaughtUpToHeadOrEndblockOpt, isProgressAtHeadOpt, transactionStoreOpt, chainDensityOpt, blockStoreOpt, reorgThresholdReadyToleranceOpt, logger) {
|
|
51
51
|
let onEventRegistrations = onEventRegistrationsOpt !== undefined ? onEventRegistrationsOpt : [];
|
|
52
52
|
let safeCheckpointTracking = safeCheckpointTrackingOpt !== undefined ? Primitive_option.valFromOption(safeCheckpointTrackingOpt) : undefined;
|
|
53
53
|
let numEventsProcessed = numEventsProcessedOpt !== undefined ? numEventsProcessedOpt : 0;
|
|
@@ -62,7 +62,7 @@ function make(chainConfig, fetchState, onEventRegistrationsOpt, indexingAddresse
|
|
|
62
62
|
logger: logger,
|
|
63
63
|
onEventRegistrations: onEventRegistrations,
|
|
64
64
|
fetchState: fetchState,
|
|
65
|
-
|
|
65
|
+
addressStore: addressStore,
|
|
66
66
|
sourceManager: sourceManager,
|
|
67
67
|
chainConfig: chainConfig,
|
|
68
68
|
isProgressAtHead: isProgressAtHead,
|
|
@@ -103,8 +103,8 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
|
|
|
103
103
|
return Stdlib_JsError.throwWithMessage(`The start block for contract "` + contract.name + `" is less than the chain start block. This is not supported yet.`);
|
|
104
104
|
}
|
|
105
105
|
});
|
|
106
|
-
let
|
|
107
|
-
let
|
|
106
|
+
let lowercaseAddresses = config.lowercaseAddresses;
|
|
107
|
+
let addressStore = AddressStore.make(config.ecosystem.name, !lowercaseAddresses, AddressStore.contractsOf(onEventRegistrations));
|
|
108
108
|
let match$1 = config.ecosystem.name;
|
|
109
109
|
let tmp;
|
|
110
110
|
switch (match$1) {
|
|
@@ -116,14 +116,13 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
|
|
|
116
116
|
tmp = undefined;
|
|
117
117
|
break;
|
|
118
118
|
}
|
|
119
|
-
let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations,
|
|
119
|
+
let fetchState = FetchState.make(startBlock, endBlock, onEventRegistrations, addressStore, indexingAddresses, config.maxAddrInPartition, chainConfig.id, (config.batchSize << 1), knownHeight, progressBlockNumber, match.onBlockRegistrations, Primitive_int.max(!config.shouldRollbackOnReorg || isInReorgThreshold ? 0 : chainConfig.maxReorgDepth, chainConfig.blockLag), Primitive_option.some(firstEventBlock), Primitive_option.some(tmp));
|
|
120
120
|
let chainReorgCheckpoints = Stdlib_Array.filterMap(reorgCheckpoints, reorgCheckpoint => {
|
|
121
121
|
if (reorgCheckpoint.chain_id === chainConfig.id) {
|
|
122
122
|
return reorgCheckpoint;
|
|
123
123
|
}
|
|
124
124
|
});
|
|
125
125
|
let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
126
|
-
let lowercaseAddresses = config.lowercaseAddresses;
|
|
127
126
|
let sources = chainConfig.sourceConfig;
|
|
128
127
|
let sources$1;
|
|
129
128
|
switch (sources.TAG) {
|
|
@@ -140,14 +139,15 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
|
|
|
140
139
|
headers: headers
|
|
141
140
|
};
|
|
142
141
|
});
|
|
143
|
-
sources$1 = EvmChain.makeSources(chain, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses);
|
|
142
|
+
sources$1 = EvmChain.makeSources(chain, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
|
|
144
143
|
break;
|
|
145
144
|
case "FuelSourceConfig" :
|
|
146
145
|
sources$1 = [FuelHyperSyncSource.make({
|
|
147
146
|
chain: chain,
|
|
148
147
|
endpointUrl: sources.hypersync,
|
|
149
148
|
apiToken: Env.envioApiToken,
|
|
150
|
-
onEventRegistrations: onEventRegistrations
|
|
149
|
+
onEventRegistrations: onEventRegistrations,
|
|
150
|
+
addressStore: addressStore
|
|
151
151
|
})];
|
|
152
152
|
break;
|
|
153
153
|
case "SvmSourceConfig" :
|
|
@@ -158,7 +158,8 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
|
|
|
158
158
|
endpointUrl: hypersync,
|
|
159
159
|
apiToken: Env.envioApiToken,
|
|
160
160
|
onEventRegistrations: onEventRegistrations,
|
|
161
|
-
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis
|
|
161
|
+
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
|
|
162
|
+
addressStore: addressStore
|
|
162
163
|
})] : (
|
|
163
164
|
rpc !== undefined ? [Svm.makeRPCSource(chain, rpc, undefined)] : Stdlib_JsError.throwWithMessage(`Chain ` + chain.toString() + ` has no SVM data source`)
|
|
164
165
|
);
|
|
@@ -169,7 +170,7 @@ function makeInternal(chainConfig, indexingAddresses, startBlock, endBlock, firs
|
|
|
169
170
|
}
|
|
170
171
|
let firstEventBlock$1 = fetchState.firstEventBlock;
|
|
171
172
|
let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
|
|
172
|
-
return make(chainConfig, fetchState, onEventRegistrations,
|
|
173
|
+
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources$1, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), ReorgDetection.make(chainReorgCheckpoints, maxReorgDepth, config.shouldRollbackOnReorg), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(BlockStore.make(config.ecosystem.name, !lowercaseAddresses)), config.reorgThresholdReadyTolerance, logger);
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
function makeFromConfig(chainConfig, config, registrationsByChainId, knownHeight) {
|
|
@@ -250,7 +251,7 @@ function knownHeight(cs) {
|
|
|
250
251
|
}
|
|
251
252
|
|
|
252
253
|
function contractAddresses(cs, contractName) {
|
|
253
|
-
return
|
|
254
|
+
return cs.addressStore.contractAddresses(contractName);
|
|
254
255
|
}
|
|
255
256
|
|
|
256
257
|
function bufferSize(cs) {
|
|
@@ -541,10 +542,6 @@ async function materializePageItems(items, transactionStore, blockStore, ecosyst
|
|
|
541
542
|
]);
|
|
542
543
|
}
|
|
543
544
|
|
|
544
|
-
function filterByClientAddress(cs, items) {
|
|
545
|
-
return FetchState.filterByClientAddress(items, cs.indexingAddresses);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
545
|
function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight, txPage, blockPage) {
|
|
549
546
|
if (txPage !== undefined) {
|
|
550
547
|
cs.transactionStore.merge(Primitive_option.valFromOption(txPage));
|
|
@@ -552,7 +549,7 @@ function handleQueryResult(cs, query, newItems, newItemsWithDcs, latestFetchedBl
|
|
|
552
549
|
if (blockPage !== undefined) {
|
|
553
550
|
cs.blockStore.merge(Primitive_option.valFromOption(blockPage));
|
|
554
551
|
}
|
|
555
|
-
let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.
|
|
552
|
+
let fs = newItemsWithDcs.length !== 0 ? FetchState.registerDynamicContracts(cs.fetchState, cs.addressStore, Primitive_int.max(knownHeight, latestFetchedBlock.blockNumber), newItemsWithDcs) : cs.fetchState;
|
|
556
553
|
cs.fetchState = FetchState.updateKnownHeight(FetchState.handleQueryResult(fs, query, latestFetchedBlock, newItems), knownHeight);
|
|
557
554
|
cs.pendingBudget = Primitive_float.max(0, cs.pendingBudget - query.itemsEst);
|
|
558
555
|
}
|
|
@@ -587,7 +584,6 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
|
|
|
587
584
|
startBlock: init.startBlock,
|
|
588
585
|
endBlock: blockNumber,
|
|
589
586
|
normalSelection: init.normalSelection,
|
|
590
|
-
contractConfigs: init.contractConfigs,
|
|
591
587
|
chainId: init.chainId,
|
|
592
588
|
latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
|
|
593
589
|
blockLag: init.blockLag,
|
|
@@ -606,7 +602,6 @@ function setEndBlockToFirstEvent(cs, blockNumber) {
|
|
|
606
602
|
startBlock: init$1.startBlock,
|
|
607
603
|
endBlock: blockNumber,
|
|
608
604
|
normalSelection: init$1.normalSelection,
|
|
609
|
-
contractConfigs: init$1.contractConfigs,
|
|
610
605
|
chainId: init$1.chainId,
|
|
611
606
|
latestOnBlockBlockNumber: init$1.latestOnBlockBlockNumber,
|
|
612
607
|
blockLag: init$1.blockLag,
|
|
@@ -645,7 +640,7 @@ function toMetrics(cs) {
|
|
|
645
640
|
numBatchesFetched: 0,
|
|
646
641
|
startBlock: cs.fetchState.startBlock,
|
|
647
642
|
endBlock: cs.fetchState.endBlock,
|
|
648
|
-
numAddresses:
|
|
643
|
+
numAddresses: cs.addressStore.size(),
|
|
649
644
|
isReady: cs.timestampCaughtUpToHeadOrEndblock !== undefined,
|
|
650
645
|
sourceBlockNumber: cs.fetchState.knownHeight,
|
|
651
646
|
progressBlockNumber: cs.committedProgressBlockNumber,
|
|
@@ -715,7 +710,6 @@ function applyBatchProgress(cs, batch, blockTimestampName) {
|
|
|
715
710
|
startBlock: init.startBlock,
|
|
716
711
|
endBlock: init.endBlock,
|
|
717
712
|
normalSelection: init.normalSelection,
|
|
718
|
-
contractConfigs: init.contractConfigs,
|
|
719
713
|
chainId: init.chainId,
|
|
720
714
|
latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
|
|
721
715
|
blockLag: init.blockLag,
|
|
@@ -774,7 +768,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
|
|
|
774
768
|
if (newProgressBlockNumber === undefined) {
|
|
775
769
|
if (isReorgChain) {
|
|
776
770
|
cs.reorgDetection = ReorgDetection.rollbackToValidBlockNumber(cs.reorgDetection, rollbackTargetBlockNumber);
|
|
777
|
-
cs.fetchState = FetchState.rollback(cs.fetchState, cs.
|
|
771
|
+
cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, rollbackTargetBlockNumber);
|
|
778
772
|
cs.transactionStore.rollback(rollbackTargetBlockNumber);
|
|
779
773
|
cs.blockStore.rollback(rollbackTargetBlockNumber);
|
|
780
774
|
cs.committedProgressBlockNumber = Primitive_int.min(cs.committedProgressBlockNumber, rollbackTargetBlockNumber);
|
|
@@ -792,7 +786,7 @@ function rollback(cs, newProgressBlockNumber, eventsProcessedDiff, rollbackTarge
|
|
|
792
786
|
if (safeCheckpointTracking !== undefined) {
|
|
793
787
|
cs.safeCheckpointTracking = SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber);
|
|
794
788
|
}
|
|
795
|
-
cs.fetchState = FetchState.rollback(cs.fetchState, cs.
|
|
789
|
+
cs.fetchState = FetchState.rollback(cs.fetchState, cs.addressStore, newProgressBlockNumber);
|
|
796
790
|
cs.transactionStore.rollback(newProgressBlockNumber);
|
|
797
791
|
cs.blockStore.rollback(newProgressBlockNumber);
|
|
798
792
|
cs.committedProgressBlockNumber = newProgressBlockNumber;
|
|
@@ -842,7 +836,6 @@ export {
|
|
|
842
836
|
isReady,
|
|
843
837
|
isFetchingAtHead,
|
|
844
838
|
isAtHeadWithoutEndBlock,
|
|
845
|
-
filterByClientAddress,
|
|
846
839
|
handleQueryResult,
|
|
847
840
|
materializeBatchItems,
|
|
848
841
|
materializePageItems,
|
package/src/ChainState.resi
CHANGED
|
@@ -9,7 +9,7 @@ let make: (
|
|
|
9
9
|
~chainConfig: Config.chain,
|
|
10
10
|
~fetchState: FetchState.t,
|
|
11
11
|
~onEventRegistrations: array<Internal.onEventRegistration>=?,
|
|
12
|
-
~
|
|
12
|
+
~addressStore: AddressStore.t,
|
|
13
13
|
~sourceManager: SourceManager.t,
|
|
14
14
|
~reorgDetection: ReorgDetection.t,
|
|
15
15
|
~committedProgressBlockNumber: int,
|
|
@@ -104,10 +104,6 @@ let isReady: t => bool
|
|
|
104
104
|
let isFetchingAtHead: t => bool
|
|
105
105
|
let isAtHeadWithoutEndBlock: t => bool
|
|
106
106
|
|
|
107
|
-
// Drop over-fetched events an address-param filter rejects, before contract
|
|
108
|
-
// registration runs on them. Uses the chain's current indexing addresses.
|
|
109
|
-
let filterByClientAddress: (t, array<Internal.item>) => array<Internal.item>
|
|
110
|
-
|
|
111
107
|
// State transitions. The chain state is mutated only through these.
|
|
112
108
|
let handleQueryResult: (
|
|
113
109
|
t,
|
package/src/Change.res
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
@tag("type")
|
|
2
2
|
type t<'entity> =
|
|
3
|
-
| @as("SET") Set({entityId:
|
|
4
|
-
| @as("DELETE") Delete({entityId:
|
|
3
|
+
| @as("SET") Set({entityId: EntityId.t, entity: 'entity, checkpointId: bigint})
|
|
4
|
+
| @as("DELETE") Delete({entityId: EntityId.t, checkpointId: bigint})
|
|
5
5
|
|
|
6
6
|
@get
|
|
7
|
-
external getEntityId: t<'entity> =>
|
|
7
|
+
external getEntityId: t<'entity> => EntityId.t = "entityId"
|
|
8
8
|
@get
|
|
9
9
|
external getCheckpointId: t<'entity> => bigint = "checkpointId"
|
package/src/Config.res
CHANGED
|
@@ -390,10 +390,6 @@ let getFieldTypeAndSchema = (prop, ~enumConfigsByName: dict<Table.enumConfig<Tab
|
|
|
390
390
|
let enumConfig = enumConfigsByName->Dict.get(enumName)->Option.getOrThrow
|
|
391
391
|
(Table.Enum({config: enumConfig}), enumConfig.schema->S.toUnknown)
|
|
392
392
|
}
|
|
393
|
-
| "entity" => {
|
|
394
|
-
let entityName = prop["entity"]->Option.getOrThrow
|
|
395
|
-
(Table.Entity({name: entityName}), S.string->S.toUnknown)
|
|
396
|
-
}
|
|
397
393
|
| other => JsError.throwWithMessage("Unknown field type in entity config: " ++ other)
|
|
398
394
|
}
|
|
399
395
|
|
package/src/Config.res.mjs
CHANGED
|
@@ -263,16 +263,6 @@ function getFieldTypeAndSchema(prop, enumConfigsByName) {
|
|
|
263
263
|
Utils.Schema.dbDate
|
|
264
264
|
];
|
|
265
265
|
break;
|
|
266
|
-
case "entity" :
|
|
267
|
-
let entityName = Stdlib_Option.getOrThrow(prop.entity, undefined);
|
|
268
|
-
match = [
|
|
269
|
-
{
|
|
270
|
-
type: "Entity",
|
|
271
|
-
name: entityName
|
|
272
|
-
},
|
|
273
|
-
S$RescriptSchema.string
|
|
274
|
-
];
|
|
275
|
-
break;
|
|
276
266
|
case "enum" :
|
|
277
267
|
let enumName = Stdlib_Option.getOrThrow(prop.enum, undefined);
|
|
278
268
|
let enumConfig = Stdlib_Option.getOrThrow(enumConfigsByName[enumName], undefined);
|
package/src/Core.res
CHANGED
|
@@ -10,6 +10,7 @@ type svmHyperSyncClientCtor
|
|
|
10
10
|
type fuelHyperSyncClientCtor
|
|
11
11
|
type transactionStoreCtor
|
|
12
12
|
type blockStoreCtor
|
|
13
|
+
type addressStoreCtor
|
|
13
14
|
type fromUserApiOptions = {
|
|
14
15
|
schema?: string,
|
|
15
16
|
env?: dict<string>,
|
|
@@ -39,6 +40,8 @@ type addon = {
|
|
|
39
40
|
transactionStore: transactionStoreCtor,
|
|
40
41
|
@as("BlockStore")
|
|
41
42
|
blockStore: blockStoreCtor,
|
|
43
|
+
@as("AddressStore")
|
|
44
|
+
addressStore: addressStoreCtor,
|
|
42
45
|
// Ordered transaction-field names exposed for the field-code contract test
|
|
43
46
|
// (the ReScript `transactionFields` arrays must match the Rust ordinals).
|
|
44
47
|
evmTransactionFieldNames: unit => array<string>,
|
package/src/EntityId.res
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Opaque representation of an entity id. The generated per-entity types expose
|
|
2
|
+
// the real scalar (string / int / bigint); the generic storage and in-memory
|
|
3
|
+
// layers work with any entity's id through this type without knowing which
|
|
4
|
+
// scalar backs it. The runtime value is always the real id, never a stringified
|
|
5
|
+
// form — `toKey` derives the string only where a JS object/dict key is needed.
|
|
6
|
+
type t
|
|
7
|
+
|
|
8
|
+
external unsafeOfAny: 'a => t = "%identity"
|
|
9
|
+
external unsafeToAny: t => 'a = "%identity"
|
|
10
|
+
external unsafeOfString: string => t = "%identity"
|
|
11
|
+
|
|
12
|
+
// Stringified id used as a JS object/dict key. `String` matches how JS coerces
|
|
13
|
+
// a value used as an object key, so an id indexes the same whether the raw
|
|
14
|
+
// value or its key form is used for lookup, across string/int/bigint.
|
|
15
|
+
let toKey: t => string = %raw(`String`)
|