envio 3.3.0-alpha.8 → 3.3.0-alpha.9
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/package.json +6 -6
- package/src/ChainState.res +104 -37
- package/src/ChainState.res.mjs +80 -19
- package/src/ChainState.resi +4 -1
- package/src/CrossChainState.res +56 -29
- package/src/CrossChainState.res.mjs +11 -2
- package/src/EnvioGlobal.res +53 -0
- package/src/EnvioGlobal.res.mjs +31 -0
- package/src/EventConfigBuilder.res +16 -17
- package/src/EventConfigBuilder.res.mjs +7 -6
- package/src/FetchState.res +151 -130
- package/src/FetchState.res.mjs +105 -88
- package/src/HandlerLoader.res +1 -1
- package/src/HandlerLoader.res.mjs +1 -1
- package/src/HandlerRegister.res +476 -305
- package/src/HandlerRegister.res.mjs +276 -209
- package/src/HandlerRegister.resi +11 -6
- package/src/Internal.res +23 -4
- package/src/LogSelection.res +102 -165
- package/src/LogSelection.res.mjs +101 -116
- package/src/Main.res +29 -143
- package/src/Main.res.mjs +25 -88
- package/src/RollbackCommit.res +4 -1
- package/src/RollbackCommit.res.mjs +3 -2
- package/src/SimulateItems.res +1 -1
- package/src/sources/Evm.res +4 -1
- package/src/sources/Evm.res.mjs +1 -1
- package/src/sources/Fuel.res +3 -1
- package/src/sources/Fuel.res.mjs +1 -1
- package/src/sources/HyperSyncSource.res +22 -33
- package/src/sources/HyperSyncSource.res.mjs +17 -26
- package/src/sources/RpcSource.res +28 -39
- package/src/sources/RpcSource.res.mjs +18 -31
- package/src/sources/SourceManager.res +1 -7
- package/src/sources/SourceManager.res.mjs +1 -2
|
@@ -3,38 +3,17 @@
|
|
|
3
3
|
import * as Utils from "./Utils.res.mjs";
|
|
4
4
|
import * as Logging from "./Logging.res.mjs";
|
|
5
5
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
6
|
+
import * as EnvioGlobal from "./EnvioGlobal.res.mjs";
|
|
6
7
|
import * as EventRouter from "./sources/EventRouter.res.mjs";
|
|
7
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
9
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
9
10
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
10
11
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
12
|
+
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
11
13
|
import * as EventConfigBuilder from "./EventConfigBuilder.res.mjs";
|
|
14
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
12
15
|
|
|
13
|
-
let
|
|
14
|
-
|
|
15
|
-
let existing = globalThis.__envioRegistry;
|
|
16
|
-
|
|
17
|
-
let registry;
|
|
18
|
-
|
|
19
|
-
if (existing == null) {
|
|
20
|
-
let fresh_eventRegistrations = {};
|
|
21
|
-
let fresh_activeRegistration = {
|
|
22
|
-
contents: undefined
|
|
23
|
-
};
|
|
24
|
-
let fresh_preRegistered = [];
|
|
25
|
-
let fresh = {
|
|
26
|
-
version: version,
|
|
27
|
-
eventRegistrations: fresh_eventRegistrations,
|
|
28
|
-
activeRegistration: fresh_activeRegistration,
|
|
29
|
-
preRegistered: fresh_preRegistered
|
|
30
|
-
};
|
|
31
|
-
globalThis.__envioRegistry = fresh;
|
|
32
|
-
registry = fresh;
|
|
33
|
-
} else {
|
|
34
|
-
registry = existing.version === version ? existing : Stdlib_JsError.throwWithMessage(`Multiple incompatible envio versions loaded in the same process: ` + existing.version + ` and ` + version + `. Deduplicate the 'envio' dependency in your project.`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let eventRegistrations = registry.eventRegistrations;
|
|
16
|
+
let eventRegistrations = EnvioGlobal.value.eventRegistrations;
|
|
38
17
|
|
|
39
18
|
function getKey(contractName, eventName) {
|
|
40
19
|
return contractName + "." + eventName;
|
|
@@ -57,12 +36,10 @@ function set(contractName, eventName, registration) {
|
|
|
57
36
|
eventRegistrations[getKey(contractName, eventName)] = registration;
|
|
58
37
|
}
|
|
59
38
|
|
|
60
|
-
let
|
|
61
|
-
|
|
62
|
-
let preRegistered = registry.preRegistered;
|
|
39
|
+
let preRegistered = EnvioGlobal.value.preRegistered;
|
|
63
40
|
|
|
64
41
|
function withRegistration(fn) {
|
|
65
|
-
let r = activeRegistration
|
|
42
|
+
let r = EnvioGlobal.value.activeRegistration;
|
|
66
43
|
if (r !== undefined) {
|
|
67
44
|
if (r.finished) {
|
|
68
45
|
return Stdlib_JsError.throwWithMessage("The indexer finished initializing, so no more handlers can be registered. Make sure the handlers are registered on the top level of the file.");
|
|
@@ -75,15 +52,13 @@ function withRegistration(fn) {
|
|
|
75
52
|
}
|
|
76
53
|
}
|
|
77
54
|
|
|
78
|
-
function startRegistration(
|
|
55
|
+
function startRegistration(config) {
|
|
79
56
|
let r = {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
onBlockByChainId: {}
|
|
83
|
-
},
|
|
57
|
+
config: config,
|
|
58
|
+
registrationsByChainId: {},
|
|
84
59
|
finished: false
|
|
85
60
|
};
|
|
86
|
-
activeRegistration
|
|
61
|
+
EnvioGlobal.value.activeRegistration = Primitive_option.some(r);
|
|
87
62
|
while (preRegistered.length !== 0) {
|
|
88
63
|
let fn = preRegistered.pop();
|
|
89
64
|
if (fn !== undefined) {
|
|
@@ -92,17 +67,27 @@ function startRegistration(ecosystem) {
|
|
|
92
67
|
};
|
|
93
68
|
}
|
|
94
69
|
|
|
95
|
-
function
|
|
96
|
-
let
|
|
97
|
-
let
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let
|
|
70
|
+
function getPendingChainRegistrations(r, chainId) {
|
|
71
|
+
let key = chainId.toString();
|
|
72
|
+
let pending = r.registrationsByChainId[key];
|
|
73
|
+
if (pending !== undefined) {
|
|
74
|
+
return pending;
|
|
75
|
+
}
|
|
76
|
+
let fresh_onEventRegistrations = {};
|
|
77
|
+
let fresh_onBlockRegistrations = [];
|
|
78
|
+
let fresh = {
|
|
79
|
+
onEventRegistrations: fresh_onEventRegistrations,
|
|
80
|
+
onBlockRegistrations: fresh_onBlockRegistrations
|
|
81
|
+
};
|
|
82
|
+
r.registrationsByChainId[key] = fresh;
|
|
83
|
+
return fresh;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function buildOnEventRegistrationWith(config, chainId, eventConfig, isWildcard, handler, contractRegister, where, startBlock) {
|
|
102
87
|
let match = config.ecosystem.name;
|
|
103
88
|
switch (match) {
|
|
104
89
|
case "evm" :
|
|
105
|
-
return EventConfigBuilder.buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister,
|
|
90
|
+
return EventConfigBuilder.buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, where, chainId, config.ecosystem.onEventBlockFilterSchema, startBlock);
|
|
106
91
|
case "fuel" :
|
|
107
92
|
return EventConfigBuilder.buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contractRegister, startBlock);
|
|
108
93
|
case "svm" :
|
|
@@ -110,113 +95,9 @@ function buildOnEventRegistration(config, chainId, eventConfig, startBlock) {
|
|
|
110
95
|
}
|
|
111
96
|
}
|
|
112
97
|
|
|
113
|
-
function
|
|
114
|
-
let
|
|
115
|
-
|
|
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) {
|
|
163
|
-
let r = activeRegistration.contents;
|
|
164
|
-
if (r === undefined) {
|
|
165
|
-
return Stdlib_JsError.throwWithMessage("The indexer has not started registering handlers, so can't finish it.");
|
|
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;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function isPendingRegistration() {
|
|
186
|
-
let r = activeRegistration.contents;
|
|
187
|
-
if (r !== undefined) {
|
|
188
|
-
return !r.finished;
|
|
189
|
-
} else {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function throwIfFinishedRegistration(methodName) {
|
|
195
|
-
let match = activeRegistration.contents;
|
|
196
|
-
if (match === undefined) {
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
let match$1 = match.finished;
|
|
200
|
-
if (match$1) {
|
|
201
|
-
return Stdlib_JsError.throwWithMessage(`Cannot call \`indexer.` + methodName + `\` after the indexer has started. Make sure all handlers are registered at the top level of your handler module.`);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function registerOnBlock(name, chainId, interval, startBlock, endBlock, handler) {
|
|
206
|
-
withRegistration(registration => {
|
|
207
|
-
let onBlockByChainId = registration.registrations.onBlockByChainId;
|
|
208
|
-
let key = chainId.toString();
|
|
209
|
-
let index = Stdlib_Option.mapOr(onBlockByChainId[key], 0, configs => configs.length);
|
|
210
|
-
Utils.Dict.push(onBlockByChainId, key, {
|
|
211
|
-
index: index,
|
|
212
|
-
name: name,
|
|
213
|
-
chainId: chainId,
|
|
214
|
-
startBlock: startBlock,
|
|
215
|
-
endBlock: endBlock,
|
|
216
|
-
interval: interval,
|
|
217
|
-
handler: handler
|
|
218
|
-
});
|
|
219
|
-
});
|
|
98
|
+
function buildOnEventRegistration(config, chainId, eventConfig, startBlock) {
|
|
99
|
+
let t = get(eventConfig.contractName, eventConfig.name);
|
|
100
|
+
return buildOnEventRegistrationWith(config, chainId, eventConfig, Stdlib_Option.getOr(Stdlib_Option.flatMap(t.eventOptions, v => v.wildcard), false), t.handler, t.contractRegister, Stdlib_Option.flatMap(t.eventOptions, v => v.where), startBlock);
|
|
220
101
|
}
|
|
221
102
|
|
|
222
103
|
function getHandler(contractName, eventName) {
|
|
@@ -227,10 +108,6 @@ function getContractRegister(contractName, eventName) {
|
|
|
227
108
|
return get(contractName, eventName).contractRegister;
|
|
228
109
|
}
|
|
229
110
|
|
|
230
|
-
function getOnEventWhere(contractName, eventName) {
|
|
231
|
-
return Stdlib_Option.flatMap(get(contractName, eventName).eventOptions, value => value.where);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
111
|
function isWildcard(contractName, eventName) {
|
|
235
112
|
return Stdlib_Option.getOr(Stdlib_Option.flatMap(get(contractName, eventName).eventOptions, value => value.wildcard), false);
|
|
236
113
|
}
|
|
@@ -255,22 +132,8 @@ function raiseDuplicateRegistration(contractName, eventName, msg, logger) {
|
|
|
255
132
|
|
|
256
133
|
function eventOptionsMatch(existing, incoming) {
|
|
257
134
|
if (existing !== undefined) {
|
|
258
|
-
if (incoming !== undefined
|
|
259
|
-
|
|
260
|
-
let b = incoming.where;
|
|
261
|
-
if (a !== undefined) {
|
|
262
|
-
if (b !== undefined) {
|
|
263
|
-
if (typeof a === "function" || typeof b === "function") {
|
|
264
|
-
return a === b;
|
|
265
|
-
} else {
|
|
266
|
-
return Primitive_object.equal(a, b);
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
} else {
|
|
272
|
-
return b === undefined;
|
|
273
|
-
}
|
|
135
|
+
if (incoming !== undefined) {
|
|
136
|
+
return existing.wildcard === incoming.wildcard;
|
|
274
137
|
} else {
|
|
275
138
|
return false;
|
|
276
139
|
}
|
|
@@ -279,6 +142,31 @@ function eventOptionsMatch(existing, incoming) {
|
|
|
279
142
|
}
|
|
280
143
|
}
|
|
281
144
|
|
|
145
|
+
function syncOnEventRegistrations(r, contractName, eventName, where, duplicateMsg, logger) {
|
|
146
|
+
let config = r.config;
|
|
147
|
+
let t = get(contractName, eventName);
|
|
148
|
+
let isWildcard = Stdlib_Option.getOr(Stdlib_Option.flatMap(t.eventOptions, v => v.wildcard), false);
|
|
149
|
+
let key = getKey(contractName, eventName);
|
|
150
|
+
ChainMap.values(config.chainMap).forEach(chainConfig => {
|
|
151
|
+
chainConfig.contracts.forEach(contract => {
|
|
152
|
+
if (contract.name !== contractName) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
let eventConfig = contract.events.find(e => e.name === eventName);
|
|
156
|
+
if (eventConfig === undefined) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
let newRegistration = buildOnEventRegistrationWith(config, chainConfig.id, eventConfig, isWildcard, t.handler, t.contractRegister, where, contract.startBlock);
|
|
160
|
+
let pending = getPendingChainRegistrations(r, chainConfig.id);
|
|
161
|
+
let existing = pending.onEventRegistrations[key];
|
|
162
|
+
if (existing !== undefined && config.ecosystem.name === "evm" && !Primitive_object.equal(existing.resolvedWhere, newRegistration.resolvedWhere)) {
|
|
163
|
+
raiseDuplicateRegistration(contractName, eventName, duplicateMsg, logger);
|
|
164
|
+
}
|
|
165
|
+
pending.onEventRegistrations[key] = newRegistration;
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
282
170
|
function setEventOptions(contractName, eventName, eventOptions, loggerOpt) {
|
|
283
171
|
let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
|
|
284
172
|
if (eventOptions === undefined) {
|
|
@@ -303,64 +191,242 @@ function setEventOptions(contractName, eventName, eventOptions, loggerOpt) {
|
|
|
303
191
|
|
|
304
192
|
function setHandler(contractName, eventName, handler, eventOptions, loggerOpt) {
|
|
305
193
|
let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
|
|
306
|
-
withRegistration(
|
|
194
|
+
withRegistration(registration => {
|
|
307
195
|
let t = get(contractName, eventName);
|
|
196
|
+
let incomingEventOptions = Stdlib_Option.map(eventOptions, v => v);
|
|
308
197
|
let prevHandler = t.handler;
|
|
309
198
|
if (prevHandler !== undefined) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
199
|
+
if (eventOptionsMatch(t.eventOptions, incomingEventOptions)) {
|
|
200
|
+
let composedHandler = async args => {
|
|
201
|
+
await prevHandler(args);
|
|
202
|
+
return await handler(args);
|
|
203
|
+
};
|
|
204
|
+
set(contractName, eventName, {
|
|
205
|
+
handler: composedHandler,
|
|
206
|
+
contractRegister: t.contractRegister,
|
|
207
|
+
eventOptions: t.eventOptions
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
raiseDuplicateRegistration(contractName, eventName, "Cannot register a second handler with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
|
|
313
211
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
eventOptions: t.eventOptions
|
|
212
|
+
} else {
|
|
213
|
+
setEventOptions(contractName, eventName, eventOptions, logger);
|
|
214
|
+
let t$1 = get(contractName, eventName);
|
|
215
|
+
set(contractName, eventName, {
|
|
216
|
+
handler: handler,
|
|
217
|
+
contractRegister: t$1.contractRegister,
|
|
218
|
+
eventOptions: t$1.eventOptions
|
|
322
219
|
});
|
|
323
220
|
}
|
|
324
|
-
|
|
325
|
-
let t$1 = get(contractName, eventName);
|
|
326
|
-
set(contractName, eventName, {
|
|
327
|
-
handler: handler,
|
|
328
|
-
contractRegister: t$1.contractRegister,
|
|
329
|
-
eventOptions: t$1.eventOptions
|
|
330
|
-
});
|
|
221
|
+
syncOnEventRegistrations(registration, contractName, eventName, Stdlib_Option.flatMap(incomingEventOptions, v => v.where), "Cannot register a second handler with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
|
|
331
222
|
});
|
|
332
223
|
}
|
|
333
224
|
|
|
334
225
|
function setContractRegister(contractName, eventName, contractRegister, eventOptions, loggerOpt) {
|
|
335
226
|
let logger = loggerOpt !== undefined ? loggerOpt : Logging.getLogger();
|
|
336
|
-
withRegistration(
|
|
227
|
+
withRegistration(registration => {
|
|
337
228
|
let t = get(contractName, eventName);
|
|
229
|
+
let incomingEventOptions = Stdlib_Option.map(eventOptions, v => v);
|
|
338
230
|
let prevContractRegister = t.contractRegister;
|
|
339
231
|
if (prevContractRegister !== undefined) {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
232
|
+
if (eventOptionsMatch(t.eventOptions, incomingEventOptions)) {
|
|
233
|
+
let composedContractRegister = async args => {
|
|
234
|
+
await prevContractRegister(args);
|
|
235
|
+
return await contractRegister(args);
|
|
236
|
+
};
|
|
237
|
+
set(contractName, eventName, {
|
|
238
|
+
handler: t.handler,
|
|
239
|
+
contractRegister: composedContractRegister,
|
|
240
|
+
eventOptions: t.eventOptions
|
|
241
|
+
});
|
|
242
|
+
} else {
|
|
243
|
+
raiseDuplicateRegistration(contractName, eventName, "Cannot register a second contractRegister with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
|
|
343
244
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
eventOptions: t.eventOptions
|
|
245
|
+
} else {
|
|
246
|
+
setEventOptions(contractName, eventName, eventOptions, logger);
|
|
247
|
+
let t$1 = get(contractName, eventName);
|
|
248
|
+
set(contractName, eventName, {
|
|
249
|
+
handler: t$1.handler,
|
|
250
|
+
contractRegister: contractRegister,
|
|
251
|
+
eventOptions: t$1.eventOptions
|
|
352
252
|
});
|
|
353
253
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
254
|
+
syncOnEventRegistrations(registration, contractName, eventName, Stdlib_Option.flatMap(incomingEventOptions, v => v.where), "Cannot register a second contractRegister with different options. Make sure all handlers for the same event use identical options (wildcard, where)", logger);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let blockRangeSchema = S$RescriptSchema.strict(S$RescriptSchema.object(s => ({
|
|
259
|
+
_gte: s.f("_gte", S$RescriptSchema.option(S$RescriptSchema.int)),
|
|
260
|
+
_lte: s.f("_lte", S$RescriptSchema.option(S$RescriptSchema.int)),
|
|
261
|
+
_every: s.f("_every", S$RescriptSchema.Option.getOr(S$RescriptSchema.option(S$RescriptSchema.intMin(S$RescriptSchema.int, 1, undefined)), 1))
|
|
262
|
+
})));
|
|
263
|
+
|
|
264
|
+
let defaultBlockRange = {
|
|
265
|
+
_gte: undefined,
|
|
266
|
+
_lte: undefined,
|
|
267
|
+
_every: 1
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
function extractRange(filter, name, ecosystem) {
|
|
271
|
+
try {
|
|
272
|
+
let inner = S$RescriptSchema.parseOrThrow(filter, ecosystem.onBlockFilterSchema);
|
|
273
|
+
if (inner !== undefined) {
|
|
274
|
+
return S$RescriptSchema.parseOrThrow(Primitive_option.valFromOption(inner), blockRangeSchema);
|
|
275
|
+
} else {
|
|
276
|
+
return defaultBlockRange;
|
|
277
|
+
}
|
|
278
|
+
} catch (raw_exn) {
|
|
279
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
280
|
+
if (exn.RE_EXN_ID === S$RescriptSchema.Raised) {
|
|
281
|
+
return Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` \`where\` returned an invalid filter: ` + Utils.prettifyExn(exn._1));
|
|
282
|
+
}
|
|
283
|
+
throw exn;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function registerOnBlock(name, where, handler, getChainsObject) {
|
|
288
|
+
withRegistration(registration => {
|
|
289
|
+
let config = registration.config;
|
|
290
|
+
let ecosystem = config.ecosystem;
|
|
291
|
+
let chainsDict = getChainsObject(config);
|
|
292
|
+
let logger = Logging.createChild({
|
|
293
|
+
onBlock: name
|
|
360
294
|
});
|
|
295
|
+
let where$1 = where === undefined || where === null ? undefined : (
|
|
296
|
+
typeof where === "function" ? where : Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` expected \`where\` to be a function or omitted, but got ` + typeof where + `.`)
|
|
297
|
+
);
|
|
298
|
+
let matchedAny = {
|
|
299
|
+
contents: false
|
|
300
|
+
};
|
|
301
|
+
ChainMap.values(config.chainMap).forEach(chainConfig => {
|
|
302
|
+
let chainId = chainConfig.id;
|
|
303
|
+
let chainObj = chainsDict[chainId.toString()];
|
|
304
|
+
let result = where$1 !== undefined ? where$1({
|
|
305
|
+
chain: chainObj
|
|
306
|
+
}) : true;
|
|
307
|
+
let match = result === true ? [
|
|
308
|
+
true,
|
|
309
|
+
defaultBlockRange
|
|
310
|
+
] : (
|
|
311
|
+
result === false ? [
|
|
312
|
+
false,
|
|
313
|
+
defaultBlockRange
|
|
314
|
+
] : (
|
|
315
|
+
typeof result === "object" && !Array.isArray(result) && result !== null ? [
|
|
316
|
+
true,
|
|
317
|
+
extractRange(result, name, ecosystem)
|
|
318
|
+
] : Stdlib_JsError.throwWithMessage(`\`indexer.` + ecosystem.onBlockMethodName + `("` + name + `")\` \`where\` predicate returned an invalid value of type ` + typeof result + `. Expected boolean or a filter object.`)
|
|
319
|
+
)
|
|
320
|
+
);
|
|
321
|
+
if (!match[0]) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
let range = match[1];
|
|
325
|
+
matchedAny.contents = true;
|
|
326
|
+
if (Stdlib_Option.getOr(range._gte, chainConfig.startBlock) < chainConfig.startBlock) {
|
|
327
|
+
Stdlib_JsError.throwWithMessage(`The start block for onBlock handler "` + name + `" is less than the chain start block (` + chainConfig.startBlock.toString() + `). This is not supported yet.`);
|
|
328
|
+
}
|
|
329
|
+
let chainEndBlock = chainConfig.endBlock;
|
|
330
|
+
if (chainEndBlock !== undefined && Stdlib_Option.getOr(range._lte, chainEndBlock) > chainEndBlock) {
|
|
331
|
+
Stdlib_JsError.throwWithMessage(`The end block for onBlock handler "` + name + `" is greater than the chain end block (` + chainEndBlock.toString() + `). This is not supported yet.`);
|
|
332
|
+
}
|
|
333
|
+
let pending = getPendingChainRegistrations(registration, chainId);
|
|
334
|
+
pending.onBlockRegistrations.push({
|
|
335
|
+
index: pending.onBlockRegistrations.length,
|
|
336
|
+
name: name,
|
|
337
|
+
chainId: chainId,
|
|
338
|
+
startBlock: range._gte,
|
|
339
|
+
endBlock: range._lte,
|
|
340
|
+
interval: range._every,
|
|
341
|
+
handler: handler
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
if (!matchedAny.contents) {
|
|
345
|
+
return Logging.childWarn(logger, `\`indexer.` + ecosystem.onBlockMethodName + `\` matched 0 chains. Check the \`where\` predicate.`);
|
|
346
|
+
}
|
|
361
347
|
});
|
|
362
348
|
}
|
|
363
349
|
|
|
350
|
+
function finishRegistration(config) {
|
|
351
|
+
let r = EnvioGlobal.value.activeRegistration;
|
|
352
|
+
if (r === undefined) {
|
|
353
|
+
return Stdlib_JsError.throwWithMessage("The indexer has not started registering handlers, so can't finish it.");
|
|
354
|
+
}
|
|
355
|
+
r.finished = true;
|
|
356
|
+
let notRegisteredEventsByContract = {};
|
|
357
|
+
let registrationsByChainId = {};
|
|
358
|
+
ChainMap.values(config.chainMap).forEach(chainConfig => {
|
|
359
|
+
let key = chainConfig.id.toString();
|
|
360
|
+
let pending = r.registrationsByChainId[key];
|
|
361
|
+
let eventRouter = EventRouter.empty();
|
|
362
|
+
let onEventRegistrations = [];
|
|
363
|
+
chainConfig.contracts.forEach(contract => {
|
|
364
|
+
let contractName = contract.name;
|
|
365
|
+
contract.events.forEach(eventConfig => {
|
|
366
|
+
let eventName = eventConfig.name;
|
|
367
|
+
let registration = Stdlib_Option.flatMap(pending, pending => pending.onEventRegistrations[getKey(contractName, eventName)]);
|
|
368
|
+
EventRouter.addOrThrow(eventRouter, eventConfig.id, undefined, contractName, registration !== undefined ? registration.isWildcard : isWildcard(contractName, eventName), eventName, ChainMap.Chain.makeUnsafe(chainConfig.id));
|
|
369
|
+
let registration$1;
|
|
370
|
+
if (registration !== undefined) {
|
|
371
|
+
registration$1 = registration;
|
|
372
|
+
} else if (hasRegistration(contractName, eventName) || config.enableRawEvents) {
|
|
373
|
+
registration$1 = buildOnEventRegistration(config, chainConfig.id, eventConfig, contract.startBlock);
|
|
374
|
+
} else {
|
|
375
|
+
let set = notRegisteredEventsByContract[contractName];
|
|
376
|
+
let eventNames;
|
|
377
|
+
if (set !== undefined) {
|
|
378
|
+
eventNames = Primitive_option.valFromOption(set);
|
|
379
|
+
} else {
|
|
380
|
+
let set$1 = new Set();
|
|
381
|
+
notRegisteredEventsByContract[contractName] = set$1;
|
|
382
|
+
eventNames = set$1;
|
|
383
|
+
}
|
|
384
|
+
eventNames.add(eventName);
|
|
385
|
+
registration$1 = undefined;
|
|
386
|
+
}
|
|
387
|
+
if (registration$1 === undefined) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
let isDroppedByWhere = config.ecosystem.name === "evm" && Utils.$$Array.isEmpty(registration$1.resolvedWhere.topicSelections);
|
|
391
|
+
if (!isDroppedByWhere) {
|
|
392
|
+
onEventRegistrations.push(registration$1);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
registrationsByChainId[key] = {
|
|
398
|
+
onEventRegistrations: onEventRegistrations,
|
|
399
|
+
onBlockRegistrations: pending !== undefined ? pending.onBlockRegistrations : []
|
|
400
|
+
};
|
|
401
|
+
});
|
|
402
|
+
let notRegisteredEntries = Object.entries(notRegisteredEventsByContract);
|
|
403
|
+
if (Utils.$$Array.notEmpty(notRegisteredEntries)) {
|
|
404
|
+
let groups = notRegisteredEntries.map(param => param[0] + ` (` + Array.from(param[1]).join(", ") + `)`).join(", ");
|
|
405
|
+
Logging.childInfo(Logging.getLogger(), `Events without a handler, skipped for indexing: ` + groups);
|
|
406
|
+
}
|
|
407
|
+
return registrationsByChainId;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function isPendingRegistration() {
|
|
411
|
+
let r = EnvioGlobal.value.activeRegistration;
|
|
412
|
+
if (r !== undefined) {
|
|
413
|
+
return !r.finished;
|
|
414
|
+
} else {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function throwIfFinishedRegistration(methodName) {
|
|
420
|
+
let match = EnvioGlobal.value.activeRegistration;
|
|
421
|
+
if (match === undefined) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
let match$1 = match.finished;
|
|
425
|
+
if (match$1) {
|
|
426
|
+
return Stdlib_JsError.throwWithMessage(`Cannot call \`indexer.` + methodName + `\` after the indexer has started. Make sure all handlers are registered at the top level of your handler module.`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
364
430
|
export {
|
|
365
431
|
startRegistration,
|
|
366
432
|
isPendingRegistration,
|
|
@@ -371,9 +437,10 @@ export {
|
|
|
371
437
|
setContractRegister,
|
|
372
438
|
getHandler,
|
|
373
439
|
getContractRegister,
|
|
374
|
-
getOnEventWhere,
|
|
375
440
|
isWildcard,
|
|
376
441
|
hasRegistration,
|
|
442
|
+
blockRangeSchema,
|
|
443
|
+
defaultBlockRange,
|
|
377
444
|
registerOnBlock,
|
|
378
445
|
}
|
|
379
|
-
/*
|
|
446
|
+
/* blockRangeSchema Not a pure module */
|
package/src/HandlerRegister.resi
CHANGED
|
@@ -4,7 +4,7 @@ type chainRegistrations = {
|
|
|
4
4
|
}
|
|
5
5
|
type registrationsByChainId = dict<chainRegistrations>
|
|
6
6
|
|
|
7
|
-
let startRegistration: (~
|
|
7
|
+
let startRegistration: (~config: Config.t) => unit
|
|
8
8
|
let isPendingRegistration: unit => bool
|
|
9
9
|
let finishRegistration: (~config: Config.t) => registrationsByChainId
|
|
10
10
|
let throwIfFinishedRegistration: (~methodName: string) => unit
|
|
@@ -35,15 +35,20 @@ let getContractRegister: (
|
|
|
35
35
|
~contractName: string,
|
|
36
36
|
~eventName: string,
|
|
37
37
|
) => option<Internal.contractRegister>
|
|
38
|
-
let getOnEventWhere: (~contractName: string, ~eventName: string) => option<JSON.t>
|
|
39
38
|
let isWildcard: (~contractName: string, ~eventName: string) => bool
|
|
40
39
|
let hasRegistration: (~contractName: string, ~eventName: string) => bool
|
|
41
40
|
|
|
41
|
+
type blockRange = {
|
|
42
|
+
_gte: option<int>,
|
|
43
|
+
_lte: option<int>,
|
|
44
|
+
_every: int,
|
|
45
|
+
}
|
|
46
|
+
let blockRangeSchema: S.t<blockRange>
|
|
47
|
+
let defaultBlockRange: blockRange
|
|
48
|
+
|
|
42
49
|
let registerOnBlock: (
|
|
43
50
|
~name: string,
|
|
44
|
-
~
|
|
45
|
-
~interval: int,
|
|
46
|
-
~startBlock: option<int>,
|
|
47
|
-
~endBlock: option<int>,
|
|
51
|
+
~where: unknown,
|
|
48
52
|
~handler: Internal.onBlockArgs => promise<unit>,
|
|
53
|
+
~getChainsObject: Config.t => dict<unknown>,
|
|
49
54
|
) => unit
|
package/src/Internal.res
CHANGED
|
@@ -459,15 +459,34 @@ type topicSelection = {
|
|
|
459
459
|
topic3: array<EvmTypes.Hex.t>,
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
+
// A single topic position of a resolved `where`: either static pre-encoded
|
|
463
|
+
// values, or a marker for "the currently registered addresses of this
|
|
464
|
+
// contract", expanded to topic values when a source query is built.
|
|
465
|
+
type topicFilter =
|
|
466
|
+
| Values(array<EvmTypes.Hex.t>)
|
|
467
|
+
| ContractAddresses({contractName: string})
|
|
468
|
+
|
|
469
|
+
type resolvedTopicSelection = {
|
|
470
|
+
topic0: array<EvmTypes.Hex.t>,
|
|
471
|
+
topic1: topicFilter,
|
|
472
|
+
topic2: topicFilter,
|
|
473
|
+
topic3: topicFilter,
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// The registered `where` fully resolved at registration time for one chain.
|
|
477
|
+
// `topicSelections` is in disjunctive normal form (outer array is OR);
|
|
478
|
+
// an empty array means the `where` returned `false` for this chain.
|
|
479
|
+
type resolvedWhere = {
|
|
480
|
+
topicSelections: array<resolvedTopicSelection>,
|
|
481
|
+
startBlock: option<int>,
|
|
482
|
+
}
|
|
483
|
+
|
|
462
484
|
// Per-event, per-invocation arguments passed to a `where` callback. The
|
|
463
485
|
// concrete `chain` shape (which contract key it exposes) is generated per
|
|
464
486
|
// event in user-project codegen — here it's an open record so codegen'd
|
|
465
487
|
// types subtype-coerce into it cleanly.
|
|
466
488
|
type onEventWhereArgs<'chain> = {chain: 'chain}
|
|
467
489
|
|
|
468
|
-
type eventFilters =
|
|
469
|
-
Static(array<topicSelection>) | Dynamic(array<Address.t> => array<topicSelection>)
|
|
470
|
-
|
|
471
490
|
type evmEventConfig = {
|
|
472
491
|
...eventConfig,
|
|
473
492
|
selectedBlockFields: Utils.Set.t<evmBlockField>,
|
|
@@ -557,7 +576,7 @@ type onEventRegistration = {
|
|
|
557
576
|
|
|
558
577
|
type evmOnEventRegistration = {
|
|
559
578
|
...onEventRegistration,
|
|
560
|
-
|
|
579
|
+
resolvedWhere: resolvedWhere,
|
|
561
580
|
}
|
|
562
581
|
|
|
563
582
|
// Fuel and SVM registrations add no ecosystem-specific fetch state (their
|