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
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
4
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
4
|
|
|
6
5
|
function make(items, endBlock, chain) {
|
|
7
6
|
let reportedHeight = Primitive_int.max(endBlock, 1);
|
|
@@ -22,7 +21,7 @@ function make(items, endBlock, chain) {
|
|
|
22
21
|
height: reportedHeight,
|
|
23
22
|
requestStats: []
|
|
24
23
|
}),
|
|
25
|
-
getItemsOrThrow: (fromBlock, toBlock,
|
|
24
|
+
getItemsOrThrow: (fromBlock, toBlock, addressSet, param, param$1, selection, param$2, param$3, param$4) => {
|
|
26
25
|
let toBlockQueried = toBlock !== undefined ? toBlock : reportedHeight;
|
|
27
26
|
let selectionEventIds = new Set();
|
|
28
27
|
selection.onEventRegistrations.forEach(reg => {
|
|
@@ -37,11 +36,20 @@ function make(items, endBlock, chain) {
|
|
|
37
36
|
if (!selectionEventIds.has(onEventRegistration.eventConfig.id)) {
|
|
38
37
|
return false;
|
|
39
38
|
}
|
|
40
|
-
|
|
39
|
+
let contractName = onEventRegistration.eventConfig.contractName;
|
|
40
|
+
let emitterAllowed = onEventRegistration.isWildcard || addressSet.has(item.payload.srcAddress, contractName, blockNumber);
|
|
41
|
+
if (!emitterAllowed) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
let groups = onEventRegistration.addressFilterParamGroups;
|
|
45
|
+
if (groups === undefined) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (groups.length === 0) {
|
|
41
49
|
return true;
|
|
42
50
|
}
|
|
43
|
-
let
|
|
44
|
-
return
|
|
51
|
+
let params = item.payload.params;
|
|
52
|
+
return groups.some(group => group.every(name => addressSet.has(params[name], contractName, blockNumber)));
|
|
45
53
|
});
|
|
46
54
|
return Promise.resolve({
|
|
47
55
|
knownHeight: reportedHeight,
|
package/src/sources/Source.res
CHANGED
|
@@ -73,8 +73,10 @@ type t = {
|
|
|
73
73
|
getItemsOrThrow: (
|
|
74
74
|
~fromBlock: int,
|
|
75
75
|
~toBlock: option<int>,
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
// The partition's slice of the chain's address index. The source hands it
|
|
77
|
+
// straight to its Rust client, which builds the query's address filter from
|
|
78
|
+
// it and gates every returned item against the chain-wide store.
|
|
79
|
+
~addressSet: AddressSet.t,
|
|
78
80
|
~knownHeight: int,
|
|
79
81
|
~partitionId: string,
|
|
80
82
|
~selection: FetchState.selection,
|
|
@@ -754,7 +754,7 @@ let executeQuery = async (
|
|
|
754
754
|
"source": source.name,
|
|
755
755
|
"fromBlock": query.fromBlock,
|
|
756
756
|
"toBlock": toBlock,
|
|
757
|
-
"addresses": query.
|
|
757
|
+
"addresses": query.addresses->AddressSet.size,
|
|
758
758
|
"retry": retry,
|
|
759
759
|
},
|
|
760
760
|
)
|
|
@@ -763,8 +763,7 @@ let executeQuery = async (
|
|
|
763
763
|
let response = await source.getItemsOrThrow(
|
|
764
764
|
~fromBlock=query.fromBlock,
|
|
765
765
|
~toBlock,
|
|
766
|
-
~
|
|
767
|
-
~contractNameByAddress=query.addressesByContractName->FetchState.deriveContractNameByAddress,
|
|
766
|
+
~addressSet=query.addresses,
|
|
768
767
|
~partitionId=query.partitionId,
|
|
769
768
|
~knownHeight,
|
|
770
769
|
~selection=query.selection,
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import * as Utils from "../Utils.res.mjs";
|
|
4
4
|
import * as Source from "./Source.res.mjs";
|
|
5
5
|
import * as Logging from "../Logging.res.mjs";
|
|
6
|
-
import * as FetchState from "../FetchState.res.mjs";
|
|
7
6
|
import * as Performance from "../bindings/Performance.res.mjs";
|
|
8
7
|
import * as ErrorHandling from "../ErrorHandling.res.mjs";
|
|
9
8
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
@@ -597,11 +596,11 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
597
596
|
source: source.name,
|
|
598
597
|
fromBlock: query.fromBlock,
|
|
599
598
|
toBlock: toBlock,
|
|
600
|
-
addresses:
|
|
599
|
+
addresses: query.addresses.size(),
|
|
601
600
|
retry: retry
|
|
602
601
|
});
|
|
603
602
|
try {
|
|
604
|
-
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.
|
|
603
|
+
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addresses, knownHeight, query.partitionId, query.selection, query.itemsTarget, retry, logger$2);
|
|
605
604
|
recordRequestStats(sourceState, response.requestStats);
|
|
606
605
|
sourceState.lastFailedAt = undefined;
|
|
607
606
|
if (response.knownHeight > sourceState.knownHeight) {
|
package/src/sources/Svm.res
CHANGED
|
@@ -100,8 +100,7 @@ let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): S
|
|
|
100
100
|
getItemsOrThrow: (
|
|
101
101
|
~fromBlock as _,
|
|
102
102
|
~toBlock as _,
|
|
103
|
-
~
|
|
104
|
-
~contractNameByAddress as _,
|
|
103
|
+
~addressSet as _,
|
|
105
104
|
~knownHeight as _,
|
|
106
105
|
~partitionId as _,
|
|
107
106
|
~selection as _,
|
package/src/sources/Svm.res.mjs
CHANGED
|
@@ -91,7 +91,7 @@ function makeRPCSource(chain, rpc, sourceForOpt) {
|
|
|
91
91
|
}]
|
|
92
92
|
};
|
|
93
93
|
},
|
|
94
|
-
getItemsOrThrow: (param, param$1, param$2, param$3, param$4, param$5, param$6, param$7, param$8
|
|
94
|
+
getItemsOrThrow: (param, param$1, param$2, param$3, param$4, param$5, param$6, param$7, param$8) => Stdlib_JsError.throwWithMessage("Svm does not support getting items")
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -191,7 +191,10 @@ module EventItems = {
|
|
|
191
191
|
// Absent means no server-side cap on the number of instructions returned.
|
|
192
192
|
maxNumInstructions?: int,
|
|
193
193
|
registrationIndexes: array<int>,
|
|
194
|
-
|
|
194
|
+
// Program names to fetch address-free even though their registrations
|
|
195
|
+
// depend on addresses (client-side filtering). None/empty means every
|
|
196
|
+
// address-dependent program is filtered server-side.
|
|
197
|
+
clientFilteredContracts: option<array<string>>,
|
|
195
198
|
}
|
|
196
199
|
|
|
197
200
|
type log = {
|
|
@@ -221,8 +224,10 @@ module EventItems = {
|
|
|
221
224
|
|
|
222
225
|
type response = {
|
|
223
226
|
nextSlot: int,
|
|
224
|
-
// One lean header per slot
|
|
225
|
-
// the
|
|
227
|
+
// One lean header per returned slot, including slots no item references —
|
|
228
|
+
// reorg detection and the batch's latest timestamp read them all. The full
|
|
229
|
+
// blocks live in the block store returned alongside, which keeps only the
|
|
230
|
+
// slots items reference.
|
|
226
231
|
blocks: array<ResponseTypes.block>,
|
|
227
232
|
items: array<item>,
|
|
228
233
|
}
|
|
@@ -240,6 +245,7 @@ type t = {
|
|
|
240
245
|
// prep.
|
|
241
246
|
getEventItems: (
|
|
242
247
|
~query: EventItems.query,
|
|
248
|
+
~addressSet: AddressSet.t,
|
|
243
249
|
) => promise<(EventItems.response, TransactionStore.t, BlockStore.t)>,
|
|
244
250
|
}
|
|
245
251
|
|
|
@@ -249,6 +255,7 @@ external classFromConfig: (
|
|
|
249
255
|
cfg,
|
|
250
256
|
string,
|
|
251
257
|
array<Registration.input>,
|
|
258
|
+
AddressStore.t,
|
|
252
259
|
) => t = "fromConfig"
|
|
253
260
|
|
|
254
261
|
let make = (
|
|
@@ -259,6 +266,7 @@ let make = (
|
|
|
259
266
|
~retryBaseMs=?,
|
|
260
267
|
~retryCeilingMs=?,
|
|
261
268
|
~eventRegistrations=[],
|
|
269
|
+
~addressStore,
|
|
262
270
|
) => {
|
|
263
271
|
let envioVersion = Utils.EnvioPackage.value.version
|
|
264
272
|
Core.getAddon().svmHyperSyncClient->classFromConfig(
|
|
@@ -272,5 +280,6 @@ let make = (
|
|
|
272
280
|
},
|
|
273
281
|
`hyperindex/${envioVersion}`,
|
|
274
282
|
eventRegistrations,
|
|
283
|
+
addressStore,
|
|
275
284
|
)
|
|
276
285
|
}
|
|
@@ -45,7 +45,7 @@ let ResponseTypes = {};
|
|
|
45
45
|
|
|
46
46
|
let EventItems = {};
|
|
47
47
|
|
|
48
|
-
function make(url, apiToken, httpReqTimeoutMillis, maxNumRetries, retryBaseMs, retryCeilingMs, eventRegistrationsOpt) {
|
|
48
|
+
function make(url, apiToken, httpReqTimeoutMillis, maxNumRetries, retryBaseMs, retryCeilingMs, eventRegistrationsOpt, addressStore) {
|
|
49
49
|
let eventRegistrations = eventRegistrationsOpt !== undefined ? eventRegistrationsOpt : [];
|
|
50
50
|
let envioVersion = Utils.EnvioPackage.value.version;
|
|
51
51
|
return Core.getAddon().SvmHyperSyncClient.fromConfig({
|
|
@@ -55,7 +55,7 @@ function make(url, apiToken, httpReqTimeoutMillis, maxNumRetries, retryBaseMs, r
|
|
|
55
55
|
maxNumRetries: maxNumRetries,
|
|
56
56
|
retryBaseMs: retryBaseMs,
|
|
57
57
|
retryCeilingMs: retryCeilingMs
|
|
58
|
-
}, `hyperindex/` + envioVersion, eventRegistrations);
|
|
58
|
+
}, `hyperindex/` + envioVersion, eventRegistrations, addressStore);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export {
|
|
@@ -6,6 +6,8 @@ type options = {
|
|
|
6
6
|
apiToken: option<string>,
|
|
7
7
|
onEventRegistrations: array<Internal.svmOnEventRegistration>,
|
|
8
8
|
clientTimeoutMillis: int,
|
|
9
|
+
// The chain's address index; the client reads it while routing.
|
|
10
|
+
addressStore: AddressStore.t,
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
// Synthesize a stable logIndex for an SVM instruction so the FetchState
|
|
@@ -66,7 +68,7 @@ let toSvmInstruction = (
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
let make = (
|
|
69
|
-
{chain, endpointUrl, apiToken, onEventRegistrations, clientTimeoutMillis}: options,
|
|
71
|
+
{chain, endpointUrl, apiToken, onEventRegistrations, clientTimeoutMillis, addressStore}: options,
|
|
70
72
|
): t => {
|
|
71
73
|
let name = "SvmHyperSync"
|
|
72
74
|
|
|
@@ -80,13 +82,13 @@ let make = (
|
|
|
80
82
|
~eventRegistrations=SvmHyperSyncClient.Registration.fromOnEventRegistrations(
|
|
81
83
|
onEventRegistrations,
|
|
82
84
|
),
|
|
85
|
+
~addressStore,
|
|
83
86
|
)
|
|
84
87
|
|
|
85
88
|
let getItemsOrThrow = async (
|
|
86
89
|
~fromBlock,
|
|
87
90
|
~toBlock,
|
|
88
|
-
~
|
|
89
|
-
~contractNameByAddress as _,
|
|
91
|
+
~addressSet,
|
|
90
92
|
~knownHeight,
|
|
91
93
|
~partitionId as _,
|
|
92
94
|
~selection: FetchState.selection,
|
|
@@ -102,10 +104,13 @@ let make = (
|
|
|
102
104
|
toSlot: toBlock,
|
|
103
105
|
maxNumInstructions: ?itemsTarget,
|
|
104
106
|
registrationIndexes: selection.onEventRegistrations->Array.map(reg => reg.index),
|
|
105
|
-
|
|
107
|
+
clientFilteredContracts: selection.clientFilteredContracts,
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
let (resp, transactionStore, blockStore) = try await client.getEventItems(
|
|
110
|
+
let (resp, transactionStore, blockStore) = try await client.getEventItems(
|
|
111
|
+
~query,
|
|
112
|
+
~addressSet,
|
|
113
|
+
) catch {
|
|
109
114
|
| exn =>
|
|
110
115
|
throw(
|
|
111
116
|
Source.GetItemsError(
|
|
@@ -58,21 +58,22 @@ function toSvmInstruction(item, programName, instructionName) {
|
|
|
58
58
|
function make(param) {
|
|
59
59
|
let onEventRegistrations = param.onEventRegistrations;
|
|
60
60
|
let chain = param.chain;
|
|
61
|
-
let client = SvmHyperSyncClient.make(param.endpointUrl, param.apiToken, param.clientTimeoutMillis, undefined, undefined, undefined, SvmHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations));
|
|
62
|
-
let getItemsOrThrow = async (fromBlock, toBlock,
|
|
61
|
+
let client = SvmHyperSyncClient.make(param.endpointUrl, param.apiToken, param.clientTimeoutMillis, undefined, undefined, undefined, SvmHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), param.addressStore);
|
|
62
|
+
let getItemsOrThrow = async (fromBlock, toBlock, addressSet, knownHeight, param, selection, itemsTarget, retry, param$1) => {
|
|
63
63
|
let totalTimeRef = Performance.now();
|
|
64
64
|
let pageFetchRef = Performance.now();
|
|
65
65
|
let query_registrationIndexes = selection.onEventRegistrations.map(reg => reg.index);
|
|
66
|
+
let query_clientFilteredContracts = selection.clientFilteredContracts;
|
|
66
67
|
let query = {
|
|
67
68
|
fromSlot: fromBlock,
|
|
68
69
|
toSlot: toBlock,
|
|
69
70
|
maxNumInstructions: itemsTarget,
|
|
70
71
|
registrationIndexes: query_registrationIndexes,
|
|
71
|
-
|
|
72
|
+
clientFilteredContracts: query_clientFilteredContracts
|
|
72
73
|
};
|
|
73
74
|
let match;
|
|
74
75
|
try {
|
|
75
|
-
match = await client.getEventItems(query);
|
|
76
|
+
match = await client.getEventItems(query, addressSet);
|
|
76
77
|
} catch (raw_exn) {
|
|
77
78
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
78
79
|
throw {
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
type indexingAddress = Internal.indexingContract
|
|
2
|
-
|
|
3
|
-
type contractConfig = {startBlock: option<int>}
|
|
4
|
-
|
|
5
|
-
// Grouped by contract name, then by address string. Contracts are few, so
|
|
6
|
-
// per-contract operations (address count, the client-side filter's lookup
|
|
7
|
-
// dict) are direct, and whole-index scans (get by address, rollback) walk the
|
|
8
|
-
// small contract set. Address strings are globally unique across contracts
|
|
9
|
-
// (conflicting registrations are rejected before they reach here).
|
|
10
|
-
type t = dict<dict<indexingAddress>>
|
|
11
|
-
|
|
12
|
-
let deriveEffectiveStartBlock = (~registrationBlock: int, ~contractStartBlock: option<int>) => {
|
|
13
|
-
Pervasives.max(Pervasives.max(registrationBlock, 0), contractStartBlock->Option.getOr(0))
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let makeContractConfigs = (~onEventRegistrations: array<Internal.onEventRegistration>): dict<
|
|
17
|
-
contractConfig,
|
|
18
|
-
> => {
|
|
19
|
-
let contractConfigs: dict<contractConfig> = Dict.make()
|
|
20
|
-
onEventRegistrations->Array.forEach(reg => {
|
|
21
|
-
let contractName = reg.eventConfig.contractName
|
|
22
|
-
switch contractConfigs->Utils.Dict.dangerouslyGetNonOption(contractName) {
|
|
23
|
-
| Some({startBlock}) =>
|
|
24
|
-
contractConfigs->Dict.set(
|
|
25
|
-
contractName,
|
|
26
|
-
{
|
|
27
|
-
startBlock: switch (startBlock, reg.startBlock) {
|
|
28
|
-
| (Some(a), Some(b)) => Some(Pervasives.min(a, b))
|
|
29
|
-
| (Some(_) as s, None) | (None, Some(_) as s) => s
|
|
30
|
-
| (None, None) => None
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
)
|
|
34
|
-
| None =>
|
|
35
|
-
contractConfigs->Dict.set(
|
|
36
|
-
contractName,
|
|
37
|
-
{
|
|
38
|
-
startBlock: reg.startBlock,
|
|
39
|
-
},
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
contractConfigs
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let makeIndexingAddress = (
|
|
47
|
-
~contract: Internal.indexingAddress,
|
|
48
|
-
~contractConfigs: dict<contractConfig>,
|
|
49
|
-
): indexingAddress => {
|
|
50
|
-
let contractStartBlock = switch contractConfigs->Utils.Dict.dangerouslyGetNonOption(
|
|
51
|
-
contract.contractName,
|
|
52
|
-
) {
|
|
53
|
-
| Some({startBlock}) => startBlock
|
|
54
|
-
| None => None
|
|
55
|
-
}
|
|
56
|
-
{
|
|
57
|
-
address: contract.address,
|
|
58
|
-
contractName: contract.contractName,
|
|
59
|
-
registrationBlock: contract.registrationBlock,
|
|
60
|
-
effectiveStartBlock: deriveEffectiveStartBlock(
|
|
61
|
-
~registrationBlock=contract.registrationBlock,
|
|
62
|
-
~contractStartBlock,
|
|
63
|
-
),
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
let insert = (indexingAddresses: t, indexingAddress: indexingAddress) => {
|
|
68
|
-
indexingAddresses
|
|
69
|
-
->Utils.Dict.getOrInsertEmptyDict(indexingAddress.contractName)
|
|
70
|
-
->Dict.set(indexingAddress.address->Address.toString, indexingAddress)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
let make = (
|
|
74
|
-
~contractConfigs: dict<contractConfig>,
|
|
75
|
-
~addresses: array<Internal.indexingAddress>,
|
|
76
|
-
): t => {
|
|
77
|
-
let indexingAddresses = Dict.make()
|
|
78
|
-
addresses->Array.forEach(contract => {
|
|
79
|
-
indexingAddresses->insert(makeIndexingAddress(~contract, ~contractConfigs))
|
|
80
|
-
})
|
|
81
|
-
indexingAddresses
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Address strings are globally unique, so the first inner dict holding the
|
|
85
|
-
// address owns it. for..in returns on the first hit without allocating a values
|
|
86
|
-
// array per lookup (get runs once per registration).
|
|
87
|
-
let get: (t, string) => option<indexingAddress> = %raw(`(index, address) => {
|
|
88
|
-
for (var contractName in index) {
|
|
89
|
-
var entry = index[contractName][address];
|
|
90
|
-
if (entry !== undefined) {
|
|
91
|
-
return entry;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return undefined;
|
|
95
|
-
}`)
|
|
96
|
-
|
|
97
|
-
let size = (indexingAddresses: t) => {
|
|
98
|
-
let total = ref(0)
|
|
99
|
-
indexingAddresses->Utils.Dict.forEach(inner => {
|
|
100
|
-
total := total.contents + inner->Utils.Dict.size
|
|
101
|
-
})
|
|
102
|
-
total.contents
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Number of registered addresses for a single contract — a for..in over that
|
|
106
|
-
// one contract's addresses. The trigger deciding when to switch a contract to
|
|
107
|
-
// client-side filtering reads this.
|
|
108
|
-
let contractCount = (indexingAddresses: t, ~contractName) =>
|
|
109
|
-
switch indexingAddresses->Utils.Dict.dangerouslyGetNonOption(contractName) {
|
|
110
|
-
| Some(inner) => inner->Utils.Dict.size
|
|
111
|
-
| None => 0
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
let getContractAddresses = (indexingAddresses: t, ~contractName): array<Address.t> => {
|
|
115
|
-
switch indexingAddresses->Utils.Dict.dangerouslyGetNonOption(contractName) {
|
|
116
|
-
| Some(inner) => inner->Dict.valuesToArray->Array.map(ia => ia.address)
|
|
117
|
-
| None => []
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let emptyContractDict: dict<indexingAddress> = Dict.make()
|
|
122
|
-
|
|
123
|
-
// The address→entry dict for a single contract, passed to that contract's
|
|
124
|
-
// precompiled `clientAddressFilter` (which does raw `byAddr[srcAddress]` access
|
|
125
|
-
// in generated JS). Every leaf of a filter references the event's own contract
|
|
126
|
-
// — `chain.<Contract>.addresses` only exposes the event's contract — so one
|
|
127
|
-
// inner dict covers the srcAddress and param-address checks alike. Returns a
|
|
128
|
-
// shared empty dict when the contract has no registered addresses.
|
|
129
|
-
let forContract = (indexingAddresses: t, ~contractName): dict<indexingAddress> =>
|
|
130
|
-
switch indexingAddresses->Utils.Dict.dangerouslyGetNonOption(contractName) {
|
|
131
|
-
| Some(inner) => inner
|
|
132
|
-
| None => emptyContractDict
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
let register = (indexingAddresses: t, additions: dict<indexingAddress>) => {
|
|
136
|
-
additions->Utils.Dict.forEach(indexingAddress => {
|
|
137
|
-
indexingAddresses->insert(indexingAddress)
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
let rollbackInPlace = (indexingAddresses: t, ~targetBlockNumber: int): unit => {
|
|
142
|
-
// forEachWithKey is a `for..in`, so deleting the key currently being visited is
|
|
143
|
-
// safe — it doesn't affect enumeration of the remaining keys.
|
|
144
|
-
indexingAddresses->Utils.Dict.forEach(inner => {
|
|
145
|
-
inner->Utils.Dict.forEachWithKey((indexingContract, address) => {
|
|
146
|
-
if indexingContract.registrationBlock > targetBlockNumber {
|
|
147
|
-
inner->Utils.Dict.deleteInPlace(address)
|
|
148
|
-
}
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Utils from "./Utils.res.mjs";
|
|
4
|
-
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
5
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
-
|
|
7
|
-
function deriveEffectiveStartBlock(registrationBlock, contractStartBlock) {
|
|
8
|
-
return Primitive_int.max(Primitive_int.max(registrationBlock, 0), Stdlib_Option.getOr(contractStartBlock, 0));
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function makeContractConfigs(onEventRegistrations) {
|
|
12
|
-
let contractConfigs = {};
|
|
13
|
-
onEventRegistrations.forEach(reg => {
|
|
14
|
-
let contractName = reg.eventConfig.contractName;
|
|
15
|
-
let match = contractConfigs[contractName];
|
|
16
|
-
if (match !== undefined) {
|
|
17
|
-
let startBlock = match.startBlock;
|
|
18
|
-
let match$1 = reg.startBlock;
|
|
19
|
-
contractConfigs[contractName] = {
|
|
20
|
-
startBlock: startBlock !== undefined ? (
|
|
21
|
-
match$1 !== undefined ? Primitive_int.min(startBlock, match$1) : startBlock
|
|
22
|
-
) : (
|
|
23
|
-
match$1 !== undefined ? match$1 : undefined
|
|
24
|
-
)
|
|
25
|
-
};
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
contractConfigs[contractName] = {
|
|
29
|
-
startBlock: reg.startBlock
|
|
30
|
-
};
|
|
31
|
-
});
|
|
32
|
-
return contractConfigs;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function makeIndexingAddress(contract, contractConfigs) {
|
|
36
|
-
let match = contractConfigs[contract.contractName];
|
|
37
|
-
let contractStartBlock = match !== undefined ? match.startBlock : undefined;
|
|
38
|
-
return {
|
|
39
|
-
address: contract.address,
|
|
40
|
-
contractName: contract.contractName,
|
|
41
|
-
registrationBlock: contract.registrationBlock,
|
|
42
|
-
effectiveStartBlock: deriveEffectiveStartBlock(contract.registrationBlock, contractStartBlock)
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function insert(indexingAddresses, indexingAddress) {
|
|
47
|
-
Utils.Dict.getOrInsertEmptyDict(indexingAddresses, indexingAddress.contractName)[indexingAddress.address] = indexingAddress;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function make(contractConfigs, addresses) {
|
|
51
|
-
let indexingAddresses = {};
|
|
52
|
-
addresses.forEach(contract => insert(indexingAddresses, makeIndexingAddress(contract, contractConfigs)));
|
|
53
|
-
return indexingAddresses;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let get = ((index, address) => {
|
|
57
|
-
for (var contractName in index) {
|
|
58
|
-
var entry = index[contractName][address];
|
|
59
|
-
if (entry !== undefined) {
|
|
60
|
-
return entry;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return undefined;
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
function size(indexingAddresses) {
|
|
67
|
-
let total = {
|
|
68
|
-
contents: 0
|
|
69
|
-
};
|
|
70
|
-
Utils.Dict.forEach(indexingAddresses, inner => {
|
|
71
|
-
total.contents = total.contents + Utils.Dict.size(inner) | 0;
|
|
72
|
-
});
|
|
73
|
-
return total.contents;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function contractCount(indexingAddresses, contractName) {
|
|
77
|
-
let inner = indexingAddresses[contractName];
|
|
78
|
-
if (inner !== undefined) {
|
|
79
|
-
return Utils.Dict.size(inner);
|
|
80
|
-
} else {
|
|
81
|
-
return 0;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function getContractAddresses(indexingAddresses, contractName) {
|
|
86
|
-
let inner = indexingAddresses[contractName];
|
|
87
|
-
if (inner !== undefined) {
|
|
88
|
-
return Object.values(inner).map(ia => ia.address);
|
|
89
|
-
} else {
|
|
90
|
-
return [];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
let emptyContractDict = {};
|
|
95
|
-
|
|
96
|
-
function forContract(indexingAddresses, contractName) {
|
|
97
|
-
let inner = indexingAddresses[contractName];
|
|
98
|
-
if (inner !== undefined) {
|
|
99
|
-
return inner;
|
|
100
|
-
} else {
|
|
101
|
-
return emptyContractDict;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function register(indexingAddresses, additions) {
|
|
106
|
-
Utils.Dict.forEach(additions, indexingAddress => insert(indexingAddresses, indexingAddress));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function rollbackInPlace(indexingAddresses, targetBlockNumber) {
|
|
110
|
-
Utils.Dict.forEach(indexingAddresses, inner => Utils.Dict.forEachWithKey(inner, (indexingContract, address) => {
|
|
111
|
-
if (indexingContract.registrationBlock > targetBlockNumber) {
|
|
112
|
-
return Utils.Dict.deleteInPlace(inner, address);
|
|
113
|
-
}
|
|
114
|
-
}));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export {
|
|
118
|
-
deriveEffectiveStartBlock,
|
|
119
|
-
makeContractConfigs,
|
|
120
|
-
makeIndexingAddress,
|
|
121
|
-
make,
|
|
122
|
-
get,
|
|
123
|
-
size,
|
|
124
|
-
contractCount,
|
|
125
|
-
getContractAddresses,
|
|
126
|
-
forContract,
|
|
127
|
-
register,
|
|
128
|
-
rollbackInPlace,
|
|
129
|
-
}
|
|
130
|
-
/* Utils Not a pure module */
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
type indexingAddress = Internal.indexingContract
|
|
2
|
-
|
|
3
|
-
type contractConfig = {startBlock: option<int>}
|
|
4
|
-
|
|
5
|
-
type t
|
|
6
|
-
|
|
7
|
-
let deriveEffectiveStartBlock: (~registrationBlock: int, ~contractStartBlock: option<int>) => int
|
|
8
|
-
|
|
9
|
-
let makeContractConfigs: (
|
|
10
|
-
~onEventRegistrations: array<Internal.onEventRegistration>,
|
|
11
|
-
) => dict<contractConfig>
|
|
12
|
-
|
|
13
|
-
let makeIndexingAddress: (
|
|
14
|
-
~contract: Internal.indexingAddress,
|
|
15
|
-
~contractConfigs: dict<contractConfig>,
|
|
16
|
-
) => indexingAddress
|
|
17
|
-
|
|
18
|
-
let make: (~contractConfigs: dict<contractConfig>, ~addresses: array<Internal.indexingAddress>) => t
|
|
19
|
-
|
|
20
|
-
let get: (t, string) => option<indexingAddress>
|
|
21
|
-
|
|
22
|
-
let size: t => int
|
|
23
|
-
|
|
24
|
-
// Number of registered addresses for a single contract (scans just that
|
|
25
|
-
// contract's addresses).
|
|
26
|
-
let contractCount: (t, ~contractName: string) => int
|
|
27
|
-
|
|
28
|
-
let getContractAddresses: (t, ~contractName: string) => array<Address.t>
|
|
29
|
-
|
|
30
|
-
// The address→entry dict for a single contract, passed to that contract's
|
|
31
|
-
// precompiled `clientAddressFilter` (raw `byAddr[srcAddress]` access in
|
|
32
|
-
// generated JS). Don't use elsewhere — prefer the domain accessors so the
|
|
33
|
-
// opaque type stays enforced.
|
|
34
|
-
let forContract: (t, ~contractName: string) => dict<indexingAddress>
|
|
35
|
-
|
|
36
|
-
let register: (t, dict<indexingAddress>) => unit
|
|
37
|
-
|
|
38
|
-
// Deletes addresses registered after `targetBlockNumber` from the index in place.
|
|
39
|
-
let rollbackInPlace: (t, ~targetBlockNumber: int) => unit
|