envio 3.3.0-alpha.8 → 3.3.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +19 -0
- package/licenses/CLA.md +35 -0
- package/licenses/EULA.md +67 -0
- package/licenses/LICENSE.md +45 -0
- package/licenses/README.md +35 -0
- package/package.json +9 -8
- package/src/Batch.res.mjs +1 -1
- package/src/BatchProcessing.res +0 -7
- package/src/BatchProcessing.res.mjs +1 -8
- package/src/ChainFetching.res +30 -18
- package/src/ChainFetching.res.mjs +23 -13
- package/src/ChainState.res +205 -59
- package/src/ChainState.res.mjs +130 -27
- package/src/ChainState.resi +16 -1
- package/src/Config.res +9 -5
- package/src/Config.res.mjs +2 -2
- package/src/Core.res +20 -0
- package/src/Core.res.mjs +12 -0
- package/src/CrossChainState.res +147 -39
- package/src/CrossChainState.res.mjs +42 -10
- package/src/EffectState.res +100 -0
- package/src/EffectState.res.mjs +74 -0
- package/src/EffectState.resi +32 -0
- package/src/Envio.res +25 -7
- package/src/Envio.res.mjs +15 -19
- package/src/EnvioGlobal.res +53 -0
- package/src/EnvioGlobal.res.mjs +31 -0
- package/src/EventConfigBuilder.res +30 -56
- package/src/EventConfigBuilder.res.mjs +24 -30
- package/src/EventProcessing.res +8 -5
- package/src/EventProcessing.res.mjs +2 -1
- package/src/FetchState.res +842 -474
- package/src/FetchState.res.mjs +545 -356
- package/src/HandlerLoader.res +1 -1
- package/src/HandlerLoader.res.mjs +1 -1
- package/src/HandlerRegister.res +478 -305
- package/src/HandlerRegister.res.mjs +277 -209
- package/src/HandlerRegister.resi +11 -6
- package/src/InMemoryStore.res +14 -26
- package/src/InMemoryStore.res.mjs +6 -19
- package/src/IndexerState.res +15 -39
- package/src/IndexerState.res.mjs +18 -29
- package/src/IndexerState.resi +2 -10
- package/src/Internal.res +121 -18
- package/src/Internal.res.mjs +96 -2
- package/src/LoadLayer.res +44 -24
- package/src/LoadLayer.res.mjs +43 -20
- package/src/LoadLayer.resi +1 -0
- package/src/LogSelection.res +92 -217
- package/src/LogSelection.res.mjs +95 -184
- package/src/Main.res +29 -143
- package/src/Main.res.mjs +25 -88
- package/src/Metrics.res +1 -1
- package/src/Metrics.res.mjs +1 -1
- package/src/Persistence.res +12 -3
- package/src/PgStorage.res +155 -82
- package/src/PgStorage.res.mjs +130 -77
- package/src/Prometheus.res +20 -10
- package/src/Prometheus.res.mjs +22 -10
- package/src/PruneStaleHistory.res +146 -35
- package/src/PruneStaleHistory.res.mjs +114 -20
- package/src/RawEvent.res +2 -2
- package/src/Rollback.res +32 -13
- package/src/Rollback.res.mjs +24 -11
- package/src/RollbackCommit.res +4 -1
- package/src/RollbackCommit.res.mjs +3 -2
- package/src/SimulateDeadInputTracker.res +1 -1
- package/src/SimulateItems.res +39 -7
- package/src/SimulateItems.res.mjs +28 -9
- package/src/TestIndexer.res +2 -2
- package/src/TestIndexer.res.mjs +2 -2
- package/src/TopicFilter.res +1 -25
- package/src/TopicFilter.res.mjs +0 -74
- package/src/UserContext.res +62 -23
- package/src/UserContext.res.mjs +26 -6
- package/src/Writing.res +60 -21
- package/src/Writing.res.mjs +27 -9
- package/src/bindings/NodeJs.res +5 -0
- package/src/bindings/Viem.res +0 -5
- package/src/sources/EventRouter.res +0 -21
- package/src/sources/EventRouter.res.mjs +0 -12
- package/src/sources/Evm.res +4 -1
- package/src/sources/Evm.res.mjs +1 -1
- package/src/sources/EvmChain.res +2 -27
- package/src/sources/EvmChain.res.mjs +2 -23
- package/src/sources/EvmRpcClient.res +58 -23
- package/src/sources/EvmRpcClient.res.mjs +11 -5
- package/src/sources/Fuel.res +3 -1
- package/src/sources/Fuel.res.mjs +1 -1
- package/src/sources/HyperSync.res +9 -38
- package/src/sources/HyperSync.res.mjs +16 -28
- package/src/sources/HyperSync.resi +2 -2
- package/src/sources/HyperSyncClient.res +88 -11
- package/src/sources/HyperSyncClient.res.mjs +39 -6
- package/src/sources/HyperSyncSource.res +18 -210
- package/src/sources/HyperSyncSource.res.mjs +9 -137
- package/src/sources/Rpc.res +0 -32
- package/src/sources/Rpc.res.mjs +1 -46
- package/src/sources/RpcSource.res +129 -533
- package/src/sources/RpcSource.res.mjs +161 -379
- package/src/sources/SimulateSource.res +37 -19
- package/src/sources/SimulateSource.res.mjs +27 -10
- package/src/sources/SourceManager.res +4 -14
- package/src/sources/SourceManager.res.mjs +3 -9
- package/src/sources/SourceManager.resi +0 -2
- package/src/sources/SvmHyperSyncSource.res +13 -3
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
package/src/LogSelection.res
CHANGED
|
@@ -1,76 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
topic1,
|
|
9
|
-
topic2,
|
|
10
|
-
topic3,
|
|
11
|
-
}->Ok
|
|
1
|
+
// Expand a resolved topic selection into concrete topic values for a query:
|
|
2
|
+
// `ContractAddresses` markers become the given partition addresses encoded as
|
|
3
|
+
// topics; `Values` pass through.
|
|
4
|
+
let materializeTopicFilter = (filter: Internal.topicFilter, ~addresses: array<Address.t>) =>
|
|
5
|
+
switch filter {
|
|
6
|
+
| Values(values) => values
|
|
7
|
+
| ContractAddresses(_) => addresses->Array.map(TopicFilter.fromAddress)
|
|
12
8
|
}
|
|
13
9
|
|
|
14
|
-
let
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
let topic0sOfSelectionsWithoutFilters = []
|
|
24
|
-
|
|
25
|
-
let selectionsWithFilters = []
|
|
26
|
-
|
|
27
|
-
topicSelections->Array.forEach(selection => {
|
|
28
|
-
if selection->hasFilters {
|
|
29
|
-
selectionsWithFilters->Array.push(selection)->ignore
|
|
30
|
-
} else {
|
|
31
|
-
selection.topic0->Array.forEach(topic0 => {
|
|
32
|
-
topic0sOfSelectionsWithoutFilters->Array.push(topic0)->ignore
|
|
33
|
-
})
|
|
34
|
-
}
|
|
10
|
+
let materializeTopicSelections = (
|
|
11
|
+
topicSelections: array<Internal.resolvedTopicSelection>,
|
|
12
|
+
~addresses: array<Address.t>,
|
|
13
|
+
): array<Internal.topicSelection> =>
|
|
14
|
+
topicSelections->Array.map(({topic0, topic1, topic2, topic3}): Internal.topicSelection => {
|
|
15
|
+
topic0,
|
|
16
|
+
topic1: topic1->materializeTopicFilter(~addresses),
|
|
17
|
+
topic2: topic2->materializeTopicFilter(~addresses),
|
|
18
|
+
topic3: topic3->materializeTopicFilter(~addresses),
|
|
35
19
|
})
|
|
36
20
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| topic0 =>
|
|
40
|
-
let selectionWithoutFilters = {
|
|
41
|
-
Internal.topic0,
|
|
42
|
-
topic1: [],
|
|
43
|
-
topic2: [],
|
|
44
|
-
topic3: [],
|
|
45
|
-
}
|
|
46
|
-
Array.concat([selectionWithoutFilters], selectionsWithFilters)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
type t = {
|
|
51
|
-
addresses: array<Address.t>,
|
|
52
|
-
topicSelections: array<Internal.topicSelection>,
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let make = (~addresses, ~topicSelections) => {
|
|
56
|
-
let topicSelections = compressTopicSelections(topicSelections)
|
|
57
|
-
{addresses, topicSelections}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
type parsedEventFilters = {
|
|
61
|
-
getEventFiltersOrThrow: ChainMap.Chain.t => Internal.eventFilters,
|
|
21
|
+
type parsedWhere = {
|
|
22
|
+
resolvedWhere: Internal.resolvedWhere,
|
|
62
23
|
filterByAddresses: bool,
|
|
63
24
|
// Indexed params filtered by `chain.<Contract>.addresses`, in disjunctive
|
|
64
25
|
// normal form (outer array OR of AND-groups). Empty unless `filterByAddresses`.
|
|
65
26
|
// Consumed by the codegen of the event's `clientAddressFilter`.
|
|
66
27
|
addressFilterParamGroups: array<array<string>>,
|
|
67
|
-
// `_gte` from the top-level `block` filter of the user's `where`,
|
|
68
|
-
// resolved at build time (per-chain via the `probeChainId`). The
|
|
69
|
-
// caller uses this to override the per-event `startBlock` — a
|
|
70
|
-
// `where`-derived startBlock always wins over contract-level
|
|
71
|
-
// config, so users can widen or narrow individual event ranges
|
|
72
|
-
// without touching `config.yaml`.
|
|
73
|
-
startBlock: option<int>,
|
|
74
28
|
}
|
|
75
29
|
|
|
76
30
|
// Inner schema for the event `block` filter chunk: `{_gte?}`.
|
|
@@ -123,24 +77,12 @@ let extractStartBlock = (
|
|
|
123
77
|
}
|
|
124
78
|
}
|
|
125
79
|
|
|
126
|
-
// Build the
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
let makeChainArg = (
|
|
132
|
-
let chainObj = Dict.make()
|
|
133
|
-
chainObj->Dict.set("id", chainId->Obj.magic)
|
|
134
|
-
chainObj->Dict.set(contractName, {"addresses": addresses}->Obj.magic)
|
|
135
|
-
chainObj
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Build the detection-time `chain` argument. `chain.<ContractName>.addresses`
|
|
139
|
-
// is a getter so the runtime can tell whether the callback actually reads
|
|
140
|
-
// it; the contract sub-object itself is built via `defineProperty` only
|
|
141
|
-
// because its `addresses` field needs the getter — the enclosing chainObj
|
|
142
|
-
// is a plain JS object.
|
|
143
|
-
let makeDetectionChainArg = (
|
|
80
|
+
// Build the `chain` argument passed into a `where` callback.
|
|
81
|
+
// `chain.<ContractName>.addresses` is a getter so the runtime can tell
|
|
82
|
+
// whether the callback actually reads it; the contract sub-object itself is
|
|
83
|
+
// built via `defineProperty` only because its `addresses` field needs the
|
|
84
|
+
// getter — the enclosing chainObj is a plain JS object.
|
|
85
|
+
let makeChainArg = (
|
|
144
86
|
~contractName: string,
|
|
145
87
|
~chainId: int,
|
|
146
88
|
~getAddresses: unit => array<Address.t>,
|
|
@@ -155,7 +97,7 @@ let makeDetectionChainArg = (
|
|
|
155
97
|
chainObj
|
|
156
98
|
}
|
|
157
99
|
|
|
158
|
-
// Sentinel returned by `chain.<Contract>.addresses
|
|
100
|
+
// Sentinel returned by `chain.<Contract>.addresses`. A Proxy
|
|
159
101
|
// whose traps throw on any access, so the only non-throwing use is passing it
|
|
160
102
|
// straight through as a param filter value — which `extractAddressFilterGroups`
|
|
161
103
|
// then finds by identity. Misuse (spread/map/index/...) fails loud at the site.
|
|
@@ -173,78 +115,46 @@ let makeAddressesProbe: (
|
|
|
173
115
|
return new Proxy([], {get: trap});
|
|
174
116
|
}`)
|
|
175
117
|
|
|
176
|
-
|
|
177
|
-
// (DNF: object => one AND-group, array => OR of groups), neutralizing each match
|
|
178
|
-
// to `[]` so later passes can't touch the throwing Proxy. Throws when the
|
|
179
|
-
// callback read the addresses but didn't use them as a param filter value, since
|
|
180
|
-
// the caller only invokes this once it knows the addresses were read.
|
|
181
|
-
let extractAddressFilterGroupsOrThrow = (
|
|
182
|
-
result: JSON.t,
|
|
183
|
-
~probe: array<Address.t>,
|
|
184
|
-
~contractName: string,
|
|
185
|
-
): array<array<string>> => {
|
|
186
|
-
let groups = []
|
|
187
|
-
let scanGroup = (paramsObj: dict<JSON.t>) => {
|
|
188
|
-
let names = []
|
|
189
|
-
paramsObj->Utils.Dict.forEachWithKey((value, key) => {
|
|
190
|
-
if value === probe->(Utils.magic: array<Address.t> => JSON.t) {
|
|
191
|
-
names->Array.push(key)->ignore
|
|
192
|
-
paramsObj->Dict.set(key, []->(Utils.magic: array<unknown> => JSON.t))
|
|
193
|
-
}
|
|
194
|
-
})
|
|
195
|
-
if names->Utils.Array.isEmpty->not {
|
|
196
|
-
groups->Array.push(names)->ignore
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
switch result {
|
|
200
|
-
| Object(obj) =>
|
|
201
|
-
switch obj->Dict.get("params") {
|
|
202
|
-
| Some(Object(p)) => scanGroup(p)
|
|
203
|
-
| Some(Array(arr)) =>
|
|
204
|
-
arr->Array.forEach(item =>
|
|
205
|
-
switch item {
|
|
206
|
-
| Object(p) => scanGroup(p)
|
|
207
|
-
| _ => ()
|
|
208
|
-
}
|
|
209
|
-
)
|
|
210
|
-
| _ => ()
|
|
211
|
-
}
|
|
212
|
-
| _ => ()
|
|
213
|
-
}
|
|
214
|
-
if groups->Utils.Array.isEmpty {
|
|
215
|
-
JsError.throwWithMessage(
|
|
216
|
-
`Invalid where configuration for ${contractName}. The callback reads \`chain.${contractName}.addresses\` but doesn't use it as an indexed-param filter value. Use it directly, e.g. { params: { to: chain.${contractName}.addresses } }.`,
|
|
217
|
-
)
|
|
218
|
-
}
|
|
219
|
-
groups
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
let parseEventFiltersOrThrow = {
|
|
118
|
+
let parseWhereOrThrow = {
|
|
223
119
|
let emptyTopics = []
|
|
224
120
|
let noopGetter = _ => emptyTopics
|
|
225
121
|
|
|
226
122
|
(
|
|
227
|
-
~
|
|
123
|
+
~where: option<JSON.t>,
|
|
228
124
|
~sighash,
|
|
229
|
-
~params
|
|
125
|
+
~params: array<string>,
|
|
230
126
|
~contractName: string,
|
|
231
|
-
~
|
|
127
|
+
~chainId: int,
|
|
232
128
|
~onEventBlockFilterSchema: S.t<option<unknown>>,
|
|
233
129
|
~topic1=noopGetter,
|
|
234
130
|
~topic2=noopGetter,
|
|
235
131
|
~topic3=noopGetter,
|
|
236
|
-
):
|
|
237
|
-
let
|
|
238
|
-
let
|
|
239
|
-
let
|
|
132
|
+
): parsedWhere => {
|
|
133
|
+
let addressFilterParamGroups = []
|
|
134
|
+
let readAddresses = ref(false)
|
|
135
|
+
let addressesSentinel = makeAddressesProbe(~contractName)
|
|
136
|
+
let sentinelJson = addressesSentinel->(Utils.magic: array<Address.t> => JSON.t)
|
|
240
137
|
let topic0 = [sighash->EvmTypes.Hex.fromStringUnsafe]
|
|
241
138
|
let default = {
|
|
242
139
|
Internal.topic0,
|
|
243
|
-
topic1: emptyTopics,
|
|
244
|
-
topic2: emptyTopics,
|
|
245
|
-
topic3: emptyTopics,
|
|
140
|
+
topic1: Values(emptyTopics),
|
|
141
|
+
topic2: Values(emptyTopics),
|
|
142
|
+
topic3: Values(emptyTopics),
|
|
246
143
|
}
|
|
247
144
|
|
|
145
|
+
// Topic positions map 1:1 onto the event's indexed params in declared
|
|
146
|
+
// order, so the sentinel check keys off `params[index]`. The sentinel must
|
|
147
|
+
// be detected before the topic getter runs — the getter would otherwise
|
|
148
|
+
// touch the throwing Proxy while encoding.
|
|
149
|
+
let topicFilterAt = (paramsFilter: dict<JSON.t>, ~index, ~getter) =>
|
|
150
|
+
switch params
|
|
151
|
+
->Array.get(index)
|
|
152
|
+
->Option.flatMap(name => paramsFilter->Utils.Dict.dangerouslyGetNonOption(name)) {
|
|
153
|
+
| Some(value) if value === sentinelJson =>
|
|
154
|
+
Internal.ContractAddresses({contractName: contractName})
|
|
155
|
+
| _ => Values(getter(paramsFilter))
|
|
156
|
+
}
|
|
157
|
+
|
|
248
158
|
// Build a single topic selection from one indexed-param record (the
|
|
249
159
|
// inside of `params`). Validates that the keys are actual indexed
|
|
250
160
|
// parameters of the event — TS type checking doesn't catch this when
|
|
@@ -253,18 +163,25 @@ let parseEventFiltersOrThrow = {
|
|
|
253
163
|
if paramsFilter->Utils.Dict.isEmpty {
|
|
254
164
|
default
|
|
255
165
|
} else {
|
|
256
|
-
|
|
166
|
+
let sentinelParamNames = []
|
|
167
|
+
paramsFilter->Utils.Dict.forEachWithKey((value, key) => {
|
|
257
168
|
if params->Array.includes(key)->not {
|
|
258
169
|
JsError.throwWithMessage(
|
|
259
170
|
`Invalid where configuration. The event doesn't have an indexed parameter "${key}" and can't use it for filtering`,
|
|
260
171
|
)
|
|
261
172
|
}
|
|
173
|
+
if value === sentinelJson {
|
|
174
|
+
sentinelParamNames->Array.push(key)->ignore
|
|
175
|
+
}
|
|
262
176
|
})
|
|
177
|
+
if sentinelParamNames->Utils.Array.isEmpty->not {
|
|
178
|
+
addressFilterParamGroups->Array.push(sentinelParamNames)->ignore
|
|
179
|
+
}
|
|
263
180
|
{
|
|
264
181
|
Internal.topic0,
|
|
265
|
-
topic1: topic1
|
|
266
|
-
topic2: topic2
|
|
267
|
-
topic3: topic3
|
|
182
|
+
topic1: paramsFilter->topicFilterAt(~index=0, ~getter=topic1),
|
|
183
|
+
topic2: paramsFilter->topicFilterAt(~index=1, ~getter=topic2),
|
|
184
|
+
topic3: paramsFilter->topicFilterAt(~index=2, ~getter=topic3),
|
|
268
185
|
}
|
|
269
186
|
}
|
|
270
187
|
}
|
|
@@ -289,7 +206,7 @@ let parseEventFiltersOrThrow = {
|
|
|
289
206
|
//
|
|
290
207
|
// The runtime accepts both the function form (the only form ReScript
|
|
291
208
|
// exposes) and a top-level static object form (TypeScript convenience).
|
|
292
|
-
let parse = (where: JSON.t): array<Internal.
|
|
209
|
+
let parse = (where: JSON.t): array<Internal.resolvedTopicSelection> => {
|
|
293
210
|
if where === Obj.magic(true) {
|
|
294
211
|
[default]
|
|
295
212
|
} else if where === Obj.magic(false) {
|
|
@@ -335,84 +252,42 @@ let parseEventFiltersOrThrow = {
|
|
|
335
252
|
}
|
|
336
253
|
}
|
|
337
254
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
255
|
+
// The callback is invoked exactly once per chain, at registration time,
|
|
256
|
+
// with the chain's real configured id. `chain.<Contract>.addresses` yields
|
|
257
|
+
// the throwing-Proxy sentinel, which the param parser turns into a
|
|
258
|
+
// `ContractAddresses` marker; addresses are expanded from the marker only
|
|
259
|
+
// when a source query is built.
|
|
260
|
+
// A misused Proxy (or any throw from the callback) propagates as-is —
|
|
261
|
+
// the Proxy's guidance message surfaces without wrapping.
|
|
262
|
+
let (topicSelections, startBlock) = switch where {
|
|
263
|
+
| None => ([default], None)
|
|
264
|
+
| Some(where) =>
|
|
265
|
+
let whereValue = if typeof(where) === #function {
|
|
266
|
+
let fn = where->(Utils.magic: JSON.t => Internal.onEventWhereArgs<_> => JSON.t)
|
|
267
|
+
let chain = makeChainArg(~contractName, ~chainId, ~getAddresses=() => {
|
|
268
|
+
readAddresses := true
|
|
269
|
+
addressesSentinel
|
|
270
|
+
})
|
|
271
|
+
fn({chain: chain->Obj.magic})
|
|
272
|
+
} else {
|
|
273
|
+
where
|
|
342
274
|
}
|
|
343
|
-
|
|
344
|
-
if
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
// a detection chain arg whose `chain.<ContractName>.addresses` getter
|
|
348
|
-
// flips a flag. The probe uses this chain's real configured id, so
|
|
349
|
-
// handlers that branch on `chain.id` are exercised along the path
|
|
350
|
-
// they take for this chain. Event configs are built per-chain, so
|
|
351
|
-
// each chain gets a `filterByAddresses` verdict that matches its
|
|
352
|
-
// own callback behaviour.
|
|
353
|
-
//
|
|
354
|
-
// The probe result is also reused to extract the per-event
|
|
355
|
-
// `startBlock` (from `where.block`) for this chain — a second
|
|
356
|
-
// invocation would risk observing different state for callbacks
|
|
357
|
-
// that close over mutable references.
|
|
358
|
-
// A misused Proxy (or any throw from the callback) propagates as-is —
|
|
359
|
-
// the Proxy's guidance message surfaces without wrapping.
|
|
360
|
-
let addressesProbe = makeAddressesProbe(~contractName)
|
|
361
|
-
let chain = makeDetectionChainArg(
|
|
362
|
-
~contractName,
|
|
363
|
-
~chainId=probeChainId,
|
|
364
|
-
~getAddresses=() => {
|
|
365
|
-
filterByAddresses := true
|
|
366
|
-
addressesProbe
|
|
367
|
-
},
|
|
275
|
+
let topicSelections = whereValue->parse
|
|
276
|
+
if readAddresses.contents && addressFilterParamGroups->Utils.Array.isEmpty {
|
|
277
|
+
JsError.throwWithMessage(
|
|
278
|
+
`Invalid where configuration for ${contractName}. The callback reads \`chain.${contractName}.addresses\` but doesn't use it as an indexed-param filter value. Use it directly, e.g. { params: { to: chain.${contractName}.addresses } }.`,
|
|
368
279
|
)
|
|
369
|
-
let probedResult = fn({chain: chain->Obj.magic})
|
|
370
|
-
if filterByAddresses.contents {
|
|
371
|
-
addressFilterParamGroups :=
|
|
372
|
-
extractAddressFilterGroupsOrThrow(probedResult, ~probe=addressesProbe, ~contractName)
|
|
373
|
-
}
|
|
374
|
-
startBlock := extractStartBlock(~onEventBlockFilterSchema, ~contractName, probedResult)
|
|
375
|
-
if filterByAddresses.contents {
|
|
376
|
-
chain => Internal.Dynamic(
|
|
377
|
-
addresses => {
|
|
378
|
-
let chainArg = makeChainArg(
|
|
379
|
-
~contractName,
|
|
380
|
-
~chainId=chain->ChainMap.Chain.toChainId,
|
|
381
|
-
~addresses,
|
|
382
|
-
)
|
|
383
|
-
fn({chain: chainArg->Obj.magic})->parse
|
|
384
|
-
},
|
|
385
|
-
)
|
|
386
|
-
} else {
|
|
387
|
-
// No probed chain referenced the contract — cache as Static
|
|
388
|
-
// per chain to avoid recomputing topic selections each batch.
|
|
389
|
-
// The addresses getter throws: if a code path the probe didn't
|
|
390
|
-
// exercise reads `chain.<Contract>.addresses` at runtime, silent
|
|
391
|
-
// [] would produce wrong topics — throw a user-friendly error
|
|
392
|
-
// instead so the user rewrites the callback to surface the
|
|
393
|
-
// dependency up-front.
|
|
394
|
-
chain => {
|
|
395
|
-
let chainId = chain->ChainMap.Chain.toChainId
|
|
396
|
-
let chainArg = makeDetectionChainArg(~contractName, ~chainId, ~getAddresses=() =>
|
|
397
|
-
JsError.throwWithMessage(
|
|
398
|
-
`Invalid where configuration. Event callback for contract "${contractName}" read \`chain.${contractName}.addresses\` at runtime but the probe didn't detect the access on chainId ${chainId->Int.toString}. Move the \`chain.${contractName}.addresses\` read above any \`chain.id\` branching so the probe picks up the dependency and switches to the dynamic fetch path.`,
|
|
399
|
-
)
|
|
400
|
-
)
|
|
401
|
-
Internal.Static(fn({chain: chainArg->Obj.magic})->parse)
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
} else {
|
|
405
|
-
startBlock := extractStartBlock(~onEventBlockFilterSchema, ~contractName, eventFilters)
|
|
406
|
-
let static: Internal.eventFilters = Static(eventFilters->parse)
|
|
407
|
-
_ => static
|
|
408
280
|
}
|
|
281
|
+
(topicSelections, extractStartBlock(~onEventBlockFilterSchema, ~contractName, whereValue))
|
|
409
282
|
}
|
|
410
283
|
|
|
411
284
|
{
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
285
|
+
resolvedWhere: {
|
|
286
|
+
topicSelections,
|
|
287
|
+
startBlock,
|
|
288
|
+
},
|
|
289
|
+
filterByAddresses: addressFilterParamGroups->Utils.Array.isEmpty->not,
|
|
290
|
+
addressFilterParamGroups,
|
|
416
291
|
}
|
|
417
292
|
}
|
|
418
293
|
}
|