envio 3.5.0-alpha.1 → 3.5.0-alpha.3
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 +27 -0
- package/src/BatchProcessing.res.mjs +12 -0
- package/src/ChainFetching.res +5 -5
- package/src/ChainFetching.res.mjs +3 -4
- package/src/ChainState.res +48 -25
- package/src/ChainState.res.mjs +23 -29
- package/src/ChainState.resi +2 -6
- package/src/Change.res +3 -3
- package/src/Config.res +2 -6
- package/src/Config.res.mjs +2 -12
- package/src/Core.res +3 -0
- package/src/CrossChainState.res +33 -10
- package/src/CrossChainState.res.mjs +21 -6
- package/src/CrossChainState.resi +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 +510 -460
- package/src/FetchState.res.mjs +413 -428
- package/src/FinalizeBackfill.res +34 -0
- package/src/FinalizeBackfill.res.mjs +26 -0
- package/src/HandlerRegister.res +45 -0
- package/src/HandlerRegister.res.mjs +43 -0
- package/src/InMemoryStore.res +11 -4
- package/src/InMemoryTable.res +26 -24
- package/src/InMemoryTable.res.mjs +19 -18
- package/src/IndexerState.res +71 -2
- package/src/IndexerState.res.mjs +67 -3
- package/src/IndexerState.resi +5 -0
- package/src/Internal.res +17 -8
- package/src/LoadLayer.res +9 -1
- package/src/LoadLayer.res.mjs +1 -0
- package/src/LogSelection.res +2 -1
- package/src/Metrics.res +37 -6
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +12 -1
- package/src/PgStorage.res +411 -57
- package/src/PgStorage.res.mjs +262 -41
- package/src/SimulateItems.res +2 -2
- package/src/TestIndexer.res +9 -5
- package/src/TestIndexer.res.mjs +5 -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/EntityFilter.res +0 -4
- package/src/db/EntityFilter.res.mjs +1 -8
- package/src/db/EntityHistory.res +16 -7
- package/src/db/EntityHistory.res.mjs +7 -6
- package/src/db/IndexRegistry.res +219 -0
- package/src/db/IndexRegistry.res.mjs +195 -0
- package/src/db/InternalTable.res +7 -0
- package/src/db/InternalTable.res.mjs +7 -0
- package/src/db/Table.res +53 -22
- package/src/db/Table.res.mjs +56 -18
- package/src/sources/AddressSet.res +48 -0
- package/src/sources/AddressSet.res.mjs +11 -0
- package/src/sources/AddressStore.res +152 -0
- package/src/sources/AddressStore.res.mjs +97 -0
- package/src/sources/EvmChain.res +3 -0
- package/src/sources/EvmChain.res.mjs +4 -2
- package/src/sources/EvmHyperSyncSource.res +6 -3
- package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
- package/src/sources/EvmRpcClient.res +6 -3
- package/src/sources/EvmRpcClient.res.mjs +3 -3
- package/src/sources/FuelHyperSync.res +4 -3
- package/src/sources/FuelHyperSync.res.mjs +3 -3
- package/src/sources/FuelHyperSync.resi +2 -1
- package/src/sources/FuelHyperSyncClient.res +12 -6
- package/src/sources/FuelHyperSyncClient.res.mjs +2 -2
- package/src/sources/FuelHyperSyncSource.res +7 -4
- package/src/sources/FuelHyperSyncSource.res.mjs +3 -3
- package/src/sources/HyperSync.res +6 -4
- package/src/sources/HyperSync.res.mjs +2 -3
- package/src/sources/HyperSync.resi +1 -1
- package/src/sources/HyperSyncClient.res +18 -9
- package/src/sources/HyperSyncClient.res.mjs +4 -4
- package/src/sources/RpcSource.res +15 -10
- package/src/sources/RpcSource.res.mjs +3 -4
- package/src/sources/SimulateSource.res +26 -11
- package/src/sources/SimulateSource.res.mjs +13 -5
- package/src/sources/Source.res +4 -2
- package/src/sources/SourceManager.res +2 -3
- package/src/sources/SourceManager.res.mjs +2 -3
- package/src/sources/Svm.res +1 -2
- package/src/sources/Svm.res.mjs +1 -1
- package/src/sources/SvmHyperSyncClient.res +12 -3
- package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
- package/src/sources/SvmHyperSyncSource.res +10 -5
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
- package/src/IndexingAddresses.res +0 -151
- package/src/IndexingAddresses.res.mjs +0 -130
- package/src/IndexingAddresses.resi +0 -39
package/src/ChainState.resi
CHANGED
|
@@ -9,7 +9,7 @@ let make: (
|
|
|
9
9
|
~chainConfig: Config.chain,
|
|
10
10
|
~fetchState: FetchState.t,
|
|
11
11
|
~onEventRegistrations: array<Internal.onEventRegistration>=?,
|
|
12
|
-
~
|
|
12
|
+
~addressStore: AddressStore.t,
|
|
13
13
|
~sourceManager: SourceManager.t,
|
|
14
14
|
~reorgDetection: ReorgDetection.t,
|
|
15
15
|
~committedProgressBlockNumber: int,
|
|
@@ -104,10 +104,6 @@ let isReady: t => bool
|
|
|
104
104
|
let isFetchingAtHead: t => bool
|
|
105
105
|
let isAtHeadWithoutEndBlock: t => bool
|
|
106
106
|
|
|
107
|
-
// Drop over-fetched events an address-param filter rejects, before contract
|
|
108
|
-
// registration runs on them. Uses the chain's current indexing addresses.
|
|
109
|
-
let filterByClientAddress: (t, array<Internal.item>) => array<Internal.item>
|
|
110
|
-
|
|
111
107
|
// State transitions. The chain state is mutated only through these.
|
|
112
108
|
let handleQueryResult: (
|
|
113
109
|
t,
|
|
@@ -141,7 +137,7 @@ let setEndBlockToFirstEvent: (t, ~blockNumber: int) => unit
|
|
|
141
137
|
let enterReorgThreshold: t => unit
|
|
142
138
|
let advanceAfterBatch: (t, ~batch: Batch.t, ~enteringReorgThreshold: bool) => unit
|
|
143
139
|
let applyBatchProgress: (t, ~batch: Batch.t, ~blockTimestampName: string) => unit
|
|
144
|
-
let markReady: t => unit
|
|
140
|
+
let markReady: (t, ~readyAt: Date.t) => unit
|
|
145
141
|
let rollback: (
|
|
146
142
|
t,
|
|
147
143
|
~newProgressBlockNumber: option<int>,
|
package/src/Change.res
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
@tag("type")
|
|
2
2
|
type t<'entity> =
|
|
3
|
-
| @as("SET") Set({entityId:
|
|
4
|
-
| @as("DELETE") Delete({entityId:
|
|
3
|
+
| @as("SET") Set({entityId: EntityId.t, entity: 'entity, checkpointId: bigint})
|
|
4
|
+
| @as("DELETE") Delete({entityId: EntityId.t, checkpointId: bigint})
|
|
5
5
|
|
|
6
6
|
@get
|
|
7
|
-
external getEntityId: t<'entity> =>
|
|
7
|
+
external getEntityId: t<'entity> => EntityId.t = "entityId"
|
|
8
8
|
@get
|
|
9
9
|
external getCheckpointId: t<'entity> => bigint = "checkpointId"
|
package/src/Config.res
CHANGED
|
@@ -390,10 +390,6 @@ let getFieldTypeAndSchema = (prop, ~enumConfigsByName: dict<Table.enumConfig<Tab
|
|
|
390
390
|
let enumConfig = enumConfigsByName->Dict.get(enumName)->Option.getOrThrow
|
|
391
391
|
(Table.Enum({config: enumConfig}), enumConfig.schema->S.toUnknown)
|
|
392
392
|
}
|
|
393
|
-
| "entity" => {
|
|
394
|
-
let entityName = prop["entity"]->Option.getOrThrow
|
|
395
|
-
(Table.Entity({name: entityName}), S.string->S.toUnknown)
|
|
396
|
-
}
|
|
397
393
|
| other => JsError.throwWithMessage("Unknown field type in entity config: " ++ other)
|
|
398
394
|
}
|
|
399
395
|
|
|
@@ -459,7 +455,7 @@ let parseEntitiesFromJson = (
|
|
|
459
455
|
)
|
|
460
456
|
)
|
|
461
457
|
|
|
462
|
-
let
|
|
458
|
+
let compositeIndexes =
|
|
463
459
|
entityJson["compositeIndices"]
|
|
464
460
|
->Option.getOr([])
|
|
465
461
|
->Array.map(ci =>
|
|
@@ -474,7 +470,7 @@ let parseEntitiesFromJson = (
|
|
|
474
470
|
let table = Table.mkTable(
|
|
475
471
|
entityName,
|
|
476
472
|
~fields=Array.concat(fields, derivedFields),
|
|
477
|
-
~
|
|
473
|
+
~compositeIndexes,
|
|
478
474
|
~description=?entityJson["description"],
|
|
479
475
|
)
|
|
480
476
|
|
package/src/Config.res.mjs
CHANGED
|
@@ -263,16 +263,6 @@ function getFieldTypeAndSchema(prop, enumConfigsByName) {
|
|
|
263
263
|
Utils.Schema.dbDate
|
|
264
264
|
];
|
|
265
265
|
break;
|
|
266
|
-
case "entity" :
|
|
267
|
-
let entityName = Stdlib_Option.getOrThrow(prop.entity, undefined);
|
|
268
|
-
match = [
|
|
269
|
-
{
|
|
270
|
-
type: "Entity",
|
|
271
|
-
name: entityName
|
|
272
|
-
},
|
|
273
|
-
S$RescriptSchema.string
|
|
274
|
-
];
|
|
275
|
-
break;
|
|
276
266
|
case "enum" :
|
|
277
267
|
let enumName = Stdlib_Option.getOrThrow(prop.enum, undefined);
|
|
278
268
|
let enumConfig = Stdlib_Option.getOrThrow(enumConfigsByName[enumName], undefined);
|
|
@@ -341,11 +331,11 @@ function parseEntitiesFromJson(entitiesJson, enumConfigsByName, globalStorage) {
|
|
|
341
331
|
return Table.mkField(prop.name, match[0], match[1], undefined, match[3], match[2], prop.name === "id", match[4], prop.linkedEntity, prop.description, prop.postgresDbName, prop.clickhouseDbName);
|
|
342
332
|
});
|
|
343
333
|
let derivedFields = Stdlib_Option.getOr(entityJson.derivedFields, []).map(df => Table.mkDerivedFromField(df.fieldName, df.derivedFromEntity, df.derivedFromField, df.description));
|
|
344
|
-
let
|
|
334
|
+
let compositeIndexes = Stdlib_Option.getOr(entityJson.compositeIndices, []).map(ci => ci.map(f => ({
|
|
345
335
|
fieldName: f.fieldName,
|
|
346
336
|
direction: f.direction === "Asc" ? "Asc" : "Desc"
|
|
347
337
|
})));
|
|
348
|
-
let table = Table.mkTable(entityName,
|
|
338
|
+
let table = Table.mkTable(entityName, compositeIndexes, fields.concat(derivedFields), entityJson.description);
|
|
349
339
|
let getApiFieldName = prop => {
|
|
350
340
|
let match = prop.linkedEntity;
|
|
351
341
|
if (match !== undefined) {
|
package/src/Core.res
CHANGED
|
@@ -10,6 +10,7 @@ type svmHyperSyncClientCtor
|
|
|
10
10
|
type fuelHyperSyncClientCtor
|
|
11
11
|
type transactionStoreCtor
|
|
12
12
|
type blockStoreCtor
|
|
13
|
+
type addressStoreCtor
|
|
13
14
|
type fromUserApiOptions = {
|
|
14
15
|
schema?: string,
|
|
15
16
|
env?: dict<string>,
|
|
@@ -39,6 +40,8 @@ type addon = {
|
|
|
39
40
|
transactionStore: transactionStoreCtor,
|
|
40
41
|
@as("BlockStore")
|
|
41
42
|
blockStore: blockStoreCtor,
|
|
43
|
+
@as("AddressStore")
|
|
44
|
+
addressStore: addressStoreCtor,
|
|
42
45
|
// Ordered transaction-field names exposed for the field-code contract test
|
|
43
46
|
// (the ReScript `transactionFields` arrays must match the Rust ordinals).
|
|
44
47
|
evmTransactionFieldNames: unit => array<string>,
|
package/src/CrossChainState.res
CHANGED
|
@@ -10,6 +10,10 @@ type t = {
|
|
|
10
10
|
chainIds: array<int>,
|
|
11
11
|
// True once every chain has caught up to head/endBlock. Monotonic during a run.
|
|
12
12
|
mutable isRealtime: bool,
|
|
13
|
+
// True once every chain has caught up and there's nothing left to process,
|
|
14
|
+
// but before the deferred schema indexes and `ready_at` are committed. The
|
|
15
|
+
// gap between this and `isRealtime` is the FinalizingIndexes phase.
|
|
16
|
+
mutable isCaughtUp: bool,
|
|
13
17
|
mutable isInReorgThreshold: bool,
|
|
14
18
|
// Indexer-wide fetch buffer pool (item count), shared across all chains.
|
|
15
19
|
targetBufferSize: int,
|
|
@@ -32,6 +36,7 @@ let make = (
|
|
|
32
36
|
chainStates,
|
|
33
37
|
chainIds: chainStates->Dict.valuesToArray->Array.map(cs => (cs->ChainState.chainConfig).id),
|
|
34
38
|
isRealtime,
|
|
39
|
+
isCaughtUp: isRealtime,
|
|
35
40
|
isInReorgThreshold,
|
|
36
41
|
targetBufferSize,
|
|
37
42
|
}
|
|
@@ -46,6 +51,7 @@ let getChainState = (crossChainState: t, chainId) =>
|
|
|
46
51
|
|
|
47
52
|
let chainStates = (crossChainState: t) => crossChainState.chainStates
|
|
48
53
|
let isRealtime = (crossChainState: t) => crossChainState.isRealtime
|
|
54
|
+
let isCaughtUp = (crossChainState: t) => crossChainState.isCaughtUp
|
|
49
55
|
let isInReorgThreshold = (crossChainState: t) => crossChainState.isInReorgThreshold
|
|
50
56
|
let targetBufferSize = (crossChainState: t) => crossChainState.targetBufferSize
|
|
51
57
|
|
|
@@ -129,10 +135,11 @@ let enterReorgThreshold = (crossChainState: t) => {
|
|
|
129
135
|
crossChainState.isInReorgThreshold = true
|
|
130
136
|
}
|
|
131
137
|
|
|
132
|
-
// Commit each progressed chain's batch progress, then
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
138
|
+
// Commit each progressed chain's batch progress, then record whether the whole
|
|
139
|
+
// indexer has caught up (every chain reached endblock or processed to head,
|
|
140
|
+
// with no processable events left). Catching up doesn't make the indexer ready:
|
|
141
|
+
// the deferred schema indexes still have to be created, which `markReady`
|
|
142
|
+
// below concludes once they're committed.
|
|
136
143
|
let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampName: string) => {
|
|
137
144
|
let chainIds = crossChainState.chainIds
|
|
138
145
|
|
|
@@ -145,15 +152,14 @@ let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampNa
|
|
|
145
152
|
}
|
|
146
153
|
}
|
|
147
154
|
|
|
148
|
-
|
|
155
|
+
crossChainState.isCaughtUp =
|
|
156
|
+
crossChainState.isCaughtUp || (crossChainState->nextItemIsNone && everyChainCaughtUp.contents)
|
|
149
157
|
|
|
158
|
+
// A run resumed with every chain already stamped ready needs no finalize —
|
|
159
|
+
// the indexes were committed together with those stamps.
|
|
150
160
|
let allChainsReady = ref(true)
|
|
151
161
|
for i in 0 to chainIds->Array.length - 1 {
|
|
152
|
-
|
|
153
|
-
if indexerCaughtUp {
|
|
154
|
-
cs->ChainState.markReady
|
|
155
|
-
}
|
|
156
|
-
if !(cs->ChainState.isReady) {
|
|
162
|
+
if !(crossChainState->getChainState(chainIds->Array.getUnsafe(i))->ChainState.isReady) {
|
|
157
163
|
allChainsReady := false
|
|
158
164
|
}
|
|
159
165
|
}
|
|
@@ -161,6 +167,23 @@ let applyBatchProgress = (crossChainState: t, ~batch: Batch.t, ~blockTimestampNa
|
|
|
161
167
|
crossChainState.isRealtime = crossChainState.isRealtime || allChainsReady.contents
|
|
162
168
|
}
|
|
163
169
|
|
|
170
|
+
// Enter the FinalizingIndexes phase without a batch. Used when the loop finds
|
|
171
|
+
// every chain already processed to its endblock — a resume that has nothing
|
|
172
|
+
// left to do still owes the schema its deferred indexes before it reports ready.
|
|
173
|
+
let markCaughtUp = (crossChainState: t) => crossChainState.isCaughtUp = true
|
|
174
|
+
|
|
175
|
+
// Concludes the FinalizingIndexes phase: stamps every chain with the `ready_at`
|
|
176
|
+
// already committed alongside the deferred schema indexes and switches the
|
|
177
|
+
// indexer to realtime.
|
|
178
|
+
let markReady = (crossChainState: t, ~readyAt) => {
|
|
179
|
+
for i in 0 to crossChainState.chainIds->Array.length - 1 {
|
|
180
|
+
crossChainState
|
|
181
|
+
->getChainState(crossChainState.chainIds->Array.getUnsafe(i))
|
|
182
|
+
->ChainState.markReady(~readyAt)
|
|
183
|
+
}
|
|
184
|
+
crossChainState.isRealtime = true
|
|
185
|
+
}
|
|
186
|
+
|
|
164
187
|
// --- Fetch control. ---
|
|
165
188
|
|
|
166
189
|
// Chains ordered furthest-behind first by fetch-frontier progress, so the
|
|
@@ -27,6 +27,7 @@ function make(chainStates, isInReorgThreshold, isRealtime, targetBufferSizeOpt)
|
|
|
27
27
|
chainStates: chainStates,
|
|
28
28
|
chainIds: Object.values(chainStates).map(cs => ChainState.chainConfig(cs).id),
|
|
29
29
|
isRealtime: isRealtime,
|
|
30
|
+
isCaughtUp: isRealtime,
|
|
30
31
|
isInReorgThreshold: isInReorgThreshold,
|
|
31
32
|
targetBufferSize: targetBufferSize
|
|
32
33
|
};
|
|
@@ -40,6 +41,10 @@ function isRealtime(crossChainState) {
|
|
|
40
41
|
return crossChainState.isRealtime;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
function isCaughtUp(crossChainState) {
|
|
45
|
+
return crossChainState.isCaughtUp;
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
function isInReorgThreshold(crossChainState) {
|
|
44
49
|
return crossChainState.isInReorgThreshold;
|
|
45
50
|
}
|
|
@@ -104,20 +109,27 @@ function applyBatchProgress(crossChainState, batch, blockTimestampName) {
|
|
|
104
109
|
everyChainCaughtUp = false;
|
|
105
110
|
}
|
|
106
111
|
}
|
|
107
|
-
|
|
112
|
+
crossChainState.isCaughtUp = crossChainState.isCaughtUp || nextItemIsNone(crossChainState) && everyChainCaughtUp;
|
|
108
113
|
let allChainsReady = true;
|
|
109
114
|
for (let i$1 = 0, i_finish$1 = chainIds.length; i$1 < i_finish$1; ++i$1) {
|
|
110
|
-
|
|
111
|
-
if (indexerCaughtUp) {
|
|
112
|
-
ChainState.markReady(cs$1);
|
|
113
|
-
}
|
|
114
|
-
if (!ChainState.isReady(cs$1)) {
|
|
115
|
+
if (!ChainState.isReady(crossChainState.chainStates[chainIds[i$1]])) {
|
|
115
116
|
allChainsReady = false;
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
crossChainState.isRealtime = crossChainState.isRealtime || allChainsReady;
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
function markCaughtUp(crossChainState) {
|
|
123
|
+
crossChainState.isCaughtUp = true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function markReady(crossChainState, readyAt) {
|
|
127
|
+
for (let i = 0, i_finish = crossChainState.chainIds.length; i < i_finish; ++i) {
|
|
128
|
+
ChainState.markReady(crossChainState.chainStates[crossChainState.chainIds[i]], readyAt);
|
|
129
|
+
}
|
|
130
|
+
crossChainState.isRealtime = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
121
133
|
function priorityOrder(crossChainState) {
|
|
122
134
|
return Object.values(crossChainState.chainStates).toSorted((a, b) => Primitive_float.compare(ChainState.frontierProgress(a), ChainState.frontierProgress(b)));
|
|
123
135
|
}
|
|
@@ -222,6 +234,7 @@ export {
|
|
|
222
234
|
make,
|
|
223
235
|
chainStates,
|
|
224
236
|
isRealtime,
|
|
237
|
+
isCaughtUp,
|
|
225
238
|
isInReorgThreshold,
|
|
226
239
|
targetBufferSize,
|
|
227
240
|
nextItemIsNone,
|
|
@@ -229,6 +242,8 @@ export {
|
|
|
229
242
|
createBatch,
|
|
230
243
|
enterReorgThreshold,
|
|
231
244
|
applyBatchProgress,
|
|
245
|
+
markCaughtUp,
|
|
246
|
+
markReady,
|
|
232
247
|
priorityOrder,
|
|
233
248
|
checkAndFetch,
|
|
234
249
|
}
|
package/src/CrossChainState.resi
CHANGED
|
@@ -15,6 +15,7 @@ let make: (
|
|
|
15
15
|
// Accessors.
|
|
16
16
|
let chainStates: t => dict<ChainState.t>
|
|
17
17
|
let isRealtime: t => bool
|
|
18
|
+
let isCaughtUp: t => bool
|
|
18
19
|
let isInReorgThreshold: t => bool
|
|
19
20
|
let targetBufferSize: t => int
|
|
20
21
|
|
|
@@ -31,6 +32,8 @@ let createBatch: (
|
|
|
31
32
|
) => Batch.t
|
|
32
33
|
let enterReorgThreshold: t => unit
|
|
33
34
|
let applyBatchProgress: (t, ~batch: Batch.t, ~blockTimestampName: string) => unit
|
|
35
|
+
let markCaughtUp: t => unit
|
|
36
|
+
let markReady: (t, ~readyAt: Date.t) => unit
|
|
34
37
|
|
|
35
38
|
// Fetch control.
|
|
36
39
|
let priorityOrder: t => array<ChainState.t>
|
package/src/EntityId.res
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Opaque representation of an entity id. The generated per-entity types expose
|
|
2
|
+
// the real scalar (string / int / bigint); the generic storage and in-memory
|
|
3
|
+
// layers work with any entity's id through this type without knowing which
|
|
4
|
+
// scalar backs it. The runtime value is always the real id, never a stringified
|
|
5
|
+
// form — `toKey` derives the string only where a JS object/dict key is needed.
|
|
6
|
+
type t
|
|
7
|
+
|
|
8
|
+
external unsafeOfAny: 'a => t = "%identity"
|
|
9
|
+
external unsafeToAny: t => 'a = "%identity"
|
|
10
|
+
external unsafeOfString: string => t = "%identity"
|
|
11
|
+
|
|
12
|
+
// Stringified id used as a JS object/dict key. `String` matches how JS coerces
|
|
13
|
+
// a value used as an object key, so an id indexes the same whether the raw
|
|
14
|
+
// value or its key form is used for lookup, across string/int/bigint.
|
|
15
|
+
let toKey: t => string = %raw(`String`)
|
|
@@ -292,61 +292,6 @@ let resolveFieldSelection = (
|
|
|
292
292
|
)
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
// ============== Client-side address filter ==============
|
|
296
|
-
|
|
297
|
-
let compileAddressFilter: string => (
|
|
298
|
-
Internal.eventPayload,
|
|
299
|
-
int,
|
|
300
|
-
dict<Internal.indexingContract>,
|
|
301
|
-
) => bool = %raw(`function (body) {
|
|
302
|
-
return new Function("event", "blockNumber", "indexingAddresses", body);
|
|
303
|
-
}`)
|
|
304
|
-
|
|
305
|
-
// Body of the client-side address filter. Two analogous registered-at-or-before
|
|
306
|
-
// checks, ANDed: (1) for non-wildcard events, the log's srcAddress must itself be
|
|
307
|
-
// registered (ownership is resolved structurally by partition, but the temporal
|
|
308
|
-
// `effectiveStartBlock` gate lives here now); (2) a DNF of address-filtered param
|
|
309
|
-
// names (OR of AND-groups) for events that filter an indexed address param. The
|
|
310
|
-
// DNF is fixed here, so it's unrolled into one boolean expression — no per-event
|
|
311
|
-
// closure, loop, or array. `None` only for wildcard events without a param
|
|
312
|
-
// filter. Exposed for snapshotting.
|
|
313
|
-
// `srcAddressExpr` is the JS expression for the event's owning address: EVM and
|
|
314
|
-
// Fuel events expose `event.srcAddress`; SVM instructions expose `event.programId`.
|
|
315
|
-
let buildAddressFilterBody = (
|
|
316
|
-
groups: array<array<string>>,
|
|
317
|
-
~isWildcard: bool,
|
|
318
|
-
~srcAddressExpr: string="event.srcAddress",
|
|
319
|
-
): option<string> => {
|
|
320
|
-
let paramLeaf = name =>
|
|
321
|
-
`(ic = indexingAddresses[p[${JSON.stringify(
|
|
322
|
-
JSON.String(name),
|
|
323
|
-
)}]]) !== undefined && ic.effectiveStartBlock <= blockNumber`
|
|
324
|
-
let paramDnf = switch groups {
|
|
325
|
-
| [] => None
|
|
326
|
-
| _ =>
|
|
327
|
-
Some(
|
|
328
|
-
groups
|
|
329
|
-
->Array.map(group => "(" ++ group->Array.map(paramLeaf)->Array.join(" && ") ++ ")")
|
|
330
|
-
->Array.join(" || "),
|
|
331
|
-
)
|
|
332
|
-
}
|
|
333
|
-
let srcLeaf = `(ic = indexingAddresses[${srcAddressExpr}]) !== undefined && ic.effectiveStartBlock <= blockNumber`
|
|
334
|
-
switch (isWildcard, paramDnf) {
|
|
335
|
-
| (true, None) => None
|
|
336
|
-
| (true, Some(dnf)) => Some("var p = event.params, ic; return " ++ dnf ++ ";")
|
|
337
|
-
| (false, None) => Some("var ic; return " ++ srcLeaf ++ ";")
|
|
338
|
-
| (false, Some(dnf)) =>
|
|
339
|
-
Some("var p = event.params, ic; return " ++ srcLeaf ++ " && (" ++ dnf ++ ");")
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
let buildAddressFilter = (
|
|
344
|
-
groups: array<array<string>>,
|
|
345
|
-
~isWildcard: bool,
|
|
346
|
-
~srcAddressExpr: string="event.srcAddress",
|
|
347
|
-
): option<(Internal.eventPayload, int, dict<Internal.indexingContract>) => bool> =>
|
|
348
|
-
buildAddressFilterBody(groups, ~isWildcard, ~srcAddressExpr)->Option.map(compileAddressFilter)
|
|
349
|
-
|
|
350
295
|
// ============== Build complete EVM event config ==============
|
|
351
296
|
|
|
352
297
|
let buildEvmEventConfig = (
|
|
@@ -431,7 +376,7 @@ let buildEvmOnEventRegistration = (
|
|
|
431
376
|
contractRegister,
|
|
432
377
|
resolvedWhere,
|
|
433
378
|
filterByAddresses,
|
|
434
|
-
|
|
379
|
+
addressFilterParamGroups,
|
|
435
380
|
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses),
|
|
436
381
|
startBlock: resolvedStartBlock,
|
|
437
382
|
}
|
|
@@ -516,7 +461,6 @@ let buildSvmOnEventRegistration = (
|
|
|
516
461
|
isWildcard,
|
|
517
462
|
filterByAddresses: false,
|
|
518
463
|
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
|
|
519
|
-
clientAddressFilter: ?buildAddressFilter([], ~isWildcard, ~srcAddressExpr="event.programId"),
|
|
520
464
|
startBlock,
|
|
521
465
|
}
|
|
522
466
|
|
|
@@ -594,6 +538,5 @@ let buildFuelOnEventRegistration = (
|
|
|
594
538
|
isWildcard,
|
|
595
539
|
filterByAddresses: false,
|
|
596
540
|
dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses=false),
|
|
597
|
-
clientAddressFilter: ?buildAddressFilter([], ~isWildcard),
|
|
598
541
|
startBlock,
|
|
599
542
|
}
|
|
@@ -243,33 +243,6 @@ function resolveFieldSelection(blockFields, transactionFields, globalBlockFields
|
|
|
243
243
|
];
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
let compileAddressFilter = (function (body) {
|
|
247
|
-
return new Function("event", "blockNumber", "indexingAddresses", body);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
function buildAddressFilterBody(groups, isWildcard, srcAddressExprOpt) {
|
|
251
|
-
let srcAddressExpr = srcAddressExprOpt !== undefined ? srcAddressExprOpt : "event.srcAddress";
|
|
252
|
-
let paramLeaf = name => `(ic = indexingAddresses[p[` + JSON.stringify(name) + `]]) !== undefined && ic.effectiveStartBlock <= blockNumber`;
|
|
253
|
-
let paramDnf = groups.length !== 0 ? groups.map(group => "(" + group.map(paramLeaf).join(" && ") + ")").join(" || ") : undefined;
|
|
254
|
-
let srcLeaf = `(ic = indexingAddresses[` + srcAddressExpr + `]) !== undefined && ic.effectiveStartBlock <= blockNumber`;
|
|
255
|
-
if (isWildcard) {
|
|
256
|
-
if (paramDnf !== undefined) {
|
|
257
|
-
return "var p = event.params, ic; return " + paramDnf + ";";
|
|
258
|
-
} else {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
} else if (paramDnf !== undefined) {
|
|
262
|
-
return "var p = event.params, ic; return " + srcLeaf + " && (" + paramDnf + ");";
|
|
263
|
-
} else {
|
|
264
|
-
return "var ic; return " + srcLeaf + ";";
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function buildAddressFilter(groups, isWildcard, srcAddressExprOpt) {
|
|
269
|
-
let srcAddressExpr = srcAddressExprOpt !== undefined ? srcAddressExprOpt : "event.srcAddress";
|
|
270
|
-
return Stdlib_Option.map(buildAddressFilterBody(groups, isWildcard, srcAddressExpr), compileAddressFilter);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
246
|
function buildEvmEventConfig(contractName, eventName, sighash, params, blockFields, transactionFields, globalBlockFieldsSetOpt, globalTransactionFieldsSetOpt) {
|
|
274
247
|
let globalBlockFieldsSet = globalBlockFieldsSetOpt !== undefined ? Primitive_option.valFromOption(globalBlockFieldsSetOpt) : new Set();
|
|
275
248
|
let globalTransactionFieldsSet = globalTransactionFieldsSetOpt !== undefined ? Primitive_option.valFromOption(globalTransactionFieldsSetOpt) : new Set();
|
|
@@ -314,7 +287,7 @@ function buildEvmOnEventRegistration(eventConfig, isWildcard, handler, contractR
|
|
|
314
287
|
isWildcard: isWildcard,
|
|
315
288
|
filterByAddresses: filterByAddresses,
|
|
316
289
|
dependsOnAddresses: Internal.dependsOnAddresses(isWildcard, filterByAddresses),
|
|
317
|
-
|
|
290
|
+
addressFilterParamGroups: match.addressFilterParamGroups,
|
|
318
291
|
startBlock: resolvedStartBlock,
|
|
319
292
|
resolvedWhere: resolvedWhere
|
|
320
293
|
};
|
|
@@ -367,7 +340,6 @@ function buildSvmOnEventRegistration(eventConfig, isWildcard, handler, contractR
|
|
|
367
340
|
isWildcard: isWildcard,
|
|
368
341
|
filterByAddresses: false,
|
|
369
342
|
dependsOnAddresses: Internal.dependsOnAddresses(isWildcard, false),
|
|
370
|
-
clientAddressFilter: buildAddressFilter([], isWildcard, "event.programId"),
|
|
371
343
|
startBlock: startBlock
|
|
372
344
|
};
|
|
373
345
|
}
|
|
@@ -437,7 +409,6 @@ function buildFuelOnEventRegistration(eventConfig, isWildcard, handler, contract
|
|
|
437
409
|
isWildcard: isWildcard,
|
|
438
410
|
filterByAddresses: false,
|
|
439
411
|
dependsOnAddresses: Internal.dependsOnAddresses(isWildcard, false),
|
|
440
|
-
clientAddressFilter: buildAddressFilter([], isWildcard, undefined),
|
|
441
412
|
startBlock: startBlock
|
|
442
413
|
};
|
|
443
414
|
}
|
|
@@ -457,9 +428,6 @@ export {
|
|
|
457
428
|
buildTopicGetter,
|
|
458
429
|
alwaysIncludedBlockFields,
|
|
459
430
|
resolveFieldSelection,
|
|
460
|
-
compileAddressFilter,
|
|
461
|
-
buildAddressFilterBody,
|
|
462
|
-
buildAddressFilter,
|
|
463
431
|
buildEvmEventConfig,
|
|
464
432
|
buildEvmOnEventRegistration,
|
|
465
433
|
alwaysIncludedSvmBlockFields,
|