envio 3.5.0-alpha.0 → 3.5.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +10 -6
- package/package.json +6 -6
- package/src/BatchProcessing.res +13 -0
- package/src/BatchProcessing.res.mjs +4 -0
- package/src/Bin.res +2 -2
- package/src/ChainFetching.res +5 -5
- package/src/ChainFetching.res.mjs +3 -4
- package/src/ChainState.res +44 -36
- package/src/ChainState.res.mjs +36 -31
- package/src/ChainState.resi +2 -12
- package/src/Change.res +3 -3
- package/src/Config.res +4 -4
- package/src/Config.res.mjs +1 -10
- package/src/Core.res +4 -35
- package/src/Core.res.mjs +1 -31
- package/src/CrossChainState.res +2 -20
- package/src/CrossChainState.res.mjs +2 -6
- package/src/EntityId.res +15 -0
- package/src/EntityId.res.mjs +9 -0
- package/src/Env.res +16 -0
- package/src/Env.res.mjs +6 -0
- package/src/EventConfigBuilder.res +1 -58
- package/src/EventConfigBuilder.res.mjs +1 -33
- package/src/FetchState.res +663 -484
- package/src/FetchState.res.mjs +491 -440
- package/src/HandlerRegister.res +45 -0
- package/src/HandlerRegister.res.mjs +43 -0
- package/src/InMemoryStore.res +11 -4
- package/src/InMemoryTable.res +12 -10
- package/src/InMemoryTable.res.mjs +6 -5
- package/src/IndexerState.res +59 -2
- package/src/IndexerState.res.mjs +48 -3
- package/src/IndexerState.resi +2 -0
- package/src/Internal.res +17 -8
- package/src/LogSelection.res +2 -1
- package/src/Metrics.res +37 -6
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +1 -1
- package/src/PgStorage.res +55 -22
- package/src/PgStorage.res.mjs +25 -17
- package/src/SimulateItems.res +2 -2
- package/src/TestIndexer.res +6 -5
- package/src/TestIndexer.res.mjs +3 -2
- package/src/UserContext.res +3 -3
- package/src/Writing.res +12 -2
- package/src/Writing.res.mjs +13 -2
- package/src/bindings/ClickHouse.res +5 -3
- package/src/bindings/ClickHouse.res.mjs +2 -5
- package/src/bindings/Vitest.res +22 -1
- package/src/bindings/Vitest.res.mjs +15 -0
- package/src/db/EntityHistory.res +16 -7
- package/src/db/EntityHistory.res.mjs +7 -6
- package/src/db/Table.res +40 -9
- package/src/db/Table.res.mjs +44 -6
- package/src/sources/AddressSet.res +48 -0
- package/src/sources/AddressSet.res.mjs +11 -0
- package/src/sources/AddressStore.res +152 -0
- package/src/sources/AddressStore.res.mjs +97 -0
- package/src/sources/EvmChain.res +3 -0
- package/src/sources/EvmChain.res.mjs +4 -2
- package/src/sources/EvmHyperSyncSource.res +9 -9
- package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
- package/src/sources/EvmRpcClient.res +10 -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 +9 -5
- package/src/sources/HyperSync.res.mjs +3 -3
- package/src/sources/HyperSync.resi +3 -2
- package/src/sources/HyperSyncClient.res +24 -10
- package/src/sources/HyperSyncClient.res.mjs +4 -4
- package/src/sources/RpcSource.res +71 -67
- package/src/sources/RpcSource.res.mjs +4 -4
- package/src/sources/SimulateSource.res +26 -11
- package/src/sources/SimulateSource.res.mjs +13 -5
- package/src/sources/Source.res +8 -4
- 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 +14 -4
- package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
- package/src/sources/SvmHyperSyncSource.res +11 -6
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
- package/src/IndexingAddresses.res +0 -108
- package/src/IndexingAddresses.res.mjs +0 -101
- package/src/IndexingAddresses.resi +0 -34
package/src/Config.res
CHANGED
|
@@ -78,6 +78,9 @@ type t = {
|
|
|
78
78
|
ecosystem: Ecosystem.t,
|
|
79
79
|
enableRawEvents: bool,
|
|
80
80
|
maxAddrInPartition: int,
|
|
81
|
+
// Per-contract registered-address count past which a dynamic contract switches
|
|
82
|
+
// to client-side filtering. Overridable in tests.
|
|
83
|
+
clientFilterAddressThreshold: int,
|
|
81
84
|
batchSize: int,
|
|
82
85
|
// Slack (in blocks) below the lagged head within which a chain still counts as
|
|
83
86
|
// ready to enter the reorg threshold, absorbing head advances between catch-up
|
|
@@ -387,10 +390,6 @@ let getFieldTypeAndSchema = (prop, ~enumConfigsByName: dict<Table.enumConfig<Tab
|
|
|
387
390
|
let enumConfig = enumConfigsByName->Dict.get(enumName)->Option.getOrThrow
|
|
388
391
|
(Table.Enum({config: enumConfig}), enumConfig.schema->S.toUnknown)
|
|
389
392
|
}
|
|
390
|
-
| "entity" => {
|
|
391
|
-
let entityName = prop["entity"]->Option.getOrThrow
|
|
392
|
-
(Table.Entity({name: entityName}), S.string->S.toUnknown)
|
|
393
|
-
}
|
|
394
393
|
| other => JsError.throwWithMessage("Unknown field type in entity config: " ++ other)
|
|
395
394
|
}
|
|
396
395
|
|
|
@@ -1033,6 +1032,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1033
1032
|
enableRawEvents: publicConfig["rawEvents"]->Option.getOr(false),
|
|
1034
1033
|
ecosystem,
|
|
1035
1034
|
maxAddrInPartition,
|
|
1035
|
+
clientFilterAddressThreshold: Env.clientFilterAddressThreshold,
|
|
1036
1036
|
batchSize: publicConfig["fullBatchSize"]->Option.getOr(5000),
|
|
1037
1037
|
reorgThresholdReadyTolerance: 100,
|
|
1038
1038
|
lowercaseAddresses,
|
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);
|
|
@@ -724,6 +714,7 @@ function fromPublic(publicConfigJson) {
|
|
|
724
714
|
ecosystem: ecosystem,
|
|
725
715
|
enableRawEvents: Stdlib_Option.getOr(publicConfig.rawEvents, false),
|
|
726
716
|
maxAddrInPartition: Env.maxAddrInPartition,
|
|
717
|
+
clientFilterAddressThreshold: Env.clientFilterAddressThreshold,
|
|
727
718
|
batchSize: Stdlib_Option.getOr(publicConfig.fullBatchSize, 5000),
|
|
728
719
|
reorgThresholdReadyTolerance: 100,
|
|
729
720
|
lowercaseAddresses: lowercaseAddresses,
|
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>,
|
|
@@ -27,7 +28,6 @@ type addon = {
|
|
|
27
28
|
encodeIndexedTopic: (~abiType: string, ~value: unknown) => EvmTypes.Hex.t,
|
|
28
29
|
fromUserApi: (string, fromUserApiOptions) => fromUserApiResult,
|
|
29
30
|
runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
|
|
30
|
-
requestShutdown: unit => unit,
|
|
31
31
|
@as("EvmHyperSyncClient")
|
|
32
32
|
evmHyperSyncClient: evmHyperSyncClientCtor,
|
|
33
33
|
@as("EvmRpcClient")
|
|
@@ -40,6 +40,8 @@ type addon = {
|
|
|
40
40
|
transactionStore: transactionStoreCtor,
|
|
41
41
|
@as("BlockStore")
|
|
42
42
|
blockStore: blockStoreCtor,
|
|
43
|
+
@as("AddressStore")
|
|
44
|
+
addressStore: addressStoreCtor,
|
|
43
45
|
// Ordered transaction-field names exposed for the field-code contract test
|
|
44
46
|
// (the ReScript `transactionFields` arrays must match the Rust ordinals).
|
|
45
47
|
evmTransactionFieldNames: unit => array<string>,
|
|
@@ -229,40 +231,7 @@ let fromUserApi = (~schema=?, ~env=?, ~files=?, ~withIndexerTypes=false, yaml) =
|
|
|
229
231
|
)
|
|
230
232
|
}
|
|
231
233
|
|
|
232
|
-
let runWithShutdownSignals = %raw(`function(run, requestShutdown) {
|
|
233
|
-
var onShutdown = function() { requestShutdown(); };
|
|
234
|
-
process.once("SIGINT", onShutdown);
|
|
235
|
-
process.once("SIGTERM", onShutdown);
|
|
236
|
-
return run().finally(function() {
|
|
237
|
-
process.off("SIGINT", onShutdown);
|
|
238
|
-
process.off("SIGTERM", onShutdown);
|
|
239
|
-
});
|
|
240
|
-
}`)
|
|
241
|
-
|
|
242
|
-
// True only when the parsed subcommand is `serve`. The subcommand is the
|
|
243
|
-
// first positional arg; global value-taking flags (`-d`/`--directory`/
|
|
244
|
-
// `--config`) consume the token after them, and other leading flags are
|
|
245
|
-
// skipped — so an unrelated arg value that happens to equal "serve" (e.g.
|
|
246
|
-
// `envio init --name serve`) does not trigger the serve-only shutdown wiring.
|
|
247
|
-
let isServeCommand: array<string> => bool = %raw(`function(args) {
|
|
248
|
-
var valueFlags = { "-d": true, "--directory": true, "--config": true };
|
|
249
|
-
for (var i = 0; i < args.length; i++) {
|
|
250
|
-
var a = args[i];
|
|
251
|
-
if (a[0] === "-") {
|
|
252
|
-
if (a.indexOf("=") === -1 && valueFlags[a]) { i++; }
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
return a === "serve";
|
|
256
|
-
}
|
|
257
|
-
return false;
|
|
258
|
-
}`)
|
|
259
|
-
|
|
260
234
|
let runCli = args => {
|
|
261
235
|
let addon = getAddon()
|
|
262
|
-
|
|
263
|
-
if isServeCommand(args) {
|
|
264
|
-
runWithShutdownSignals(invoke, () => addon.requestShutdown())
|
|
265
|
-
} else {
|
|
266
|
-
invoke()
|
|
267
|
-
}
|
|
236
|
+
addon.runCli(~args, ~envioPackageDir=Null.make(envioPackageDir))
|
|
268
237
|
}
|
package/src/Core.res.mjs
CHANGED
|
@@ -191,37 +191,9 @@ function fromUserApi(schema, env, files, withIndexerTypesOpt, yaml) {
|
|
|
191
191
|
});
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
let runWithShutdownSignals = (function(run, requestShutdown) {
|
|
195
|
-
var onShutdown = function() { requestShutdown(); };
|
|
196
|
-
process.once("SIGINT", onShutdown);
|
|
197
|
-
process.once("SIGTERM", onShutdown);
|
|
198
|
-
return run().finally(function() {
|
|
199
|
-
process.off("SIGINT", onShutdown);
|
|
200
|
-
process.off("SIGTERM", onShutdown);
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
let isServeCommand = (function(args) {
|
|
205
|
-
var valueFlags = { "-d": true, "--directory": true, "--config": true };
|
|
206
|
-
for (var i = 0; i < args.length; i++) {
|
|
207
|
-
var a = args[i];
|
|
208
|
-
if (a[0] === "-") {
|
|
209
|
-
if (a.indexOf("=") === -1 && valueFlags[a]) { i++; }
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
return a === "serve";
|
|
213
|
-
}
|
|
214
|
-
return false;
|
|
215
|
-
});
|
|
216
|
-
|
|
217
194
|
function runCli(args) {
|
|
218
195
|
let addon = getAddon();
|
|
219
|
-
|
|
220
|
-
if (isServeCommand(args)) {
|
|
221
|
-
return runWithShutdownSignals(invoke, () => addon.requestShutdown());
|
|
222
|
-
} else {
|
|
223
|
-
return addon.runCli(args, envioPackageDir);
|
|
224
|
-
}
|
|
196
|
+
return addon.runCli(args, envioPackageDir);
|
|
225
197
|
}
|
|
226
198
|
|
|
227
199
|
export {
|
|
@@ -236,8 +208,6 @@ export {
|
|
|
236
208
|
getAddon,
|
|
237
209
|
getConfigJson,
|
|
238
210
|
fromUserApi,
|
|
239
|
-
runWithShutdownSignals,
|
|
240
|
-
isServeCommand,
|
|
241
211
|
runCli,
|
|
242
212
|
}
|
|
243
213
|
/* envioPackageDir Not a pure module */
|
package/src/CrossChainState.res
CHANGED
|
@@ -249,19 +249,6 @@ let checkAndFetch = async (
|
|
|
249
249
|
// while it takes its first measurements. Its probe is one admission unit.
|
|
250
250
|
let coldChainBudget = minimumAdmissionBudget
|
|
251
251
|
|
|
252
|
-
// Chunk reservations get headroom over the density estimate so a
|
|
253
|
-
// denser-than-expected range doesn't truncate at the server cap; realtime
|
|
254
|
-
// gets more since a forced catch-up query there costs a head-poll roundtrip.
|
|
255
|
-
let chunkItemsMultiplier = crossChainState.isRealtime ? 3. : 1.5
|
|
256
|
-
|
|
257
|
-
// Server-cap floor for bounded queries: their block range is already the
|
|
258
|
-
// hard bound on the response, so a low density estimate shrinking the cap
|
|
259
|
-
// below this only buys self-truncated responses. Splitting the target pool
|
|
260
|
-
// across a chain's concurrency slots keeps the worst case — every in-flight
|
|
261
|
-
// bounded query returning a full floored response at once — at ~one buffer
|
|
262
|
-
// target.
|
|
263
|
-
let itemsTargetFloor = crossChainState.targetBufferSize / FetchState.maxChainConcurrency
|
|
264
|
-
|
|
265
252
|
let prioritizedChainStates = crossChainState->priorityOrder
|
|
266
253
|
|
|
267
254
|
// Alignment anchor: the first known-height chain in priority order — which,
|
|
@@ -308,12 +295,7 @@ let checkAndFetch = async (
|
|
|
308
295
|
Some(cs->ChainState.blockAtProgress(~progress=progress +. 0.1))
|
|
309
296
|
| _ => None
|
|
310
297
|
}
|
|
311
|
-
switch cs->ChainState.getNextQuery(
|
|
312
|
-
~chainTargetItems,
|
|
313
|
-
~chunkItemsMultiplier,
|
|
314
|
-
~itemsTargetFloor,
|
|
315
|
-
~maxTargetBlock?,
|
|
316
|
-
) {
|
|
298
|
+
switch cs->ChainState.getNextQuery(~chainTargetItems, ~maxTargetBlock?) {
|
|
317
299
|
| WaitingForNewBlock as action => actionByChain->Utils.Dict.setByInt(chainId, action)
|
|
318
300
|
| NothingToQuery =>
|
|
319
301
|
// A chain below its head can emit no query when its budget went to
|
|
@@ -333,7 +315,7 @@ let checkAndFetch = async (
|
|
|
333
315
|
{
|
|
334
316
|
"fromBlock": query.fromBlock,
|
|
335
317
|
"targetBlock": query.toBlock,
|
|
336
|
-
"targetEvents": query.
|
|
318
|
+
"targetEvents": query.itemsEst,
|
|
337
319
|
},
|
|
338
320
|
)
|
|
339
321
|
)
|
|
@@ -6,9 +6,7 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
6
6
|
import * as Logging from "./Logging.res.mjs";
|
|
7
7
|
import * as ChainMap from "./ChainMap.res.mjs";
|
|
8
8
|
import * as ChainState from "./ChainState.res.mjs";
|
|
9
|
-
import * as FetchState from "./FetchState.res.mjs";
|
|
10
9
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
11
|
-
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
12
10
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
13
11
|
import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
14
12
|
import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
|
|
@@ -147,8 +145,6 @@ async function checkAndFetch(crossChainState, dispatchChain) {
|
|
|
147
145
|
contents: Primitive_float.max(0, targetBudget - totalReadyCount(crossChainState) - totalReservedSize(crossChainState))
|
|
148
146
|
};
|
|
149
147
|
let minimumAdmissionBudget = targetBudget * 0.1;
|
|
150
|
-
let chunkItemsMultiplier = crossChainState.isRealtime ? 3 : 1.5;
|
|
151
|
-
let itemsTargetFloor = Primitive_int.div(crossChainState.targetBufferSize, FetchState.maxChainConcurrency);
|
|
152
148
|
let prioritizedChainStates = priorityOrder(crossChainState);
|
|
153
149
|
let alignment = crossChainState.isRealtime ? undefined : Stdlib_Option.map(prioritizedChainStates.find(cs => ChainState.knownHeight(cs) !== 0), cs => [
|
|
154
150
|
ChainState.chainConfig(cs).id,
|
|
@@ -170,7 +166,7 @@ async function checkAndFetch(crossChainState, dispatchChain) {
|
|
|
170
166
|
isCold ? Primitive_float.min(remaining.contents, minimumAdmissionBudget) : remaining.contents
|
|
171
167
|
) + ChainState.pendingBudget(cs);
|
|
172
168
|
let maxTargetBlock = alignment !== undefined && alignment[0] !== chainId ? ChainState.blockAtProgress(cs, alignment[1] + 0.1) : undefined;
|
|
173
|
-
let action = ChainState.getNextQuery(cs, chainTargetItems,
|
|
169
|
+
let action = ChainState.getNextQuery(cs, chainTargetItems, maxTargetBlock);
|
|
174
170
|
if (typeof action !== "object") {
|
|
175
171
|
if (action === "WaitingForNewBlock") {
|
|
176
172
|
actionByChain[chainId] = action;
|
|
@@ -186,7 +182,7 @@ async function checkAndFetch(crossChainState, dispatchChain) {
|
|
|
186
182
|
partitions[query.partitionId] = {
|
|
187
183
|
fromBlock: query.fromBlock,
|
|
188
184
|
targetBlock: query.toBlock,
|
|
189
|
-
targetEvents: query.
|
|
185
|
+
targetEvents: query.itemsEst
|
|
190
186
|
};
|
|
191
187
|
});
|
|
192
188
|
Logging.trace({
|
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`)
|
package/src/Env.res
CHANGED
|
@@ -9,6 +9,22 @@ let updateSyncTimeOnRestart =
|
|
|
9
9
|
let targetBufferSize = envSafe->EnvSafe.get("ENVIO_INDEXING_MAX_BUFFER_SIZE", S.option(S.int))
|
|
10
10
|
let maxAddrInPartition = envSafe->EnvSafe.get("MAX_PARTITION_SIZE", S.int, ~fallback=5_000)
|
|
11
11
|
|
|
12
|
+
// Most parallel in-flight queries a single chain may have at once, across all
|
|
13
|
+
// its partitions (consumed as FetchState.maxChainConcurrency).
|
|
14
|
+
let maxChainConcurrency = 100
|
|
15
|
+
|
|
16
|
+
// Switch a single contract to client-side address filtering
|
|
17
|
+
// once its registered address count crosses this threshold. Keeping addresses
|
|
18
|
+
// server-side spreads the contract across ceil(count / maxAddrInPartition)
|
|
19
|
+
// partitions, each holding an in-flight query slot; capping a contract at half
|
|
20
|
+
// the chain's concurrency budget stops one busy contract from monopolising them.
|
|
21
|
+
let clientFilterAddressThreshold =
|
|
22
|
+
envSafe->EnvSafe.get(
|
|
23
|
+
"ENVIO_CLIENT_FILTER_ADDRESS_THRESHOLD",
|
|
24
|
+
S.int,
|
|
25
|
+
~fallback=maxAddrInPartition * maxChainConcurrency / 2,
|
|
26
|
+
)
|
|
27
|
+
|
|
12
28
|
// Target number of in-memory objects (uncommitted entity/effect changes plus
|
|
13
29
|
// unwritten batch items) the store holds before processing waits for the write
|
|
14
30
|
// cycle to catch up.
|
package/src/Env.res.mjs
CHANGED
|
@@ -19,6 +19,8 @@ let targetBufferSize = EnvSafe.get(envSafe, "ENVIO_INDEXING_MAX_BUFFER_SIZE", S$
|
|
|
19
19
|
|
|
20
20
|
let maxAddrInPartition = EnvSafe.get(envSafe, "MAX_PARTITION_SIZE", S$RescriptSchema.int, undefined, 5000, undefined, undefined);
|
|
21
21
|
|
|
22
|
+
let clientFilterAddressThreshold = EnvSafe.get(envSafe, "ENVIO_CLIENT_FILTER_ADDRESS_THRESHOLD", S$RescriptSchema.int, undefined, (maxAddrInPartition * 100 | 0) / 2 | 0, undefined, undefined);
|
|
23
|
+
|
|
22
24
|
let inMemoryObjectsTarget = EnvSafe.get(envSafe, "ENVIO_IN_MEMORY_OBJECTS_TARGET", S$RescriptSchema.int, undefined, 100000, undefined, undefined);
|
|
23
25
|
|
|
24
26
|
let serverPort = EnvSafe.get(envSafe, "ENVIO_INDEXER_PORT", S$RescriptSchema.port(S$RescriptSchema.int, undefined), undefined, EnvSafe.get(envSafe, "METRICS_PORT", S$RescriptSchema.port(S$RescriptSchema.int, undefined), undefined, 9898, undefined, undefined), undefined, undefined);
|
|
@@ -216,10 +218,14 @@ let ThrottleWrites = {
|
|
|
216
218
|
|
|
217
219
|
EnvSafe.close(envSafe);
|
|
218
220
|
|
|
221
|
+
let maxChainConcurrency = 100;
|
|
222
|
+
|
|
219
223
|
export {
|
|
220
224
|
updateSyncTimeOnRestart,
|
|
221
225
|
targetBufferSize,
|
|
222
226
|
maxAddrInPartition,
|
|
227
|
+
maxChainConcurrency,
|
|
228
|
+
clientFilterAddressThreshold,
|
|
223
229
|
inMemoryObjectsTarget,
|
|
224
230
|
serverPort,
|
|
225
231
|
tuiEnvVar,
|
|
@@ -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,
|