envio 3.3.0-alpha.6 → 3.3.0-alpha.8
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 +23 -5
- package/package.json +6 -6
- package/src/Address.res +5 -2
- package/src/Address.res.mjs +3 -1
- package/src/Api.res +1 -1
- package/src/Api.res.mjs +1 -1
- package/src/ChainFetching.res +34 -34
- package/src/ChainFetching.res.mjs +29 -29
- package/src/ChainState.res +328 -165
- package/src/ChainState.res.mjs +205 -100
- package/src/ChainState.resi +16 -7
- package/src/Config.res +55 -27
- package/src/Config.res.mjs +46 -7
- package/src/ContractRegisterContext.res +2 -12
- package/src/ContractRegisterContext.res.mjs +3 -5
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +54 -79
- package/src/CrossChainState.res.mjs +45 -60
- package/src/CrossChainState.resi +1 -1
- package/src/Ecosystem.res +3 -3
- package/src/Ecosystem.res.mjs +3 -3
- package/src/Envio.res +14 -9
- package/src/EventConfigBuilder.res +105 -56
- package/src/EventConfigBuilder.res.mjs +69 -33
- package/src/EventProcessing.res +19 -15
- package/src/EventProcessing.res.mjs +13 -12
- package/src/FetchState.res +383 -182
- package/src/FetchState.res.mjs +260 -264
- package/src/HandlerLoader.res +7 -112
- package/src/HandlerLoader.res.mjs +1 -87
- package/src/HandlerRegister.res +209 -6
- package/src/HandlerRegister.res.mjs +90 -5
- package/src/HandlerRegister.resi +13 -2
- package/src/IndexerState.res +6 -3
- package/src/IndexerState.res.mjs +3 -3
- package/src/IndexerState.resi +1 -1
- package/src/IndexingAddresses.res +10 -7
- package/src/IndexingAddresses.res.mjs +8 -7
- package/src/IndexingAddresses.resi +3 -1
- package/src/Internal.res +82 -36
- package/src/Internal.res.mjs +11 -1
- package/src/Main.res +22 -23
- package/src/Main.res.mjs +15 -17
- package/src/Metrics.res +43 -2
- package/src/Metrics.res.mjs +39 -3
- package/src/Prometheus.res +0 -35
- package/src/Prometheus.res.mjs +33 -68
- package/src/RawEvent.res +7 -2
- package/src/RawEvent.res.mjs +4 -4
- package/src/SimulateItems.res +26 -8
- package/src/SimulateItems.res.mjs +11 -14
- package/src/TestIndexer.res +1 -1
- package/src/TestIndexer.res.mjs +1 -1
- package/src/sources/BlockStore.res +46 -0
- package/src/sources/BlockStore.res.mjs +24 -0
- package/src/sources/EventRouter.res +19 -12
- package/src/sources/EventRouter.res.mjs +7 -5
- package/src/sources/Evm.res +54 -4
- package/src/sources/Evm.res.mjs +43 -4
- package/src/sources/EvmChain.res +14 -18
- package/src/sources/EvmChain.res.mjs +12 -13
- package/src/sources/FieldMask.res +39 -0
- package/src/sources/FieldMask.res.mjs +42 -0
- package/src/sources/Fuel.res +5 -2
- package/src/sources/Fuel.res.mjs +4 -3
- package/src/sources/HyperFuelSource.res +32 -40
- package/src/sources/HyperFuelSource.res.mjs +53 -45
- package/src/sources/HyperSync.res +35 -9
- package/src/sources/HyperSync.res.mjs +52 -35
- package/src/sources/HyperSync.resi +11 -3
- package/src/sources/HyperSyncClient.res +15 -4
- package/src/sources/HyperSyncHeightStream.res +1 -8
- package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
- package/src/sources/HyperSyncSource.res +39 -58
- package/src/sources/HyperSyncSource.res.mjs +44 -44
- package/src/sources/RpcSource.res +114 -92
- package/src/sources/RpcSource.res.mjs +77 -46
- package/src/sources/RpcWebSocketHeightStream.res +0 -5
- package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
- package/src/sources/SimulateSource.res +6 -3
- package/src/sources/SimulateSource.res.mjs +13 -5
- package/src/sources/Source.res +25 -5
- package/src/sources/SourceManager.res +66 -3
- package/src/sources/SourceManager.res.mjs +55 -7
- package/src/sources/SourceManager.resi +10 -0
- package/src/sources/Svm.res +15 -10
- package/src/sources/Svm.res.mjs +24 -7
- package/src/sources/SvmHyperSyncClient.res +5 -6
- package/src/sources/SvmHyperSyncSource.res +84 -41
- package/src/sources/SvmHyperSyncSource.res.mjs +90 -43
- package/src/sources/TransactionStore.res +6 -107
- package/src/sources/TransactionStore.res.mjs +5 -86
- package/svm.schema.json +19 -0
package/src/HandlerLoader.res
CHANGED
|
@@ -72,116 +72,12 @@ let autoLoadFromSrcHandlers = async (~handlers: string) => {
|
|
|
72
72
|
->Promise.all
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
// `
|
|
79
|
-
//
|
|
80
|
-
let
|
|
81
|
-
let newChainMap = config.chainMap->ChainMap.map(chain => {
|
|
82
|
-
let newContracts = chain.contracts->Array.map(contract => {
|
|
83
|
-
let newEvents = contract.events->Array.map(
|
|
84
|
-
ev => {
|
|
85
|
-
let isWildcard = HandlerRegister.isWildcard(
|
|
86
|
-
~contractName=ev.contractName,
|
|
87
|
-
~eventName=ev.name,
|
|
88
|
-
)
|
|
89
|
-
let handler = HandlerRegister.getHandler(
|
|
90
|
-
~contractName=ev.contractName,
|
|
91
|
-
~eventName=ev.name,
|
|
92
|
-
)
|
|
93
|
-
let contractRegister = HandlerRegister.getContractRegister(
|
|
94
|
-
~contractName=ev.contractName,
|
|
95
|
-
~eventName=ev.name,
|
|
96
|
-
)
|
|
97
|
-
switch config.ecosystem.name {
|
|
98
|
-
| Fuel =>
|
|
99
|
-
let fuelEv = ev->(Utils.magic: Internal.eventConfig => Internal.fuelEventConfig)
|
|
100
|
-
|
|
101
|
-
({
|
|
102
|
-
...fuelEv,
|
|
103
|
-
isWildcard,
|
|
104
|
-
handler,
|
|
105
|
-
contractRegister,
|
|
106
|
-
clientAddressFilter: ?EventConfigBuilder.buildAddressFilter([], ~isWildcard),
|
|
107
|
-
dependsOnAddresses: Internal.dependsOnAddresses(
|
|
108
|
-
~isWildcard,
|
|
109
|
-
~filterByAddresses=false,
|
|
110
|
-
),
|
|
111
|
-
} :> Internal.eventConfig)
|
|
112
|
-
| Evm =>
|
|
113
|
-
let evmEv = ev->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig)
|
|
114
|
-
let eventFilters = HandlerRegister.getOnEventWhere(
|
|
115
|
-
~contractName=ev.contractName,
|
|
116
|
-
~eventName=ev.name,
|
|
117
|
-
)
|
|
118
|
-
let indexedParams = evmEv.paramsMetadata->Array.filter(p => p.indexed)
|
|
119
|
-
let {
|
|
120
|
-
getEventFiltersOrThrow,
|
|
121
|
-
filterByAddresses,
|
|
122
|
-
addressFilterParamGroups,
|
|
123
|
-
} = LogSelection.parseEventFiltersOrThrow(
|
|
124
|
-
~eventFilters,
|
|
125
|
-
~sighash=evmEv.sighash,
|
|
126
|
-
~params=indexedParams->Array.map(p => p.name),
|
|
127
|
-
~contractName=ev.contractName,
|
|
128
|
-
~probeChainId=chain.id,
|
|
129
|
-
~onEventBlockFilterSchema=config.ecosystem.onEventBlockFilterSchema,
|
|
130
|
-
~topic1=?indexedParams
|
|
131
|
-
->Array.get(0)
|
|
132
|
-
->Option.map(EventConfigBuilder.buildTopicGetter),
|
|
133
|
-
~topic2=?indexedParams
|
|
134
|
-
->Array.get(1)
|
|
135
|
-
->Option.map(EventConfigBuilder.buildTopicGetter),
|
|
136
|
-
~topic3=?indexedParams
|
|
137
|
-
->Array.get(2)
|
|
138
|
-
->Option.map(EventConfigBuilder.buildTopicGetter),
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
({
|
|
142
|
-
...evmEv,
|
|
143
|
-
isWildcard,
|
|
144
|
-
handler,
|
|
145
|
-
contractRegister,
|
|
146
|
-
getEventFiltersOrThrow,
|
|
147
|
-
filterByAddresses,
|
|
148
|
-
clientAddressFilter: ?EventConfigBuilder.buildAddressFilter(
|
|
149
|
-
addressFilterParamGroups,
|
|
150
|
-
~isWildcard,
|
|
151
|
-
),
|
|
152
|
-
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses),
|
|
153
|
-
} :> Internal.eventConfig)
|
|
154
|
-
| Svm =>
|
|
155
|
-
let svmEv =
|
|
156
|
-
ev->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
|
|
157
|
-
({
|
|
158
|
-
...svmEv,
|
|
159
|
-
isWildcard,
|
|
160
|
-
handler,
|
|
161
|
-
contractRegister,
|
|
162
|
-
clientAddressFilter: ?EventConfigBuilder.buildAddressFilter(
|
|
163
|
-
[],
|
|
164
|
-
~isWildcard,
|
|
165
|
-
~srcAddressExpr="event.programId",
|
|
166
|
-
),
|
|
167
|
-
dependsOnAddresses: Internal.dependsOnAddresses(
|
|
168
|
-
~isWildcard,
|
|
169
|
-
~filterByAddresses=false,
|
|
170
|
-
),
|
|
171
|
-
} :> Internal.eventConfig)
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
)
|
|
175
|
-
{...contract, events: newEvents}
|
|
176
|
-
})
|
|
177
|
-
{...chain, contracts: newContracts}
|
|
178
|
-
})
|
|
179
|
-
{...config, chainMap: newChainMap}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// `Config` never reads `HandlerRegister`. The only way to get a config that
|
|
183
|
-
// reflects registration state is through the returned value here.
|
|
184
|
-
let registerAllHandlers = async (~config: Config.t) => {
|
|
75
|
+
// `Config` holds only event definitions; handler + per-chain `where`
|
|
76
|
+
// registration state is layered on separately as `onEventRegistration`s by
|
|
77
|
+
// `HandlerRegister.finishRegistration`. This loads the user handler files
|
|
78
|
+
// (populating the global `HandlerRegister` registry as a side effect) and
|
|
79
|
+
// returns the resulting per-chain registrations.
|
|
80
|
+
let registerAllHandlers = async (~config: Config.t): HandlerRegister.registrationsByChainId => {
|
|
185
81
|
HandlerRegister.startRegistration(~ecosystem=config.ecosystem)
|
|
186
82
|
|
|
187
83
|
// Auto-load all .js files from src/handlers directory
|
|
@@ -194,6 +90,5 @@ let registerAllHandlers = async (~config: Config.t) => {
|
|
|
194
90
|
})
|
|
195
91
|
->Promise.all
|
|
196
92
|
|
|
197
|
-
|
|
198
|
-
(applyRegistrations(~config), registrations)
|
|
93
|
+
HandlerRegister.finishRegistration(~config)
|
|
199
94
|
}
|
|
@@ -5,16 +5,11 @@ import * as Path from "path";
|
|
|
5
5
|
import * as Utils from "./Utils.res.mjs";
|
|
6
6
|
import * as Logging from "./Logging.res.mjs";
|
|
7
7
|
import * as Process from "process";
|
|
8
|
-
import * as ChainMap from "./ChainMap.res.mjs";
|
|
9
|
-
import * as Internal from "./Internal.res.mjs";
|
|
10
8
|
import * as Nodemodule from "node:module";
|
|
11
|
-
import * as LogSelection from "./LogSelection.res.mjs";
|
|
12
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
13
9
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
14
10
|
import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
15
11
|
import * as HandlerRegister from "./HandlerRegister.res.mjs";
|
|
16
12
|
import * as Promises from "node:fs/promises";
|
|
17
|
-
import * as EventConfigBuilder from "./EventConfigBuilder.res.mjs";
|
|
18
13
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
19
14
|
|
|
20
15
|
try {
|
|
@@ -61,98 +56,17 @@ async function autoLoadFromSrcHandlers(handlers) {
|
|
|
61
56
|
})));
|
|
62
57
|
}
|
|
63
58
|
|
|
64
|
-
function applyRegistrations(config) {
|
|
65
|
-
let newChainMap = ChainMap.map(config.chainMap, chain => {
|
|
66
|
-
let newContracts = chain.contracts.map(contract => {
|
|
67
|
-
let newEvents = contract.events.map(ev => {
|
|
68
|
-
let isWildcard = HandlerRegister.isWildcard(ev.contractName, ev.name);
|
|
69
|
-
let handler = HandlerRegister.getHandler(ev.contractName, ev.name);
|
|
70
|
-
let contractRegister = HandlerRegister.getContractRegister(ev.contractName, ev.name);
|
|
71
|
-
let match = config.ecosystem.name;
|
|
72
|
-
switch (match) {
|
|
73
|
-
case "evm" :
|
|
74
|
-
let eventFilters = HandlerRegister.getOnEventWhere(ev.contractName, ev.name);
|
|
75
|
-
let indexedParams = ev.paramsMetadata.filter(p => p.indexed);
|
|
76
|
-
let match$1 = LogSelection.parseEventFiltersOrThrow(eventFilters, ev.sighash, indexedParams.map(p => p.name), ev.contractName, chain.id, config.ecosystem.onEventBlockFilterSchema, Stdlib_Option.map(indexedParams[0], EventConfigBuilder.buildTopicGetter), Stdlib_Option.map(indexedParams[1], EventConfigBuilder.buildTopicGetter), Stdlib_Option.map(indexedParams[2], EventConfigBuilder.buildTopicGetter));
|
|
77
|
-
let filterByAddresses = match$1.filterByAddresses;
|
|
78
|
-
let newrecord = {...ev};
|
|
79
|
-
newrecord.getEventFiltersOrThrow = match$1.getEventFiltersOrThrow;
|
|
80
|
-
newrecord.contractRegister = contractRegister;
|
|
81
|
-
newrecord.handler = handler;
|
|
82
|
-
newrecord.clientAddressFilter = EventConfigBuilder.buildAddressFilter(match$1.addressFilterParamGroups, isWildcard, undefined);
|
|
83
|
-
newrecord.dependsOnAddresses = Internal.dependsOnAddresses(isWildcard, filterByAddresses);
|
|
84
|
-
newrecord.filterByAddresses = filterByAddresses;
|
|
85
|
-
newrecord.isWildcard = isWildcard;
|
|
86
|
-
return newrecord;
|
|
87
|
-
case "fuel" :
|
|
88
|
-
let newrecord$1 = {...ev};
|
|
89
|
-
newrecord$1.contractRegister = contractRegister;
|
|
90
|
-
newrecord$1.handler = handler;
|
|
91
|
-
newrecord$1.clientAddressFilter = EventConfigBuilder.buildAddressFilter([], isWildcard, undefined);
|
|
92
|
-
newrecord$1.dependsOnAddresses = Internal.dependsOnAddresses(isWildcard, false);
|
|
93
|
-
newrecord$1.isWildcard = isWildcard;
|
|
94
|
-
return newrecord$1;
|
|
95
|
-
case "svm" :
|
|
96
|
-
let newrecord$2 = {...ev};
|
|
97
|
-
newrecord$2.contractRegister = contractRegister;
|
|
98
|
-
newrecord$2.handler = handler;
|
|
99
|
-
newrecord$2.clientAddressFilter = EventConfigBuilder.buildAddressFilter([], isWildcard, "event.programId");
|
|
100
|
-
newrecord$2.dependsOnAddresses = Internal.dependsOnAddresses(isWildcard, false);
|
|
101
|
-
newrecord$2.isWildcard = isWildcard;
|
|
102
|
-
return newrecord$2;
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
return {
|
|
106
|
-
name: contract.name,
|
|
107
|
-
abi: contract.abi,
|
|
108
|
-
addresses: contract.addresses,
|
|
109
|
-
events: newEvents,
|
|
110
|
-
startBlock: contract.startBlock
|
|
111
|
-
};
|
|
112
|
-
});
|
|
113
|
-
let newrecord = {...chain};
|
|
114
|
-
newrecord.contracts = newContracts;
|
|
115
|
-
return newrecord;
|
|
116
|
-
});
|
|
117
|
-
return {
|
|
118
|
-
name: config.name,
|
|
119
|
-
description: config.description,
|
|
120
|
-
handlers: config.handlers,
|
|
121
|
-
contractHandlers: config.contractHandlers,
|
|
122
|
-
shouldRollbackOnReorg: config.shouldRollbackOnReorg,
|
|
123
|
-
shouldSaveFullHistory: config.shouldSaveFullHistory,
|
|
124
|
-
storage: config.storage,
|
|
125
|
-
chainMap: newChainMap,
|
|
126
|
-
defaultChain: config.defaultChain,
|
|
127
|
-
ecosystem: config.ecosystem,
|
|
128
|
-
enableRawEvents: config.enableRawEvents,
|
|
129
|
-
maxAddrInPartition: config.maxAddrInPartition,
|
|
130
|
-
batchSize: config.batchSize,
|
|
131
|
-
lowercaseAddresses: config.lowercaseAddresses,
|
|
132
|
-
isDev: config.isDev,
|
|
133
|
-
userEntitiesByName: config.userEntitiesByName,
|
|
134
|
-
userEntities: config.userEntities,
|
|
135
|
-
allEntities: config.allEntities,
|
|
136
|
-
allEnums: config.allEnums
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
59
|
async function registerAllHandlers(config) {
|
|
141
60
|
HandlerRegister.startRegistration(config.ecosystem);
|
|
142
61
|
await autoLoadFromSrcHandlers(config.handlers);
|
|
143
62
|
await Promise.all(config.contractHandlers.map(param => registerContractHandlers(param.name, param.handler)));
|
|
144
|
-
|
|
145
|
-
return [
|
|
146
|
-
applyRegistrations(config),
|
|
147
|
-
registrations
|
|
148
|
-
];
|
|
63
|
+
return HandlerRegister.finishRegistration(config);
|
|
149
64
|
}
|
|
150
65
|
|
|
151
66
|
export {
|
|
152
67
|
toImportUrl,
|
|
153
68
|
registerContractHandlers,
|
|
154
69
|
autoLoadFromSrcHandlers,
|
|
155
|
-
applyRegistrations,
|
|
156
70
|
registerAllHandlers,
|
|
157
71
|
}
|
|
158
72
|
/* Not a pure module */
|
package/src/HandlerRegister.res
CHANGED
|
@@ -10,18 +10,30 @@ let empty = {
|
|
|
10
10
|
eventOptions: None,
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Per-chain onEventRegistrations built from the event definitions in
|
|
14
|
+
// `Config.t` plus whatever handler/contractRegister/eventOptions got
|
|
15
|
+
// registered for them, and the onBlock registrations collected during
|
|
16
|
+
// registration.
|
|
17
|
+
type chainRegistrations = {
|
|
18
|
+
onEventRegistrations: array<Internal.onEventRegistration>,
|
|
19
|
+
onBlockRegistrations: array<Internal.onBlockRegistration>,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// The finished registration state returned by `finishRegistration`.
|
|
23
|
+
type registrationsByChainId = dict<chainRegistrations>
|
|
24
|
+
|
|
25
|
+
type pendingRegistrations = {onBlockByChainId: dict<array<Internal.onBlockRegistration>>}
|
|
14
26
|
|
|
15
27
|
type activeRegistration = {
|
|
16
28
|
ecosystem: Ecosystem.t,
|
|
17
|
-
registrations:
|
|
29
|
+
registrations: pendingRegistrations,
|
|
18
30
|
mutable finished: bool,
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
// Stashed on `globalThis` so a duplicate envio module instance — e.g. when the
|
|
22
34
|
// CLI's `bin.mjs` resolves envio from one path but the user's handlers resolve
|
|
23
35
|
// it from `node_modules/envio` — shares one registry. Without this, each copy
|
|
24
|
-
// keeps its own dict and `
|
|
36
|
+
// keeps its own dict and `buildOnEventRegistrations` reads empty state.
|
|
25
37
|
//
|
|
26
38
|
// Version-gated: the record shapes below can evolve between envio versions,
|
|
27
39
|
// so the guard uses strict full-version equality. On mismatch we throw with
|
|
@@ -119,11 +131,202 @@ let startRegistration = (~ecosystem) => {
|
|
|
119
131
|
}
|
|
120
132
|
}
|
|
121
133
|
|
|
122
|
-
|
|
134
|
+
// Enrich one event definition into its (event, chain) registration using
|
|
135
|
+
// whatever handler/contractRegister/where the user registered for it. Shared
|
|
136
|
+
// by chain startup (`buildOnEventRegistrations`), `simulate`, and test
|
|
137
|
+
// helpers so the three stay in sync instead of re-deriving the per-ecosystem
|
|
138
|
+
// dispatch each place.
|
|
139
|
+
let buildOnEventRegistration = (
|
|
140
|
+
~config: Config.t,
|
|
141
|
+
~chainId: int,
|
|
142
|
+
~eventConfig: Internal.eventConfig,
|
|
143
|
+
~startBlock=?,
|
|
144
|
+
): Internal.onEventRegistration => {
|
|
145
|
+
let contractName = eventConfig.contractName
|
|
146
|
+
let eventName = eventConfig.name
|
|
147
|
+
let t = get(~contractName, ~eventName)
|
|
148
|
+
let isWildcard = t.eventOptions->Option.flatMap(v => v.wildcard)->Option.getOr(false)
|
|
149
|
+
let handler = t.handler
|
|
150
|
+
let contractRegister = t.contractRegister
|
|
151
|
+
|
|
152
|
+
switch config.ecosystem.name {
|
|
153
|
+
| Fuel =>
|
|
154
|
+
(EventConfigBuilder.buildFuelOnEventRegistration(
|
|
155
|
+
~eventConfig=eventConfig->(Utils.magic: Internal.eventConfig => Internal.fuelEventConfig),
|
|
156
|
+
~isWildcard,
|
|
157
|
+
~handler,
|
|
158
|
+
~contractRegister,
|
|
159
|
+
~startBlock?,
|
|
160
|
+
) :> Internal.onEventRegistration)
|
|
161
|
+
| Svm =>
|
|
162
|
+
(EventConfigBuilder.buildSvmOnEventRegistration(
|
|
163
|
+
~eventConfig=eventConfig->(
|
|
164
|
+
Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig
|
|
165
|
+
),
|
|
166
|
+
~isWildcard,
|
|
167
|
+
~handler,
|
|
168
|
+
~contractRegister,
|
|
169
|
+
~startBlock?,
|
|
170
|
+
) :> Internal.onEventRegistration)
|
|
171
|
+
| Evm =>
|
|
172
|
+
(EventConfigBuilder.buildEvmOnEventRegistration(
|
|
173
|
+
~eventConfig=eventConfig->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig),
|
|
174
|
+
~isWildcard,
|
|
175
|
+
~handler,
|
|
176
|
+
~contractRegister,
|
|
177
|
+
~eventFilters=t.eventOptions->Option.flatMap(v => v.where),
|
|
178
|
+
~probeChainId=chainId,
|
|
179
|
+
~onEventBlockFilterSchema=config.ecosystem.onEventBlockFilterSchema,
|
|
180
|
+
~startBlock?,
|
|
181
|
+
) :> Internal.onEventRegistration)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Enrich a chain's static event definitions with the registered
|
|
186
|
+
// handler/contractRegister/where (validating them along the way) to produce
|
|
187
|
+
// the onEventRegistrations ChainState indexes off. Runs once per chain when
|
|
188
|
+
// registration finishes, so a bad `where`/duplicate event throws during
|
|
189
|
+
// startup with a stack trace pointing here instead of surfacing later from
|
|
190
|
+
// inside ChainState's construction. Events without a handler/contractRegister
|
|
191
|
+
// get added to `notRegisteredEventsByContract` (event names grouped by
|
|
192
|
+
// contract name) so the caller can report them once for the whole indexer
|
|
193
|
+
// instead of per chain.
|
|
194
|
+
let buildOnEventRegistrations = (
|
|
195
|
+
~chainConfig: Config.chain,
|
|
196
|
+
~config: Config.t,
|
|
197
|
+
~notRegisteredEventsByContract: dict<Utils.Set.t<string>>,
|
|
198
|
+
): array<Internal.onEventRegistration> => {
|
|
199
|
+
// We don't need the router itself, but only validation logic,
|
|
200
|
+
// since now event router is created for selection of events
|
|
201
|
+
// and validation doesn't work correctly in routers.
|
|
202
|
+
// Ideally to split it into two different parts.
|
|
203
|
+
let eventRouter = EventRouter.empty()
|
|
204
|
+
|
|
205
|
+
let onEventRegistrations: array<Internal.onEventRegistration> = []
|
|
206
|
+
|
|
207
|
+
chainConfig.contracts->Array.forEach(contract => {
|
|
208
|
+
let contractName = contract.name
|
|
209
|
+
|
|
210
|
+
contract.events->Array.forEach(eventConfig => {
|
|
211
|
+
let eventName = eventConfig.name
|
|
212
|
+
|
|
213
|
+
let onEventRegistration = buildOnEventRegistration(
|
|
214
|
+
~config,
|
|
215
|
+
~chainId=chainConfig.id,
|
|
216
|
+
~eventConfig,
|
|
217
|
+
~startBlock=?contract.startBlock,
|
|
218
|
+
)
|
|
219
|
+
let {isWildcard, handler, contractRegister} = onEventRegistration
|
|
220
|
+
|
|
221
|
+
// Should validate the events
|
|
222
|
+
eventRouter->EventRouter.addOrThrow(
|
|
223
|
+
eventConfig.id,
|
|
224
|
+
(),
|
|
225
|
+
~contractName,
|
|
226
|
+
~chain=ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id),
|
|
227
|
+
~eventName,
|
|
228
|
+
~isWildcard,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
// Filter out events without a handler/contractRegister so they aren't
|
|
232
|
+
// fetched or dispatched (unless raw events are enabled).
|
|
233
|
+
let shouldBeIncluded = if config.enableRawEvents {
|
|
234
|
+
true
|
|
235
|
+
} else {
|
|
236
|
+
let isRegistered = contractRegister->Option.isSome || handler->Option.isSome
|
|
237
|
+
if !isRegistered {
|
|
238
|
+
let eventNames = switch notRegisteredEventsByContract->Utils.Dict.dangerouslyGetNonOption(
|
|
239
|
+
contractName,
|
|
240
|
+
) {
|
|
241
|
+
| Some(set) => set
|
|
242
|
+
| None => {
|
|
243
|
+
let set = Utils.Set.make()
|
|
244
|
+
notRegisteredEventsByContract->Dict.set(contractName, set)
|
|
245
|
+
set
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
eventNames->Utils.Set.add(eventName)->ignore
|
|
249
|
+
}
|
|
250
|
+
isRegistered
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check if event has Static([]) filters (from a dynamic where
|
|
254
|
+
// callback returning `false` / SkipAll for this chain).
|
|
255
|
+
// If so, skip it entirely - it should never be fetched
|
|
256
|
+
let shouldSkip = try {
|
|
257
|
+
let getEventFiltersOrThrow = (
|
|
258
|
+
onEventRegistration->(
|
|
259
|
+
Utils.magic: Internal.onEventRegistration => Internal.evmOnEventRegistration
|
|
260
|
+
)
|
|
261
|
+
).getEventFiltersOrThrow
|
|
262
|
+
|
|
263
|
+
// Check for non-evm chains
|
|
264
|
+
if (
|
|
265
|
+
getEventFiltersOrThrow->(Utils.magic: (ChainMap.Chain.t => Internal.eventFilters) => bool)
|
|
266
|
+
) {
|
|
267
|
+
switch getEventFiltersOrThrow(ChainMap.Chain.makeUnsafe(~chainId=chainConfig.id)) {
|
|
268
|
+
| Static([]) => true
|
|
269
|
+
| _ => false
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
false
|
|
273
|
+
}
|
|
274
|
+
} catch {
|
|
275
|
+
// Can throw when filter is invalid
|
|
276
|
+
// Don't skip an event in this case. Let it throw in a better place - source code
|
|
277
|
+
| _ => false
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if shouldBeIncluded && !shouldSkip {
|
|
281
|
+
onEventRegistrations->Array.push(onEventRegistration)
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
onEventRegistrations
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let finishRegistration = (~config: Config.t): registrationsByChainId => {
|
|
123
290
|
switch activeRegistration.contents {
|
|
124
291
|
| Some(r) => {
|
|
125
292
|
r.finished = true
|
|
126
|
-
|
|
293
|
+
let notRegisteredEventsByContract: dict<Utils.Set.t<string>> = Dict.make()
|
|
294
|
+
let registrationsByChainId: registrationsByChainId = Dict.make()
|
|
295
|
+
config.chainMap
|
|
296
|
+
->ChainMap.values
|
|
297
|
+
->Array.forEach(chainConfig => {
|
|
298
|
+
let key = chainConfig.id->Int.toString
|
|
299
|
+
registrationsByChainId->Dict.set(
|
|
300
|
+
key,
|
|
301
|
+
{
|
|
302
|
+
onEventRegistrations: buildOnEventRegistrations(
|
|
303
|
+
~chainConfig,
|
|
304
|
+
~config,
|
|
305
|
+
~notRegisteredEventsByContract,
|
|
306
|
+
),
|
|
307
|
+
onBlockRegistrations: r.registrations.onBlockByChainId
|
|
308
|
+
->Utils.Dict.dangerouslyGetNonOption(key)
|
|
309
|
+
->Option.getOr([]),
|
|
310
|
+
},
|
|
311
|
+
)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
// Reported once for the whole indexer (a shared contract on multiple
|
|
315
|
+
// chains would otherwise repeat the same message per chain).
|
|
316
|
+
let notRegisteredEntries = notRegisteredEventsByContract->Dict.toArray
|
|
317
|
+
if notRegisteredEntries->Utils.Array.notEmpty {
|
|
318
|
+
let groups =
|
|
319
|
+
notRegisteredEntries
|
|
320
|
+
->Array.map(((contractName, eventNames)) =>
|
|
321
|
+
`${contractName} (${eventNames->Utils.Set.toArray->Array.joinUnsafe(", ")})`
|
|
322
|
+
)
|
|
323
|
+
->Array.joinUnsafe(", ")
|
|
324
|
+
Logging.getLogger()->Logging.childInfo(
|
|
325
|
+
`Events without a handler, skipped for indexing: ${groups}`,
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
registrationsByChainId
|
|
127
330
|
}
|
|
128
331
|
| None =>
|
|
129
332
|
JsError.throwWithMessage(
|
|
@@ -178,7 +381,7 @@ let registerOnBlock = (
|
|
|
178
381
|
interval,
|
|
179
382
|
chainId,
|
|
180
383
|
handler,
|
|
181
|
-
}: Internal.
|
|
384
|
+
}: Internal.onBlockRegistration
|
|
182
385
|
),
|
|
183
386
|
)
|
|
184
387
|
})
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
4
|
import * as Logging from "./Logging.res.mjs";
|
|
5
|
+
import * as ChainMap from "./ChainMap.res.mjs";
|
|
6
|
+
import * as EventRouter from "./sources/EventRouter.res.mjs";
|
|
5
7
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
8
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
7
9
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
10
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
11
|
+
import * as EventConfigBuilder from "./EventConfigBuilder.res.mjs";
|
|
8
12
|
|
|
9
13
|
let version = Utils.EnvioPackage.value.version;
|
|
10
14
|
|
|
@@ -88,14 +92,94 @@ function startRegistration(ecosystem) {
|
|
|
88
92
|
};
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
function
|
|
95
|
+
function buildOnEventRegistration(config, chainId, eventConfig, startBlock) {
|
|
96
|
+
let contractName = eventConfig.contractName;
|
|
97
|
+
let eventName = eventConfig.name;
|
|
98
|
+
let t = get(contractName, eventName);
|
|
99
|
+
let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(t.eventOptions, v => v.wildcard), false);
|
|
100
|
+
let handler = t.handler;
|
|
101
|
+
let contractRegister = t.contractRegister;
|
|
102
|
+
let match = config.ecosystem.name;
|
|
103
|
+
switch (match) {
|
|
104
|
+
case "evm" :
|
|
105
|
+
return EventConfigBuilder.buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, Stdlib_Option.flatMap(t.eventOptions, v => v.where), chainId, config.ecosystem.onEventBlockFilterSchema, startBlock);
|
|
106
|
+
case "fuel" :
|
|
107
|
+
return EventConfigBuilder.buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
|
|
108
|
+
case "svm" :
|
|
109
|
+
return EventConfigBuilder.buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function buildOnEventRegistrations(chainConfig, config, notRegisteredEventsByContract) {
|
|
114
|
+
let eventRouter = EventRouter.empty();
|
|
115
|
+
let onEventRegistrations = [];
|
|
116
|
+
chainConfig.contracts.forEach(contract => {
|
|
117
|
+
let contractName = contract.name;
|
|
118
|
+
contract.events.forEach(eventConfig => {
|
|
119
|
+
let eventName = eventConfig.name;
|
|
120
|
+
let onEventRegistration = buildOnEventRegistration(config, chainConfig.id, eventConfig, contract.startBlock);
|
|
121
|
+
EventRouter.addOrThrow(eventRouter, eventConfig.id, undefined, contractName, onEventRegistration.isWildcard, eventName, ChainMap.Chain.makeUnsafe(chainConfig.id));
|
|
122
|
+
let shouldBeIncluded;
|
|
123
|
+
if (config.enableRawEvents) {
|
|
124
|
+
shouldBeIncluded = true;
|
|
125
|
+
} else {
|
|
126
|
+
let isRegistered = Stdlib_Option.isSome(onEventRegistration.contractRegister) || Stdlib_Option.isSome(onEventRegistration.handler);
|
|
127
|
+
if (!isRegistered) {
|
|
128
|
+
let set = notRegisteredEventsByContract[contractName];
|
|
129
|
+
let eventNames;
|
|
130
|
+
if (set !== undefined) {
|
|
131
|
+
eventNames = Primitive_option.valFromOption(set);
|
|
132
|
+
} else {
|
|
133
|
+
let set$1 = new Set();
|
|
134
|
+
notRegisteredEventsByContract[contractName] = set$1;
|
|
135
|
+
eventNames = set$1;
|
|
136
|
+
}
|
|
137
|
+
eventNames.add(eventName);
|
|
138
|
+
}
|
|
139
|
+
shouldBeIncluded = isRegistered;
|
|
140
|
+
}
|
|
141
|
+
let shouldSkip;
|
|
142
|
+
try {
|
|
143
|
+
let getEventFiltersOrThrow = onEventRegistration.getEventFiltersOrThrow;
|
|
144
|
+
if (getEventFiltersOrThrow) {
|
|
145
|
+
let match = getEventFiltersOrThrow(ChainMap.Chain.makeUnsafe(chainConfig.id));
|
|
146
|
+
shouldSkip = match.TAG === "Static" ? match._0.length === 0 : false;
|
|
147
|
+
} else {
|
|
148
|
+
shouldSkip = false;
|
|
149
|
+
}
|
|
150
|
+
} catch (exn) {
|
|
151
|
+
shouldSkip = false;
|
|
152
|
+
}
|
|
153
|
+
if (shouldBeIncluded && !shouldSkip) {
|
|
154
|
+
onEventRegistrations.push(onEventRegistration);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
return onEventRegistrations;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function finishRegistration(config) {
|
|
92
163
|
let r = activeRegistration.contents;
|
|
93
|
-
if (r
|
|
94
|
-
r.finished = true;
|
|
95
|
-
return r.registrations;
|
|
96
|
-
} else {
|
|
164
|
+
if (r === undefined) {
|
|
97
165
|
return Stdlib_JsError.throwWithMessage("The indexer has not started registering handlers, so can't finish it.");
|
|
98
166
|
}
|
|
167
|
+
r.finished = true;
|
|
168
|
+
let notRegisteredEventsByContract = {};
|
|
169
|
+
let registrationsByChainId = {};
|
|
170
|
+
ChainMap.values(config.chainMap).forEach(chainConfig => {
|
|
171
|
+
let key = chainConfig.id.toString();
|
|
172
|
+
registrationsByChainId[key] = {
|
|
173
|
+
onEventRegistrations: buildOnEventRegistrations(chainConfig, config, notRegisteredEventsByContract),
|
|
174
|
+
onBlockRegistrations: Stdlib_Option.getOr(r.registrations.onBlockByChainId[key], [])
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
let notRegisteredEntries = Object.entries(notRegisteredEventsByContract);
|
|
178
|
+
if (Utils.$$Array.notEmpty(notRegisteredEntries)) {
|
|
179
|
+
let groups = notRegisteredEntries.map(param => param[0] + ` (` + Array.from(param[1]).join(", ") + `)`).join(", ");
|
|
180
|
+
Logging.childInfo(Logging.getLogger(), `Events without a handler, skipped for indexing: ` + groups);
|
|
181
|
+
}
|
|
182
|
+
return registrationsByChainId;
|
|
99
183
|
}
|
|
100
184
|
|
|
101
185
|
function isPendingRegistration() {
|
|
@@ -282,6 +366,7 @@ export {
|
|
|
282
366
|
isPendingRegistration,
|
|
283
367
|
finishRegistration,
|
|
284
368
|
throwIfFinishedRegistration,
|
|
369
|
+
buildOnEventRegistration,
|
|
285
370
|
setHandler,
|
|
286
371
|
setContractRegister,
|
|
287
372
|
getHandler,
|
package/src/HandlerRegister.resi
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
type
|
|
1
|
+
type chainRegistrations = {
|
|
2
|
+
onEventRegistrations: array<Internal.onEventRegistration>,
|
|
3
|
+
onBlockRegistrations: array<Internal.onBlockRegistration>,
|
|
4
|
+
}
|
|
5
|
+
type registrationsByChainId = dict<chainRegistrations>
|
|
2
6
|
|
|
3
7
|
let startRegistration: (~ecosystem: Ecosystem.t) => unit
|
|
4
8
|
let isPendingRegistration: unit => bool
|
|
5
|
-
let finishRegistration:
|
|
9
|
+
let finishRegistration: (~config: Config.t) => registrationsByChainId
|
|
6
10
|
let throwIfFinishedRegistration: (~methodName: string) => unit
|
|
7
11
|
|
|
12
|
+
let buildOnEventRegistration: (
|
|
13
|
+
~config: Config.t,
|
|
14
|
+
~chainId: int,
|
|
15
|
+
~eventConfig: Internal.eventConfig,
|
|
16
|
+
~startBlock: int=?,
|
|
17
|
+
) => Internal.onEventRegistration
|
|
18
|
+
|
|
8
19
|
let setHandler: (
|
|
9
20
|
~contractName: string,
|
|
10
21
|
~eventName: string,
|
package/src/IndexerState.res
CHANGED
|
@@ -188,7 +188,7 @@ let makeFromDbState = (
|
|
|
188
188
|
~config: Config.t,
|
|
189
189
|
~persistence: Persistence.t,
|
|
190
190
|
~initialState: Persistence.initialState,
|
|
191
|
-
~
|
|
191
|
+
~registrationsByChainId,
|
|
192
192
|
~isDevelopmentMode=false,
|
|
193
193
|
~shouldUseTui=false,
|
|
194
194
|
~exitAfterFirstEventBlock=false,
|
|
@@ -234,7 +234,7 @@ let makeFromDbState = (
|
|
|
234
234
|
~isInReorgThreshold,
|
|
235
235
|
~isRealtime,
|
|
236
236
|
~config,
|
|
237
|
-
~
|
|
237
|
+
~registrationsByChainId,
|
|
238
238
|
~reducedPollingInterval?,
|
|
239
239
|
),
|
|
240
240
|
)
|
|
@@ -362,7 +362,10 @@ let clearRollback = (state: t) => state.rollbackState = NoRollback
|
|
|
362
362
|
let invalidateInflight = (state: t) => state.epoch = state.epoch + 1
|
|
363
363
|
|
|
364
364
|
let applyBatchProgress = (state: t, ~batch: Batch.t) =>
|
|
365
|
-
state.crossChainState->CrossChainState.applyBatchProgress(
|
|
365
|
+
state.crossChainState->CrossChainState.applyBatchProgress(
|
|
366
|
+
~batch,
|
|
367
|
+
~blockTimestampName=state.config.ecosystem.blockTimestampName,
|
|
368
|
+
)
|
|
366
369
|
|
|
367
370
|
// Processing-loop mutex. Guards ProcessEventBatch re-entry so only one
|
|
368
371
|
// processing loop runs at a time.
|