envio 3.5.0-alpha.3 → 3.5.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/package.json +6 -6
- package/src/Batch.res +8 -8
- package/src/Batch.res.mjs +4 -3
- package/src/BatchProcessing.res +17 -7
- package/src/BatchProcessing.res.mjs +8 -4
- package/src/ChainFetching.res +20 -19
- package/src/ChainFetching.res.mjs +14 -14
- package/src/ChainId.res +58 -0
- package/src/ChainId.res.mjs +49 -0
- package/src/ChainId.resi +27 -0
- package/src/ChainMap.res +12 -26
- package/src/ChainMap.res.mjs +5 -41
- package/src/ChainMap.resi +5 -20
- package/src/ChainMetadata.res +1 -1
- package/src/ChainMetadata.res.mjs +3 -2
- package/src/ChainState.res +35 -20
- package/src/ChainState.res.mjs +34 -11
- package/src/ChainState.resi +2 -0
- package/src/Config.res +41 -24
- package/src/Config.res.mjs +17 -14
- package/src/ContractRegisterContext.res +1 -1
- package/src/ContractRegisterContext.res.mjs +1 -1
- package/src/Core.res +1 -0
- package/src/CrossChainState.res +61 -16
- package/src/CrossChainState.res.mjs +37 -13
- package/src/CrossChainState.resi +3 -2
- package/src/Env.res +0 -3
- package/src/Env.res.mjs +0 -3
- package/src/EventConfigBuilder.res +1 -1
- package/src/EventProcessing.res +4 -4
- package/src/EventProcessing.res.mjs +5 -5
- package/src/FetchState.res +261 -89
- package/src/FetchState.res.mjs +187 -63
- package/src/FinalizeBackfill.res +33 -6
- package/src/FinalizeBackfill.res.mjs +19 -1
- package/src/HandlerRegister.res +8 -8
- package/src/HandlerRegister.res.mjs +5 -4
- package/src/HandlerRegister.resi +1 -1
- package/src/InMemoryStore.res +4 -0
- package/src/InMemoryStore.res.mjs +1 -1
- package/src/IndexerLoop.res +9 -2
- package/src/IndexerLoop.res.mjs +8 -1
- package/src/IndexerState.res +41 -24
- package/src/IndexerState.res.mjs +36 -17
- package/src/IndexerState.resi +9 -7
- package/src/Internal.res +19 -17
- package/src/Internal.res.mjs +18 -9
- package/src/LoadLayer.res +1 -1
- package/src/LoadLayer.res.mjs +2 -1
- package/src/LogSelection.res +2 -2
- package/src/Main.res +7 -11
- package/src/Main.res.mjs +4 -6
- package/src/Metrics.res +6 -6
- package/src/Metrics.res.mjs +4 -3
- package/src/Persistence.res +15 -7
- package/src/PgStorage.res +284 -258
- package/src/PgStorage.res.mjs +181 -208
- package/src/RawEvent.res +1 -2
- package/src/RawEvent.res.mjs +1 -1
- package/src/Rollback.res +18 -20
- package/src/Rollback.res.mjs +9 -9
- package/src/RollbackCommit.res +2 -2
- package/src/RollbackCommit.res.mjs +3 -3
- package/src/SafeCheckpointTracking.res +2 -2
- package/src/SimulateDeadInputTracker.res +17 -21
- package/src/SimulateDeadInputTracker.res.mjs +9 -8
- package/src/SimulateItems.res +8 -10
- package/src/SimulateItems.res.mjs +9 -10
- package/src/Sink.res +2 -2
- package/src/Sink.res.mjs +3 -2
- package/src/TestIndexer.res +11 -14
- package/src/TestIndexer.res.mjs +14 -10
- package/src/UserContext.res +2 -2
- package/src/UserContext.res.mjs +2 -1
- package/src/Utils.res +0 -4
- package/src/Utils.res.mjs +1 -6
- package/src/bindings/ClickHouse.res +20 -2
- package/src/bindings/ClickHouse.res.mjs +25 -14
- package/src/db/EntityHistory.res +1 -1
- package/src/db/IndexCatalog.res +248 -0
- package/src/db/IndexCatalog.res.mjs +222 -0
- package/src/db/IndexDefinition.res +131 -0
- package/src/db/IndexDefinition.res.mjs +138 -0
- package/src/db/IndexManager.res +145 -0
- package/src/db/IndexManager.res.mjs +130 -0
- package/src/db/InternalTable.res +50 -26
- package/src/db/InternalTable.res.mjs +32 -24
- package/src/db/Table.res +11 -1
- package/src/db/Table.res.mjs +9 -4
- package/src/sources/AddressSet.res +5 -4
- package/src/sources/AddressStore.res +25 -14
- package/src/sources/AddressStore.res.mjs +10 -12
- package/src/sources/Evm.res +2 -2
- package/src/sources/Evm.res.mjs +1 -1
- package/src/sources/EvmChain.res +3 -3
- package/src/sources/EvmChain.res.mjs +3 -3
- package/src/sources/EvmHyperSyncSource.res +7 -7
- package/src/sources/EvmHyperSyncSource.res.mjs +6 -6
- package/src/sources/Fuel.res +2 -2
- package/src/sources/Fuel.res.mjs +1 -1
- package/src/sources/FuelHyperSyncClient.res +3 -0
- package/src/sources/FuelHyperSyncClient.res.mjs +1 -0
- package/src/sources/FuelHyperSyncSource.res +5 -7
- package/src/sources/FuelHyperSyncSource.res.mjs +5 -5
- package/src/sources/HyperSync.resi +2 -2
- package/src/sources/HyperSyncClient.res +5 -0
- package/src/sources/HyperSyncClient.res.mjs +1 -0
- package/src/sources/RpcSource.res +13 -14
- package/src/sources/RpcSource.res.mjs +13 -12
- package/src/sources/SimulateSource.res +42 -15
- package/src/sources/SimulateSource.res.mjs +27 -9
- package/src/sources/Source.res +1 -5
- package/src/sources/SourceManager.res +12 -12
- package/src/sources/SourceManager.res.mjs +11 -10
- package/src/sources/SourceManager.resi +2 -2
- package/src/sources/Svm.res +4 -5
- package/src/sources/Svm.res.mjs +5 -4
- package/src/sources/SvmHyperSyncClient.res +3 -0
- package/src/sources/SvmHyperSyncClient.res.mjs +1 -0
- package/src/sources/SvmHyperSyncSource.res +4 -4
- package/src/sources/SvmHyperSyncSource.res.mjs +3 -3
- package/src/tui/Tui.res +1 -1
- package/src/tui/Tui.res.mjs +2 -1
- package/src/tui/components/CustomHooks.res +5 -4
- package/src/tui/components/CustomHooks.res.mjs +4 -2
- package/src/db/IndexRegistry.res +0 -219
- package/src/db/IndexRegistry.res.mjs +0 -195
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Utils from "../Utils.res.mjs";
|
|
4
|
+
import * as IndexCatalog from "./IndexCatalog.res.mjs";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as IndexDefinition from "./IndexDefinition.res.mjs";
|
|
7
|
+
|
|
8
|
+
function make() {
|
|
9
|
+
return {
|
|
10
|
+
catalog: IndexCatalog.make(),
|
|
11
|
+
inflight: {},
|
|
12
|
+
tableQueue: {}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function reload(manager, rows) {
|
|
17
|
+
manager.catalog = IndexCatalog.fromRows(rows);
|
|
18
|
+
return manager.catalog;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function catalog(manager) {
|
|
22
|
+
return manager.catalog;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isSatisfied(manager, definition, coverage) {
|
|
26
|
+
return Stdlib_Option.isSome(IndexCatalog.find(manager.catalog, definition, coverage));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function conflictError(entry, definition, pgSchema) {
|
|
30
|
+
return new Error(`Cannot create the index "` + entry.name + `" in schema "` + pgSchema + `" for ` + IndexDefinition.describe(definition) + `. A different index already holds that name and the indexer can't safely replace it: ` + Stdlib_Option.getOr(IndexCatalog.rejectReason(entry, definition, "Exact"), "it is unique") + `. Drop that index by hand, then restart the indexer.`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function verificationError(name, definition, pgSchema, reason) {
|
|
34
|
+
return new Error(`The index "` + name + `" in schema "` + pgSchema + `" is not usable after its DDL ran: ` + reason + `. It was meant to cover ` + IndexDefinition.describe(definition) + `. Drop it by hand, then restart the indexer.`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function prepare(manager, definition, coverage, pgSchema) {
|
|
38
|
+
let match = IndexCatalog.find(manager.catalog, definition, coverage);
|
|
39
|
+
if (match !== undefined) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
let name = IndexDefinition.name(definition);
|
|
43
|
+
let create = IndexDefinition.makeCreateQuery(definition, pgSchema);
|
|
44
|
+
let entry = IndexCatalog.getByName(manager.catalog, name);
|
|
45
|
+
if (entry === undefined) {
|
|
46
|
+
return {
|
|
47
|
+
definition: definition,
|
|
48
|
+
name: name,
|
|
49
|
+
isRebuild: false,
|
|
50
|
+
queries: [create]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (IndexCatalog.isExactly(entry, definition)) {
|
|
54
|
+
return {
|
|
55
|
+
definition: definition,
|
|
56
|
+
name: name,
|
|
57
|
+
isRebuild: true,
|
|
58
|
+
queries: [
|
|
59
|
+
IndexDefinition.makeDropQuery(pgSchema, name),
|
|
60
|
+
create
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
throw conflictError(entry, definition, pgSchema);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function verifyOrThrow(prepared, rows, pgSchema) {
|
|
68
|
+
let name = prepared.name;
|
|
69
|
+
let definition = prepared.definition;
|
|
70
|
+
let entry = rows.map(IndexCatalog.fromRow).find(entry => entry.name === name);
|
|
71
|
+
if (entry !== undefined) {
|
|
72
|
+
let reason = IndexCatalog.rejectReason(entry, definition, "Exact");
|
|
73
|
+
if (reason !== undefined) {
|
|
74
|
+
throw verificationError(name, definition, pgSchema, reason);
|
|
75
|
+
}
|
|
76
|
+
return entry;
|
|
77
|
+
}
|
|
78
|
+
throw verificationError(name, definition, pgSchema, "PostgreSQL has no such index");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function record(manager, entry) {
|
|
82
|
+
IndexCatalog.set(manager.catalog, entry);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resync(manager, name, rows) {
|
|
86
|
+
let entry = rows.map(IndexCatalog.fromRow).find(entry => entry.name === name);
|
|
87
|
+
if (entry !== undefined) {
|
|
88
|
+
return IndexCatalog.set(manager.catalog, entry);
|
|
89
|
+
} else {
|
|
90
|
+
return IndexCatalog.remove(manager.catalog, name);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function ensure(manager, definition, coverage, build) {
|
|
95
|
+
if (isSatisfied(manager, definition, coverage)) {
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
}
|
|
98
|
+
let key = IndexCatalog.coverageKey(coverage) + IndexDefinition.key(definition);
|
|
99
|
+
let promise = manager.inflight[key];
|
|
100
|
+
if (promise !== undefined) {
|
|
101
|
+
return promise;
|
|
102
|
+
}
|
|
103
|
+
let tail = manager.tableQueue[definition.tableName];
|
|
104
|
+
let tail$1 = tail !== undefined ? tail : Promise.resolve();
|
|
105
|
+
let promise$1 = tail$1.then(() => {
|
|
106
|
+
if (isSatisfied(manager, definition, coverage)) {
|
|
107
|
+
return Promise.resolve();
|
|
108
|
+
} else {
|
|
109
|
+
return build();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
manager.inflight[key] = promise$1;
|
|
113
|
+
manager.tableQueue[definition.tableName] = Utils.$$Promise.silentCatch(promise$1);
|
|
114
|
+
return promise$1.finally(() => Utils.Dict.deleteInPlace(manager.inflight, key));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export {
|
|
118
|
+
make,
|
|
119
|
+
reload,
|
|
120
|
+
catalog,
|
|
121
|
+
isSatisfied,
|
|
122
|
+
conflictError,
|
|
123
|
+
verificationError,
|
|
124
|
+
prepare,
|
|
125
|
+
verifyOrThrow,
|
|
126
|
+
record,
|
|
127
|
+
resync,
|
|
128
|
+
ensure,
|
|
129
|
+
}
|
|
130
|
+
/* Utils Not a pure module */
|
package/src/db/InternalTable.res
CHANGED
|
@@ -56,7 +56,7 @@ module Chains = {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
type t = {
|
|
59
|
-
@as("id") id:
|
|
59
|
+
@as("id") id: ChainId.t,
|
|
60
60
|
@as("start_block") startBlock: int,
|
|
61
61
|
@as("end_block") endBlock: Null.t<int>,
|
|
62
62
|
@as("max_reorg_depth") maxReorgDepth: int,
|
|
@@ -69,7 +69,7 @@ module Chains = {
|
|
|
69
69
|
let table = mkTable(
|
|
70
70
|
"envio_chains",
|
|
71
71
|
~fields=[
|
|
72
|
-
mkField((#id: field :> string),
|
|
72
|
+
mkField((#id: field :> string), ChainId, ~fieldSchema=ChainId.schema, ~isPrimaryKey),
|
|
73
73
|
// Values populated from config
|
|
74
74
|
mkField((#start_block: field :> string), Int32, ~fieldSchema=S.int),
|
|
75
75
|
mkField((#end_block: field :> string), Int32, ~fieldSchema=S.null(S.int), ~isNullable),
|
|
@@ -164,15 +164,24 @@ SET ${setClauses->Array.joinUnsafe(",\n ")}
|
|
|
164
164
|
WHERE "${(#id: field :> string)}" = $1;`
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
// Written
|
|
168
|
-
//
|
|
167
|
+
// Written only once every schema-defined index is verified, so a chain is
|
|
168
|
+
// never reported ready without the indexes the schema promises. One row at a
|
|
169
|
+
// time, like `setMeta`: the id column is INTEGER or BIGINT depending on the
|
|
170
|
+
// configured `ChainId.mode`, and a bare `= $2` needs no cast either way.
|
|
171
|
+
//
|
|
172
|
+
// `IS NULL` so a chain keeps the timestamp it first caught up at, matching the
|
|
173
|
+
// sticky in-memory `ChainState.markReady`. Without it a partial recovery (eg a
|
|
174
|
+
// chain added to an already-synced indexer) would restamp the ready chains in
|
|
175
|
+
// the database while their in-memory copies kept the old value — and the next
|
|
176
|
+
// chain-metadata write would push the stale value back over the committed one.
|
|
169
177
|
let makeSetReadyAtQuery = (~pgSchema) =>
|
|
170
178
|
`UPDATE "${pgSchema}"."${table.tableName}"
|
|
171
179
|
SET "${(#ready_at: field :> string)}" = $1
|
|
172
|
-
WHERE "${(#id: field :> string)}" =
|
|
180
|
+
WHERE "${(#id: field :> string)}" = $2
|
|
181
|
+
AND "${(#ready_at: field :> string)}" IS NULL;`
|
|
173
182
|
|
|
174
183
|
type rawInitialState = {
|
|
175
|
-
id:
|
|
184
|
+
id: ChainId.t,
|
|
176
185
|
startBlock: int,
|
|
177
186
|
endBlock: Null.t<int>,
|
|
178
187
|
maxReorgDepth: int,
|
|
@@ -198,7 +207,7 @@ FROM "${pgSchema}"."${table.tableName}";`
|
|
|
198
207
|
}
|
|
199
208
|
|
|
200
209
|
type rawIndexingAddress = {
|
|
201
|
-
chainId:
|
|
210
|
+
chainId: ChainId.t,
|
|
202
211
|
address: Address.t,
|
|
203
212
|
contractName: string,
|
|
204
213
|
registrationBlock: int,
|
|
@@ -231,7 +240,9 @@ FROM "${pgSchema}"."${EnvioAddresses.table.tableName}";`
|
|
|
231
240
|
|
|
232
241
|
let indexingAddressesByChainId = Dict.make()
|
|
233
242
|
rawIndexingAddresses->Array.forEach(row => {
|
|
234
|
-
|
|
243
|
+
// BIGINT chain ids come back as strings; normalizing here keeps the
|
|
244
|
+
// grouping key identical to the one derived from the chains rows below.
|
|
245
|
+
let key = row.chainId->ChainId.normalizeOrThrow->ChainId.toString
|
|
235
246
|
let addresses = switch indexingAddressesByChainId->Dict.get(key) {
|
|
236
247
|
| Some(addresses) => addresses
|
|
237
248
|
| None =>
|
|
@@ -249,10 +260,14 @@ FROM "${pgSchema}"."${EnvioAddresses.table.tableName}";`
|
|
|
249
260
|
})
|
|
250
261
|
|
|
251
262
|
rawInitialStates->Array.map(rawInitialState => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
263
|
+
let id = rawInitialState.id->ChainId.normalizeOrThrow
|
|
264
|
+
{
|
|
265
|
+
...rawInitialState,
|
|
266
|
+
id,
|
|
267
|
+
indexingAddresses: indexingAddressesByChainId
|
|
268
|
+
->Dict.get(id->ChainId.toString)
|
|
269
|
+
->Option.getOr([]),
|
|
270
|
+
}
|
|
256
271
|
})
|
|
257
272
|
}
|
|
258
273
|
|
|
@@ -295,7 +310,7 @@ WHERE "id" = $1;`
|
|
|
295
310
|
}
|
|
296
311
|
|
|
297
312
|
type progressedChain = {
|
|
298
|
-
chainId:
|
|
313
|
+
chainId: ChainId.t,
|
|
299
314
|
progressBlockNumber: int,
|
|
300
315
|
sourceBlockNumber: int,
|
|
301
316
|
totalEventsProcessed: float,
|
|
@@ -309,7 +324,7 @@ WHERE "id" = $1;`
|
|
|
309
324
|
progressedChains->Array.forEach(data => {
|
|
310
325
|
let params = []
|
|
311
326
|
|
|
312
|
-
params->Array.push(data.chainId->(Utils.magic:
|
|
327
|
+
params->Array.push(data.chainId->(Utils.magic: ChainId.t => unknown))->ignore
|
|
313
328
|
|
|
314
329
|
progressFields->Array.forEach(field => {
|
|
315
330
|
params
|
|
@@ -393,7 +408,7 @@ module Checkpoints = {
|
|
|
393
408
|
type t = {
|
|
394
409
|
id: bigint,
|
|
395
410
|
@as("chain_id")
|
|
396
|
-
chainId:
|
|
411
|
+
chainId: ChainId.t,
|
|
397
412
|
@as("block_number")
|
|
398
413
|
blockNumber: int,
|
|
399
414
|
@as("block_hash")
|
|
@@ -405,7 +420,7 @@ module Checkpoints = {
|
|
|
405
420
|
// Schema for parsing DB results where BIGINT columns come back as strings
|
|
406
421
|
let dbSchema = S.object(s => {
|
|
407
422
|
id: s.field("id", Utils.BigInt.schema),
|
|
408
|
-
chainId: s.field("chain_id",
|
|
423
|
+
chainId: s.field("chain_id", ChainId.schema),
|
|
409
424
|
blockNumber: s.field("block_number", S.int),
|
|
410
425
|
blockHash: s.field(
|
|
411
426
|
"block_hash",
|
|
@@ -423,7 +438,7 @@ module Checkpoints = {
|
|
|
423
438
|
"envio_checkpoints",
|
|
424
439
|
~fields=[
|
|
425
440
|
mkField((#id: field :> string), UInt64, ~fieldSchema=S.bigint, ~isPrimaryKey),
|
|
426
|
-
mkField((#chain_id: field :> string),
|
|
441
|
+
mkField((#chain_id: field :> string), ChainId, ~fieldSchema=ChainId.schema),
|
|
427
442
|
mkField((#block_number: field :> string), Int32, ~fieldSchema=S.int),
|
|
428
443
|
mkField((#block_hash: field :> string), String, ~fieldSchema=S.null(S.string), ~isNullable),
|
|
429
444
|
mkField((#events_processed: field :> string), Int32, ~fieldSchema=S.int),
|
|
@@ -460,9 +475,17 @@ WHERE cp."${(#block_hash: field :> string)}" IS NOT NULL
|
|
|
460
475
|
`SELECT COALESCE(MAX(${(#id: field :> string)}), ${initialCheckpointId->BigInt.toString}) AS id FROM "${pgSchema}"."${table.tableName}";`
|
|
461
476
|
}
|
|
462
477
|
|
|
463
|
-
let makeInsertCheckpointQuery = (~pgSchema) => {
|
|
478
|
+
let makeInsertCheckpointQuery = (~pgSchema, ~chainIdMode: ChainId.mode=Int32) => {
|
|
479
|
+
let chainIdArrayType = Table.getPgFieldType(
|
|
480
|
+
~fieldType=ChainId,
|
|
481
|
+
~pgSchema,
|
|
482
|
+
~isArray=true,
|
|
483
|
+
~isNumericArrayAsText=false,
|
|
484
|
+
~isNullable=false,
|
|
485
|
+
~chainIdMode,
|
|
486
|
+
)
|
|
464
487
|
`INSERT INTO "${pgSchema}"."${table.tableName}" ("${(#id: field :> string)}", "${(#chain_id: field :> string)}", "${(#block_number: field :> string)}", "${(#block_hash: field :> string)}", "${(#events_processed: field :> string)}")
|
|
465
|
-
SELECT * FROM unnest($1::${(BigInt: Postgres.columnType :> string)}[],$2::${
|
|
488
|
+
SELECT * FROM unnest($1::${(BigInt: Postgres.columnType :> string)}[],$2::${chainIdArrayType},$3::${(Integer: Postgres.columnType :> string)}[],$4::${(Text: Postgres.columnType :> string)}[],$5::${(Integer: Postgres.columnType :> string)}[]);`
|
|
466
489
|
}
|
|
467
490
|
|
|
468
491
|
let insert = (
|
|
@@ -473,8 +496,9 @@ SELECT * FROM unnest($1::${(BigInt: Postgres.columnType :> string)}[],$2::${(Int
|
|
|
473
496
|
~checkpointBlockNumbers,
|
|
474
497
|
~checkpointBlockHashes,
|
|
475
498
|
~checkpointEventsProcessed,
|
|
499
|
+
~chainIdMode: ChainId.mode=Int32,
|
|
476
500
|
) => {
|
|
477
|
-
let query = makeInsertCheckpointQuery(~pgSchema)
|
|
501
|
+
let query = makeInsertCheckpointQuery(~pgSchema, ~chainIdMode)
|
|
478
502
|
|
|
479
503
|
// Convert bigint arrays to string arrays for postgres driver compatibility
|
|
480
504
|
let checkpointIdStrings = checkpointIds->Utils.BigInt.arrayToStringArray
|
|
@@ -489,7 +513,7 @@ SELECT * FROM unnest($1::${(BigInt: Postgres.columnType :> string)}[],$2::${(Int
|
|
|
489
513
|
checkpointEventsProcessed,
|
|
490
514
|
)->(
|
|
491
515
|
Utils.magic: (
|
|
492
|
-
(array<string>, array<
|
|
516
|
+
(array<string>, array<ChainId.t>, array<int>, array<Null.t<string>>, array<int>)
|
|
493
517
|
) => unknown
|
|
494
518
|
),
|
|
495
519
|
)
|
|
@@ -530,7 +554,7 @@ LIMIT 1;`
|
|
|
530
554
|
let getRollbackTargetCheckpoint = (
|
|
531
555
|
sql,
|
|
532
556
|
~pgSchema,
|
|
533
|
-
~reorgChainId:
|
|
557
|
+
~reorgChainId: ChainId.t,
|
|
534
558
|
~lastKnownValidBlockNumber: int,
|
|
535
559
|
) => {
|
|
536
560
|
let rawResult: promise<array<{"id": string}>> =
|
|
@@ -547,7 +571,7 @@ LIMIT 1;`
|
|
|
547
571
|
|
|
548
572
|
let makeGetRollbackProgressDiffQuery = (~pgSchema) => {
|
|
549
573
|
`SELECT
|
|
550
|
-
"${(#chain_id: field :> string)}",
|
|
574
|
+
"${(#chain_id: field :> string)}"::float8 as "${(#chain_id: field :> string)}",
|
|
551
575
|
SUM("${(#events_processed: field :> string)}") as events_processed_diff,
|
|
552
576
|
MIN("${(#block_number: field :> string)}") - 1 as new_progress_block_number
|
|
553
577
|
FROM "${pgSchema}"."${table.tableName}"
|
|
@@ -568,7 +592,7 @@ GROUP BY "${(#chain_id: field :> string)}";`
|
|
|
568
592
|
->(
|
|
569
593
|
Utils.magic: promise<unknown> => promise<
|
|
570
594
|
array<{
|
|
571
|
-
"chain_id":
|
|
595
|
+
"chain_id": ChainId.t,
|
|
572
596
|
"events_processed_diff": string,
|
|
573
597
|
"new_progress_block_number": int,
|
|
574
598
|
}>,
|
|
@@ -581,7 +605,7 @@ module RawEvents = {
|
|
|
581
605
|
type t = Internal.rawEvent
|
|
582
606
|
|
|
583
607
|
let schema = S.schema((s): t => {
|
|
584
|
-
chain_id: s.matches(
|
|
608
|
+
chain_id: s.matches(ChainId.schema),
|
|
585
609
|
event_id: s.matches(S.bigint),
|
|
586
610
|
event_name: s.matches(S.string),
|
|
587
611
|
contract_name: s.matches(S.string),
|
|
@@ -598,7 +622,7 @@ module RawEvents = {
|
|
|
598
622
|
let table = mkTable(
|
|
599
623
|
"raw_events",
|
|
600
624
|
~fields=[
|
|
601
|
-
mkField("chain_id",
|
|
625
|
+
mkField("chain_id", ChainId, ~fieldSchema=ChainId.schema),
|
|
602
626
|
mkField("event_id", UInt64, ~fieldSchema=S.bigint),
|
|
603
627
|
mkField("event_name", String, ~fieldSchema=S.string),
|
|
604
628
|
mkField("contract_name", String, ~fieldSchema=S.string),
|
|
@@ -4,6 +4,7 @@ import * as Table from "./Table.res.mjs";
|
|
|
4
4
|
import * as Utils from "../Utils.res.mjs";
|
|
5
5
|
import * as Config from "../Config.res.mjs";
|
|
6
6
|
import * as Address from "../Address.res.mjs";
|
|
7
|
+
import * as ChainId from "../ChainId.res.mjs";
|
|
7
8
|
import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
|
|
8
9
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
9
10
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
@@ -25,7 +26,7 @@ let fields = [
|
|
|
25
26
|
];
|
|
26
27
|
|
|
27
28
|
let table = Table.mkTable("envio_chains", undefined, [
|
|
28
|
-
Table.mkField("id", "
|
|
29
|
+
Table.mkField("id", "ChainId", ChainId.schema, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined),
|
|
29
30
|
Table.mkField("start_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
30
31
|
Table.mkField("end_block", "Int32", S$RescriptSchema.$$null(S$RescriptSchema.int), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
31
32
|
Table.mkField("max_reorg_depth", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
@@ -104,7 +105,8 @@ WHERE "` + "id" + `" = $1;`;
|
|
|
104
105
|
function makeSetReadyAtQuery(pgSchema) {
|
|
105
106
|
return `UPDATE "` + pgSchema + `"."` + table.tableName + `"
|
|
106
107
|
SET "` + "ready_at" + `" = $1
|
|
107
|
-
WHERE "` + "id" + `" =
|
|
108
|
+
WHERE "` + "id" + `" = $2
|
|
109
|
+
AND "` + "ready_at" + `" IS NULL;`;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
function makeGetInitialStateQuery(pgSchema) {
|
|
@@ -135,7 +137,7 @@ async function getInitialState(sql, pgSchema) {
|
|
|
135
137
|
]);
|
|
136
138
|
let indexingAddressesByChainId = {};
|
|
137
139
|
match[1].forEach(row => {
|
|
138
|
-
let key = row.chainId
|
|
140
|
+
let key = ChainId.toString(ChainId.normalizeOrThrow(row.chainId));
|
|
139
141
|
let addresses = indexingAddressesByChainId[key];
|
|
140
142
|
let addresses$1;
|
|
141
143
|
if (addresses !== undefined) {
|
|
@@ -151,18 +153,21 @@ async function getInitialState(sql, pgSchema) {
|
|
|
151
153
|
registrationBlock: row.registrationBlock
|
|
152
154
|
});
|
|
153
155
|
});
|
|
154
|
-
return match[0].map(rawInitialState =>
|
|
155
|
-
id
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
156
|
+
return match[0].map(rawInitialState => {
|
|
157
|
+
let id = ChainId.normalizeOrThrow(rawInitialState.id);
|
|
158
|
+
return {
|
|
159
|
+
id: id,
|
|
160
|
+
startBlock: rawInitialState.startBlock,
|
|
161
|
+
endBlock: rawInitialState.endBlock,
|
|
162
|
+
maxReorgDepth: rawInitialState.maxReorgDepth,
|
|
163
|
+
firstEventBlockNumber: rawInitialState.firstEventBlockNumber,
|
|
164
|
+
timestampCaughtUpToHeadOrEndblock: rawInitialState.timestampCaughtUpToHeadOrEndblock,
|
|
165
|
+
numEventsProcessed: rawInitialState.numEventsProcessed,
|
|
166
|
+
progressBlockNumber: rawInitialState.progressBlockNumber,
|
|
167
|
+
indexingAddresses: Stdlib_Option.getOr(indexingAddressesByChainId[ChainId.toString(id)], []),
|
|
168
|
+
sourceBlockNumber: rawInitialState.sourceBlockNumber
|
|
169
|
+
};
|
|
170
|
+
});
|
|
166
171
|
}
|
|
167
172
|
|
|
168
173
|
let progressFields = [
|
|
@@ -269,7 +274,7 @@ let EnvioInfo = {
|
|
|
269
274
|
|
|
270
275
|
let dbSchema = S$RescriptSchema.object(s => ({
|
|
271
276
|
id: s.f("id", Utils.$$BigInt.schema),
|
|
272
|
-
chain_id: s.f("chain_id",
|
|
277
|
+
chain_id: s.f("chain_id", ChainId.schema),
|
|
273
278
|
block_number: s.f("block_number", S$RescriptSchema.int),
|
|
274
279
|
block_hash: s.f("block_hash", S$RescriptSchema.union([
|
|
275
280
|
S$RescriptSchema.string,
|
|
@@ -280,7 +285,7 @@ let dbSchema = S$RescriptSchema.object(s => ({
|
|
|
280
285
|
|
|
281
286
|
let table$2 = Table.mkTable("envio_checkpoints", undefined, [
|
|
282
287
|
Table.mkField("id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined),
|
|
283
|
-
Table.mkField("chain_id", "
|
|
288
|
+
Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
284
289
|
Table.mkField("block_number", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
285
290
|
Table.mkField("block_hash", "String", S$RescriptSchema.$$null(S$RescriptSchema.string), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
286
291
|
Table.mkField("events_processed", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)
|
|
@@ -311,13 +316,16 @@ function makeCommitedCheckpointIdQuery(pgSchema) {
|
|
|
311
316
|
return `SELECT COALESCE(MAX(` + "id" + `), ` + (0n).toString() + `) AS id FROM "` + pgSchema + `"."` + table$2.tableName + `";`;
|
|
312
317
|
}
|
|
313
318
|
|
|
314
|
-
function makeInsertCheckpointQuery(pgSchema) {
|
|
319
|
+
function makeInsertCheckpointQuery(pgSchema, chainIdModeOpt) {
|
|
320
|
+
let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
|
|
321
|
+
let chainIdArrayType = Table.getPgFieldType("ChainId", pgSchema, true, false, false, chainIdMode);
|
|
315
322
|
return `INSERT INTO "` + pgSchema + `"."` + table$2.tableName + `" ("` + "id" + `", "` + "chain_id" + `", "` + "block_number" + `", "` + "block_hash" + `", "` + "events_processed" + `")
|
|
316
|
-
SELECT * FROM unnest($1::` + "BIGINT" + `[],$2::` +
|
|
323
|
+
SELECT * FROM unnest($1::` + "BIGINT" + `[],$2::` + chainIdArrayType + `,$3::` + "INTEGER" + `[],$4::` + "TEXT" + `[],$5::` + "INTEGER" + `[]);`;
|
|
317
324
|
}
|
|
318
325
|
|
|
319
|
-
function insert(sql, pgSchema, checkpointIds, checkpointChainIds, checkpointBlockNumbers, checkpointBlockHashes, checkpointEventsProcessed) {
|
|
320
|
-
let
|
|
326
|
+
function insert(sql, pgSchema, checkpointIds, checkpointChainIds, checkpointBlockNumbers, checkpointBlockHashes, checkpointEventsProcessed, chainIdModeOpt) {
|
|
327
|
+
let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
|
|
328
|
+
let query = makeInsertCheckpointQuery(pgSchema, chainIdMode);
|
|
321
329
|
let checkpointIdStrings = Utils.$$BigInt.arrayToStringArray(checkpointIds);
|
|
322
330
|
return sql.unsafe(query, [
|
|
323
331
|
checkpointIdStrings,
|
|
@@ -359,7 +367,7 @@ function getRollbackTargetCheckpoint(sql, pgSchema, reorgChainId, lastKnownValid
|
|
|
359
367
|
|
|
360
368
|
function makeGetRollbackProgressDiffQuery(pgSchema) {
|
|
361
369
|
return `SELECT
|
|
362
|
-
"` + "chain_id" + `",
|
|
370
|
+
"` + "chain_id" + `"::float8 as "` + "chain_id" + `",
|
|
363
371
|
SUM("` + "events_processed" + `") as events_processed_diff,
|
|
364
372
|
MIN("` + "block_number" + `") - 1 as new_progress_block_number
|
|
365
373
|
FROM "` + pgSchema + `"."` + table$2.tableName + `"
|
|
@@ -389,7 +397,7 @@ let Checkpoints = {
|
|
|
389
397
|
};
|
|
390
398
|
|
|
391
399
|
let schema = S$RescriptSchema.schema(s => ({
|
|
392
|
-
chain_id: s.m(
|
|
400
|
+
chain_id: s.m(ChainId.schema),
|
|
393
401
|
event_id: s.m(S$RescriptSchema.bigint),
|
|
394
402
|
event_name: s.m(S$RescriptSchema.string),
|
|
395
403
|
contract_name: s.m(S$RescriptSchema.string),
|
|
@@ -404,7 +412,7 @@ let schema = S$RescriptSchema.schema(s => ({
|
|
|
404
412
|
}));
|
|
405
413
|
|
|
406
414
|
let table$3 = Table.mkTable("raw_events", undefined, [
|
|
407
|
-
Table.mkField("chain_id", "
|
|
415
|
+
Table.mkField("chain_id", "ChainId", ChainId.schema, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
408
416
|
Table.mkField("event_id", "UInt64", S$RescriptSchema.bigint, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
409
417
|
Table.mkField("event_name", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
|
410
418
|
Table.mkField("contract_name", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
|
package/src/db/Table.res
CHANGED
|
@@ -23,6 +23,9 @@ type fieldType =
|
|
|
23
23
|
| UInt52
|
|
24
24
|
| UInt64
|
|
25
25
|
| Int32
|
|
26
|
+
// Resolved to Int32 or UInt64 storage from the config's `ChainId.mode`, so
|
|
27
|
+
// the internal tables can stay module-level constants.
|
|
28
|
+
| ChainId
|
|
26
29
|
| Number
|
|
27
30
|
| BigInt({precision?: int})
|
|
28
31
|
| BigDecimal({config?: (int, int)}) // (precision, scale)
|
|
@@ -138,11 +141,17 @@ let getPgFieldType = (
|
|
|
138
141
|
~isArray,
|
|
139
142
|
~isNumericArrayAsText,
|
|
140
143
|
~isNullable,
|
|
144
|
+
~chainIdMode: ChainId.mode=Int32,
|
|
141
145
|
) => {
|
|
142
146
|
let columnType = switch fieldType {
|
|
143
147
|
| String => (Postgres.Text :> string)
|
|
144
148
|
| Boolean => (Postgres.Boolean :> string)
|
|
145
149
|
| Int32 => (Postgres.Integer :> string)
|
|
150
|
+
| ChainId =>
|
|
151
|
+
switch chainIdMode {
|
|
152
|
+
| Int32 => (Postgres.Integer :> string)
|
|
153
|
+
| Int64 => (Postgres.BigInt :> string)
|
|
154
|
+
}
|
|
146
155
|
| Uint32 => (Postgres.BigInt :> string)
|
|
147
156
|
| UInt52 => (Postgres.BigInt :> string)
|
|
148
157
|
| UInt64 => (Postgres.BigInt :> string)
|
|
@@ -378,7 +387,7 @@ type sqlParams<'entity> = {
|
|
|
378
387
|
hasArrayField: bool,
|
|
379
388
|
}
|
|
380
389
|
|
|
381
|
-
let toSqlParams = (table: table, ~schema, ~pgSchema) => {
|
|
390
|
+
let toSqlParams = (table: table, ~schema, ~pgSchema, ~chainIdMode: ChainId.mode=Int32) => {
|
|
382
391
|
let quotedFieldNames = []
|
|
383
392
|
let quotedNonPrimaryFieldNames = []
|
|
384
393
|
let arrayFieldTypes = []
|
|
@@ -441,6 +450,7 @@ let toSqlParams = (table: table, ~schema, ~pgSchema) => {
|
|
|
441
450
|
~isArray=true,
|
|
442
451
|
~isNullable=f.isNullable,
|
|
443
452
|
~isNumericArrayAsText=false,
|
|
453
|
+
~chainIdMode,
|
|
444
454
|
)
|
|
445
455
|
switch f.fieldType {
|
|
446
456
|
| Enum(_) => `${(Text: Postgres.columnType :> string)}[]::${pgFieldType}`
|
package/src/db/Table.res.mjs
CHANGED
|
@@ -96,7 +96,8 @@ function getPgFieldName(fieldOrDerived) {
|
|
|
96
96
|
|
|
97
97
|
let idFieldName = "id";
|
|
98
98
|
|
|
99
|
-
function getPgFieldType(fieldType, pgSchema, isArray, isNumericArrayAsText, isNullable) {
|
|
99
|
+
function getPgFieldType(fieldType, pgSchema, isArray, isNumericArrayAsText, isNullable, chainIdModeOpt) {
|
|
100
|
+
let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
|
|
100
101
|
let columnType;
|
|
101
102
|
if (typeof fieldType !== "object") {
|
|
102
103
|
switch (fieldType) {
|
|
@@ -109,6 +110,9 @@ function getPgFieldType(fieldType, pgSchema, isArray, isNumericArrayAsText, isNu
|
|
|
109
110
|
case "Int32" :
|
|
110
111
|
columnType = "INTEGER";
|
|
111
112
|
break;
|
|
113
|
+
case "ChainId" :
|
|
114
|
+
columnType = chainIdMode === "int32" ? "INTEGER" : "BIGINT";
|
|
115
|
+
break;
|
|
112
116
|
case "Number" :
|
|
113
117
|
columnType = "DOUBLE PRECISION";
|
|
114
118
|
break;
|
|
@@ -247,7 +251,7 @@ function getIdFieldOrThrow(table) {
|
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
function getIdPgFieldType(table, pgSchema) {
|
|
250
|
-
return getPgFieldType(getIdFieldOrThrow(table).fieldType, pgSchema, false, false, false);
|
|
254
|
+
return getPgFieldType(getIdFieldOrThrow(table).fieldType, pgSchema, false, false, false, undefined);
|
|
251
255
|
}
|
|
252
256
|
|
|
253
257
|
function getIdSchema(table) {
|
|
@@ -324,7 +328,8 @@ function getUnfilteredCompositeIndexesUnsafe(table) {
|
|
|
324
328
|
}));
|
|
325
329
|
}
|
|
326
330
|
|
|
327
|
-
function toSqlParams(table, schema, pgSchema) {
|
|
331
|
+
function toSqlParams(table, schema, pgSchema, chainIdModeOpt) {
|
|
332
|
+
let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
|
|
328
333
|
let quotedFieldNames = [];
|
|
329
334
|
let quotedNonPrimaryFieldNames = [];
|
|
330
335
|
let arrayFieldTypes = [];
|
|
@@ -395,7 +400,7 @@ function toSqlParams(table, schema, pgSchema) {
|
|
|
395
400
|
let tmp;
|
|
396
401
|
if (field$1.TAG === "Field") {
|
|
397
402
|
let f = field$1._0;
|
|
398
|
-
let pgFieldType = getPgFieldType(f.fieldType, pgSchema, true, false, f.isNullable);
|
|
403
|
+
let pgFieldType = getPgFieldType(f.fieldType, pgSchema, true, false, f.isNullable, chainIdMode);
|
|
399
404
|
let match = f.fieldType;
|
|
400
405
|
tmp = typeof match !== "object" ? (
|
|
401
406
|
match === "Boolean" ? "INTEGER" + `[]::` + pgFieldType : pgFieldType
|
|
@@ -15,10 +15,11 @@ type startBlockGroup = {startBlock: int, count: int}
|
|
|
15
15
|
// fetch state can size and split partitions without ever walking the addresses.
|
|
16
16
|
@send external countFor: (t, string) => int = "countFor"
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
|
|
18
|
+
// This set holds the address under the contract and it has already started at
|
|
19
|
+
// the block — the gate a real source's router applies to a server-side
|
|
20
|
+
// address-filtered query. The temporal half still comes from the store: a
|
|
21
|
+
// merged partition's addresses don't all start at the same block.
|
|
22
|
+
@send external containsAt: (t, Address.t, string, int) => bool = "containsAt"
|
|
22
23
|
|
|
23
24
|
@send external contractNames: t => array<string> = "contractNames"
|
|
24
25
|
|
|
@@ -67,28 +67,39 @@ let make = (~ecosystem: Ecosystem.name, ~shouldChecksum: bool, ~contracts: array
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// The contracts of a chain that have events, with the earliest block any of
|
|
70
|
-
// their events may fire at.
|
|
70
|
+
// their events may fire at. `None` means unrestricted, so it wins over any
|
|
71
|
+
// `Some`: one registration without a start block makes the whole contract's
|
|
72
|
+
// address gate open from the chain's start, and the per-registration start
|
|
73
|
+
// block still holds back the registrations that declared one.
|
|
71
74
|
let contractsOf = (~onEventRegistrations: array<Internal.onEventRegistration>): array<contract> => {
|
|
72
|
-
|
|
75
|
+
// Only ever holds a declared start block, so a missing key is unambiguous —
|
|
76
|
+
// storing `None` in a dict would be indistinguishable from "not seen yet".
|
|
77
|
+
let startBlocks: dict<int> = Dict.make()
|
|
78
|
+
let unrestricted = Utils.Set.make()
|
|
73
79
|
let names = []
|
|
74
80
|
onEventRegistrations->Array.forEach(reg => {
|
|
75
81
|
let name = reg.eventConfig.contractName
|
|
76
|
-
|
|
77
|
-
| None =>
|
|
82
|
+
if !(names->Array.includes(name)) {
|
|
78
83
|
names->Array.push(name)->ignore
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
}
|
|
85
|
+
switch reg.startBlock {
|
|
86
|
+
| None => unrestricted->Utils.Set.add(name)->ignore
|
|
87
|
+
| Some(startBlock) =>
|
|
81
88
|
startBlocks->Dict.set(
|
|
82
89
|
name,
|
|
83
|
-
switch (
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
| (None, None) => None
|
|
90
|
+
switch startBlocks->Utils.Dict.dangerouslyGetNonOption(name) {
|
|
91
|
+
| Some(existing) => Pervasives.min(existing, startBlock)
|
|
92
|
+
| None => startBlock
|
|
87
93
|
},
|
|
88
94
|
)
|
|
89
95
|
}
|
|
90
96
|
})
|
|
91
|
-
names->Array.map(name => {
|
|
97
|
+
names->Array.map(name => {
|
|
98
|
+
name,
|
|
99
|
+
startBlock: unrestricted->Utils.Set.has(name)
|
|
100
|
+
? None
|
|
101
|
+
: startBlocks->Utils.Dict.dangerouslyGetNonOption(name),
|
|
102
|
+
})
|
|
92
103
|
}
|
|
93
104
|
|
|
94
105
|
@send external nextId: t => int = "nextId"
|
|
@@ -108,9 +119,9 @@ external startBlockGroups: (t, string) => array<AddressSet.startBlockGroup> = "s
|
|
|
108
119
|
@send external contractCount: (t, string) => int = "contractCount"
|
|
109
120
|
@send external size: t => int = "size"
|
|
110
121
|
|
|
111
|
-
// The gate routing applies, exposed for the simulate source — it has
|
|
112
|
-
// query boundary to gate at.
|
|
113
|
-
@send external
|
|
122
|
+
// The chain-wide gate routing applies, exposed for the simulate source — it has
|
|
123
|
+
// no real query boundary to gate at.
|
|
124
|
+
@send external isIndexedAt: (t, Address.t, string, int) => bool = "isIndexedAt"
|
|
114
125
|
|
|
115
126
|
// Drops every address registered after the target block, returning how many
|
|
116
127
|
// were dropped. Ids are tombstoned rather than reused, so sets built before the
|