envio 3.10.0 → 3.11.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/evm.schema.json +21 -4
- package/fuel.schema.json +21 -4
- package/index.d.ts +7 -6
- package/package.json +6 -6
- package/src/ChainState.res +9 -73
- package/src/ChainState.res.mjs +6 -58
- package/src/Config.res +73 -18
- package/src/Config.res.mjs +69 -16
- package/src/EventConfigBuilder.res +14 -10
- package/src/EventConfigBuilder.res.mjs +6 -4
- package/src/HandlerRegister.res +12 -11
- package/src/HandlerRegister.res.mjs +6 -5
- package/src/Internal.res +20 -4
- package/src/Internal.res.mjs +9 -0
- package/src/Main.res +8 -1
- package/src/Main.res.mjs +3 -3
- package/src/Persistence.res +11 -1
- package/src/Persistence.res.mjs +6 -2
- package/src/PgStorage.res +1 -1
- package/src/PgStorage.res.mjs +1 -1
- package/src/SimulateItems.res +13 -6
- package/src/SimulateItems.res.mjs +7 -6
- package/src/TestIndexer.res +6 -5
- package/src/TestIndexer.res.mjs +5 -4
- package/src/db/InternalTable.res +1 -1
- package/src/db/InternalTable.res.mjs +2 -1
- package/src/sources/ChainSources.res +72 -0
- package/src/sources/ChainSources.res.mjs +55 -0
- package/src/sources/EvmHyperSyncSource.res +10 -22
- package/src/sources/EvmHyperSyncSource.res.mjs +9 -41
- package/src/sources/FuelHyperSyncSource.res +10 -18
- package/src/sources/FuelHyperSyncSource.res.mjs +8 -38
- package/src/sources/HyperSync.res +31 -0
- package/src/sources/HyperSync.res.mjs +31 -0
- package/src/sources/HyperSync.resi +16 -0
- package/src/sources/StartBlockResolver.res +173 -0
- package/src/sources/StartBlockResolver.res.mjs +134 -0
- package/src/sources/Svm.res +0 -51
- package/src/sources/Svm.res.mjs +0 -46
- package/src/sources/SvmHyperSyncSource.res +20 -9
- package/src/sources/SvmHyperSyncSource.res.mjs +34 -12
- package/svm.schema.json +80 -66
package/evm.schema.json
CHANGED
|
@@ -494,10 +494,8 @@
|
|
|
494
494
|
"minimum": 0
|
|
495
495
|
},
|
|
496
496
|
"start_block": {
|
|
497
|
-
"description": "The block at which the indexer should start ingesting data",
|
|
498
|
-
"
|
|
499
|
-
"format": "uint64",
|
|
500
|
-
"minimum": 0
|
|
497
|
+
"description": "The block at which the indexer should start ingesting data, or \"latest\" to start from the chain's current head block when the indexer is first deployed. Once resolved, the concrete block is persisted and reused every time the indexer resumes normally (for example recovering from a crash), so downtime is backfilled instead of skipped. Running `envio start`/`dev` with -r (--restart) resets this like any other config change: \"latest\" resolves again, against the head at that time.",
|
|
498
|
+
"$ref": "#/$defs/StartBlock"
|
|
501
499
|
},
|
|
502
500
|
"end_block": {
|
|
503
501
|
"description": "The block at which the indexer should terminate.",
|
|
@@ -685,6 +683,25 @@
|
|
|
685
683
|
"url"
|
|
686
684
|
]
|
|
687
685
|
},
|
|
686
|
+
"StartBlock": {
|
|
687
|
+
"description": "A chain's configured start block: either a concrete block number or the\nliteral \"latest\". Config parsing never touches the network, so `Latest`\nstays unresolved here — it's resolved once at runtime, right before the\nindexer's first-ever persisted state is written, and never re-resolved\non a normal resume (see packages/envio/src/sources/StartBlockResolver.res).\nNote: this repo's `-r`/`--restart` CLI flag wipes the DB and re-deploys\nfrom scratch, so it re-resolves \"latest\" too — \"resume\" here means the\nopposite: recovering from a crash or process restart without `-r`.",
|
|
688
|
+
"anyOf": [
|
|
689
|
+
{
|
|
690
|
+
"type": "integer",
|
|
691
|
+
"format": "uint64",
|
|
692
|
+
"minimum": 0
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
"$ref": "#/$defs/StartBlockTag"
|
|
696
|
+
}
|
|
697
|
+
]
|
|
698
|
+
},
|
|
699
|
+
"StartBlockTag": {
|
|
700
|
+
"type": "string",
|
|
701
|
+
"enum": [
|
|
702
|
+
"latest"
|
|
703
|
+
]
|
|
704
|
+
},
|
|
688
705
|
"ChainContract": {
|
|
689
706
|
"type": "object",
|
|
690
707
|
"properties": {
|
package/fuel.schema.json
CHANGED
|
@@ -328,10 +328,8 @@
|
|
|
328
328
|
]
|
|
329
329
|
},
|
|
330
330
|
"start_block": {
|
|
331
|
-
"description": "The block at which the indexer should start ingesting data",
|
|
332
|
-
"
|
|
333
|
-
"format": "uint64",
|
|
334
|
-
"minimum": 0
|
|
331
|
+
"description": "The block at which the indexer should start ingesting data, or \"latest\" to start from the chain's current head block when the indexer is first deployed. Once resolved, the concrete block is persisted and reused every time the indexer resumes normally (for example recovering from a crash), so downtime is backfilled instead of skipped. Running `envio start`/`dev` with -r (--restart) resets this like any other config change: \"latest\" resolves again, against the head at that time.",
|
|
332
|
+
"$ref": "#/$defs/StartBlock"
|
|
335
333
|
},
|
|
336
334
|
"end_block": {
|
|
337
335
|
"description": "The block at which the indexer should terminate.",
|
|
@@ -388,6 +386,25 @@
|
|
|
388
386
|
"start_block"
|
|
389
387
|
]
|
|
390
388
|
},
|
|
389
|
+
"StartBlock": {
|
|
390
|
+
"description": "A chain's configured start block: either a concrete block number or the\nliteral \"latest\". Config parsing never touches the network, so `Latest`\nstays unresolved here — it's resolved once at runtime, right before the\nindexer's first-ever persisted state is written, and never re-resolved\non a normal resume (see packages/envio/src/sources/StartBlockResolver.res).\nNote: this repo's `-r`/`--restart` CLI flag wipes the DB and re-deploys\nfrom scratch, so it re-resolves \"latest\" too — \"resume\" here means the\nopposite: recovering from a crash or process restart without `-r`.",
|
|
391
|
+
"anyOf": [
|
|
392
|
+
{
|
|
393
|
+
"type": "integer",
|
|
394
|
+
"format": "uint64",
|
|
395
|
+
"minimum": 0
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"$ref": "#/$defs/StartBlockTag"
|
|
399
|
+
}
|
|
400
|
+
]
|
|
401
|
+
},
|
|
402
|
+
"StartBlockTag": {
|
|
403
|
+
"type": "string",
|
|
404
|
+
"enum": [
|
|
405
|
+
"latest"
|
|
406
|
+
]
|
|
407
|
+
},
|
|
391
408
|
"HyperfuelConfig": {
|
|
392
409
|
"type": "object",
|
|
393
410
|
"properties": {
|
package/index.d.ts
CHANGED
|
@@ -1377,7 +1377,7 @@ type SvmNamedAccounts<
|
|
|
1377
1377
|
Acc extends Readonly<Record<string, unknown>>,
|
|
1378
1378
|
Fields extends SvmFieldsSelection,
|
|
1379
1379
|
> = {
|
|
1380
|
-
readonly [K in keyof Acc
|
|
1380
|
+
readonly [K in keyof Acc]: SvmInstructionAccount<Fields, K & string>;
|
|
1381
1381
|
};
|
|
1382
1382
|
|
|
1383
1383
|
/** The parent transaction of a {@link SvmInstruction}, narrowed to the
|
|
@@ -1524,8 +1524,8 @@ export type SvmOnInstructionOptions<
|
|
|
1524
1524
|
/** Program name as declared under `chains[].programs[].name` in
|
|
1525
1525
|
* `config.yaml`. */
|
|
1526
1526
|
readonly program: P;
|
|
1527
|
-
/** Instruction name
|
|
1528
|
-
* `chains[].programs[].instructions[].name`
|
|
1527
|
+
/** Instruction name from the program's IDL, or from
|
|
1528
|
+
* `chains[].programs[].instructions[].name` when the layout is inline. */
|
|
1529
1529
|
readonly instruction: I;
|
|
1530
1530
|
readonly fields?: Fields & SvmFieldsLiteralCheck<Fields>;
|
|
1531
1531
|
readonly where?: SvmOnInstructionWhere<SvmAccountsOf<P, I>>;
|
|
@@ -1768,9 +1768,10 @@ type SvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1768
1768
|
}
|
|
1769
1769
|
? {
|
|
1770
1770
|
/**
|
|
1771
|
-
* Register an instruction handler.
|
|
1772
|
-
*
|
|
1773
|
-
* Handler `fields` is the only
|
|
1771
|
+
* Register an instruction handler. `program` and `instruction`
|
|
1772
|
+
* name an entry from the IDL or YAML. Dispatch uses that
|
|
1773
|
+
* instruction's discriminator. Handler `fields` is the only
|
|
1774
|
+
* source of payload selection.
|
|
1774
1775
|
*/
|
|
1775
1776
|
readonly onInstruction: <
|
|
1776
1777
|
P extends keyof Programs & string,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -68,10 +68,10 @@
|
|
|
68
68
|
"tsx": "4.21.0"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
|
-
"envio-linux-x64": "3.
|
|
72
|
-
"envio-linux-x64-musl": "3.
|
|
73
|
-
"envio-linux-arm64": "3.
|
|
74
|
-
"envio-darwin-x64": "3.
|
|
75
|
-
"envio-darwin-arm64": "3.
|
|
71
|
+
"envio-linux-x64": "3.11.0",
|
|
72
|
+
"envio-linux-x64-musl": "3.11.0",
|
|
73
|
+
"envio-linux-arm64": "3.11.0",
|
|
74
|
+
"envio-darwin-x64": "3.11.0",
|
|
75
|
+
"envio-darwin-arm64": "3.11.0"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/ChainState.res
CHANGED
|
@@ -203,7 +203,9 @@ let makeInternal = (
|
|
|
203
203
|
|
|
204
204
|
chainConfig.contracts->Array.forEach(contract => {
|
|
205
205
|
switch contract.startBlock {
|
|
206
|
-
|
|
206
|
+
// Against the resolved `~startBlock`, which came from storage:
|
|
207
|
+
// `chainConfig.startBlock` still says whatever config.yaml said.
|
|
208
|
+
| Some(contractStartBlock) if contractStartBlock < startBlock =>
|
|
207
209
|
JsError.throwWithMessage(
|
|
208
210
|
`The start block for contract "${contract.name}" is less than the chain start block. This is not supported yet.`,
|
|
209
211
|
)
|
|
@@ -262,78 +264,12 @@ let makeInternal = (
|
|
|
262
264
|
})
|
|
263
265
|
|
|
264
266
|
// Create sources lazily here - this is where API token validation happens
|
|
265
|
-
let
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
let headers = rpc.headers
|
|
272
|
-
{
|
|
273
|
-
url: rpc.url,
|
|
274
|
-
sourceFor: rpc.sourceFor,
|
|
275
|
-
?syncConfig,
|
|
276
|
-
?ws,
|
|
277
|
-
?headers,
|
|
278
|
-
}
|
|
279
|
-
})
|
|
280
|
-
EvmChain.makeSources(
|
|
281
|
-
~chainId,
|
|
282
|
-
~onEventRegistrations=onEventRegistrations->(
|
|
283
|
-
Utils.magic: array<Internal.onEventRegistration> => array<Internal.evmOnEventRegistration>
|
|
284
|
-
),
|
|
285
|
-
~hyperSync=hypersync,
|
|
286
|
-
~rpcs=evmRpcs,
|
|
287
|
-
~lowercaseAddresses,
|
|
288
|
-
~addressStore,
|
|
289
|
-
)
|
|
290
|
-
| Config.FuelSourceConfig({hypersync}) => [
|
|
291
|
-
FuelHyperSyncSource.make({
|
|
292
|
-
chainId,
|
|
293
|
-
endpointUrl: hypersync,
|
|
294
|
-
apiToken: Env.envioApiToken,
|
|
295
|
-
onEventRegistrations,
|
|
296
|
-
addressStore,
|
|
297
|
-
}),
|
|
298
|
-
]
|
|
299
|
-
| Config.SvmSourceConfig({hypersync, rpc}) =>
|
|
300
|
-
switch (hypersync, rpc) {
|
|
301
|
-
| (None, None) =>
|
|
302
|
-
JsError.throwWithMessage(`Chain ${chainId->ChainId.toString} has no SVM data source`)
|
|
303
|
-
| (None, Some(rpc)) => [Svm.makeRPCSource(~chainId, ~rpc)]
|
|
304
|
-
| (Some(hypersyncUrl), _) =>
|
|
305
|
-
// HyperSync drives instruction sync. A configured RPC is ignored for now
|
|
306
|
-
// (RPC fallback isn't wired up yet).
|
|
307
|
-
let apiToken = Env.envioApiToken
|
|
308
|
-
[
|
|
309
|
-
SvmHyperSyncSource.make({
|
|
310
|
-
chainId,
|
|
311
|
-
endpointUrl: hypersyncUrl,
|
|
312
|
-
apiToken,
|
|
313
|
-
onEventRegistrations: onEventRegistrations->(
|
|
314
|
-
Utils.magic: array<Internal.onEventRegistration> => array<
|
|
315
|
-
Internal.svmOnEventRegistration,
|
|
316
|
-
>
|
|
317
|
-
),
|
|
318
|
-
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
|
|
319
|
-
addressStore,
|
|
320
|
-
}),
|
|
321
|
-
]
|
|
322
|
-
}
|
|
323
|
-
| Config.SimulateSourceConfig({items, endBlock, ?transactionStore, ?blockStore}) => [
|
|
324
|
-
SimulateSource.make(
|
|
325
|
-
~items,
|
|
326
|
-
~endBlock,
|
|
327
|
-
~chainId,
|
|
328
|
-
~addressStore,
|
|
329
|
-
~ecosystem=config.ecosystem.name,
|
|
330
|
-
~transactionStore,
|
|
331
|
-
~blockStore,
|
|
332
|
-
),
|
|
333
|
-
]
|
|
334
|
-
// For tests: use ready-to-use sources directly
|
|
335
|
-
| Config.CustomSources(sources) => sources
|
|
336
|
-
}
|
|
267
|
+
let sources = ChainSources.make(
|
|
268
|
+
~chainConfig,
|
|
269
|
+
~onEventRegistrations,
|
|
270
|
+
~addressStore,
|
|
271
|
+
~lowercaseAddresses,
|
|
272
|
+
)
|
|
337
273
|
|
|
338
274
|
let blockStore = BlockStore.make(
|
|
339
275
|
~ecosystem=config.ecosystem.name,
|
package/src/ChainState.res.mjs
CHANGED
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Env from "./Env.res.mjs";
|
|
4
3
|
import * as Svm from "./sources/Svm.res.mjs";
|
|
5
4
|
import * as Core from "./Core.res.mjs";
|
|
6
5
|
import * as Batch from "./Batch.res.mjs";
|
|
7
6
|
import * as Utils from "./Utils.res.mjs";
|
|
8
7
|
import * as ChainId from "./ChainId.res.mjs";
|
|
9
8
|
import * as Logging from "./Logging.res.mjs";
|
|
10
|
-
import * as EvmChain from "./sources/EvmChain.res.mjs";
|
|
11
9
|
import * as FieldMask from "./sources/FieldMask.res.mjs";
|
|
12
10
|
import * as BlockStore from "./sources/BlockStore.res.mjs";
|
|
13
11
|
import * as FetchState from "./FetchState.res.mjs";
|
|
14
12
|
import * as AddressRows from "./AddressRows.res.mjs";
|
|
15
13
|
import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
|
|
16
14
|
import * as AddressStore from "./sources/AddressStore.res.mjs";
|
|
15
|
+
import * as ChainSources from "./sources/ChainSources.res.mjs";
|
|
17
16
|
import * as EntityTables from "./EntityTables.res.mjs";
|
|
18
17
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
19
18
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
20
19
|
import * as SourceManager from "./sources/SourceManager.res.mjs";
|
|
21
20
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
22
|
-
import * as SimulateSource from "./sources/SimulateSource.res.mjs";
|
|
23
21
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
24
22
|
import * as ContractMapping from "./ContractMapping.res.mjs";
|
|
25
23
|
import * as Primitive_float from "@rescript/runtime/lib/es6/Primitive_float.js";
|
|
26
24
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
27
25
|
import * as TransactionStore from "./sources/TransactionStore.res.mjs";
|
|
28
|
-
import * as SvmHyperSyncSource from "./sources/SvmHyperSyncSource.res.mjs";
|
|
29
|
-
import * as FuelHyperSyncSource from "./sources/FuelHyperSyncSource.res.mjs";
|
|
30
26
|
import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
|
|
31
27
|
|
|
32
28
|
function configStorageRows(chainConfig, ecosystem, contractMapping) {
|
|
@@ -128,8 +124,8 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
128
124
|
});
|
|
129
125
|
let onEventRegistrations = match.onEventRegistrations;
|
|
130
126
|
chainConfig.contracts.forEach(contract => {
|
|
131
|
-
let
|
|
132
|
-
if (
|
|
127
|
+
let contractStartBlock = contract.startBlock;
|
|
128
|
+
if (contractStartBlock !== undefined && contractStartBlock < startBlock) {
|
|
133
129
|
return Stdlib_JsError.throwWithMessage(`The start block for contract "` + contract.name + `" is less than the chain start block. This is not supported yet.`);
|
|
134
130
|
}
|
|
135
131
|
});
|
|
@@ -152,55 +148,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
152
148
|
return reorgCheckpoint;
|
|
153
149
|
}
|
|
154
150
|
});
|
|
155
|
-
let
|
|
156
|
-
let sources = chainConfig.sourceConfig;
|
|
157
|
-
let sources$1;
|
|
158
|
-
switch (sources.TAG) {
|
|
159
|
-
case "EvmSourceConfig" :
|
|
160
|
-
let evmRpcs = sources.rpcs.map(rpc => {
|
|
161
|
-
let syncConfig = rpc.syncConfig;
|
|
162
|
-
let ws = rpc.ws;
|
|
163
|
-
let headers = rpc.headers;
|
|
164
|
-
return {
|
|
165
|
-
url: rpc.url,
|
|
166
|
-
sourceFor: rpc.sourceFor,
|
|
167
|
-
syncConfig: syncConfig,
|
|
168
|
-
ws: ws,
|
|
169
|
-
headers: headers
|
|
170
|
-
};
|
|
171
|
-
});
|
|
172
|
-
sources$1 = EvmChain.makeSources(chainId$1, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
|
|
173
|
-
break;
|
|
174
|
-
case "FuelSourceConfig" :
|
|
175
|
-
sources$1 = [FuelHyperSyncSource.make({
|
|
176
|
-
chainId: chainId$1,
|
|
177
|
-
endpointUrl: sources.hypersync,
|
|
178
|
-
apiToken: Env.envioApiToken,
|
|
179
|
-
onEventRegistrations: onEventRegistrations,
|
|
180
|
-
addressStore: addressStore
|
|
181
|
-
})];
|
|
182
|
-
break;
|
|
183
|
-
case "SvmSourceConfig" :
|
|
184
|
-
let rpc = sources.rpc;
|
|
185
|
-
let hypersync = sources.hypersync;
|
|
186
|
-
sources$1 = hypersync !== undefined ? [SvmHyperSyncSource.make({
|
|
187
|
-
chainId: chainId$1,
|
|
188
|
-
endpointUrl: hypersync,
|
|
189
|
-
apiToken: Env.envioApiToken,
|
|
190
|
-
onEventRegistrations: onEventRegistrations,
|
|
191
|
-
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
|
|
192
|
-
addressStore: addressStore
|
|
193
|
-
})] : (
|
|
194
|
-
rpc !== undefined ? [Svm.makeRPCSource(chainId$1, rpc, undefined)] : Stdlib_JsError.throwWithMessage(`Chain ` + ChainId.toString(chainId$1) + ` has no SVM data source`)
|
|
195
|
-
);
|
|
196
|
-
break;
|
|
197
|
-
case "SimulateSourceConfig" :
|
|
198
|
-
sources$1 = [SimulateSource.make(sources.items, sources.endBlock, chainId$1, addressStore, config.ecosystem.name, Primitive_option.some(sources.transactionStore), Primitive_option.some(sources.blockStore))];
|
|
199
|
-
break;
|
|
200
|
-
case "CustomSources" :
|
|
201
|
-
sources$1 = sources._0;
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
151
|
+
let sources = ChainSources.make(chainConfig, onEventRegistrations, addressStore, lowercaseAddresses);
|
|
204
152
|
let blockStore = BlockStore.make(config.ecosystem.name, !lowercaseAddresses);
|
|
205
153
|
if (Utils.$$Array.notEmpty(chainReorgCheckpoints)) {
|
|
206
154
|
let seedPage = BlockStore.fromJs(chainReorgCheckpoints.map(cp => ({
|
|
@@ -211,7 +159,7 @@ function makeFromDbState(chainConfig, resumedChainState, reorgCheckpoints, isInR
|
|
|
211
159
|
}
|
|
212
160
|
let firstEventBlock$1 = fetchState.firstEventBlock;
|
|
213
161
|
let chainDensity = firstEventBlock$1 !== undefined && progressBlockNumber > firstEventBlock$1 && numEventsProcessed > 0 ? numEventsProcessed / (progressBlockNumber - firstEventBlock$1 | 0) : undefined;
|
|
214
|
-
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources
|
|
162
|
+
return make(chainConfig, fetchState, onEventRegistrations, addressStore, SourceManager.make(sources, isRealtime, undefined, undefined, undefined, reducedPollingInterval, undefined, undefined), progressBlockNumber, Primitive_option.some(SafeCheckpointTracking.make(maxReorgDepth, config.shouldRollbackOnReorg, chainReorgCheckpoints)), config.shouldRollbackOnReorg, maxReorgDepth, numEventsProcessed, Primitive_option.some(timestampCaughtUpToHeadOrEndblock), undefined, Primitive_option.some(TransactionStore.make(config.ecosystem.name, !lowercaseAddresses)), Primitive_option.some(chainDensity), Primitive_option.some(blockStore), config.reorgThresholdReadyTolerance, EntityTables.perChain(config.userEntities), logger);
|
|
215
163
|
}
|
|
216
164
|
|
|
217
165
|
function logger(cs) {
|
|
@@ -959,4 +907,4 @@ export {
|
|
|
959
907
|
markReady,
|
|
960
908
|
rollback,
|
|
961
909
|
}
|
|
962
|
-
/*
|
|
910
|
+
/* Svm Not a pure module */
|
package/src/Config.res
CHANGED
|
@@ -26,10 +26,18 @@ type evmRpcConfig = {
|
|
|
26
26
|
headers: option<dict<string>>,
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// Unboxed so the runtime value is exactly what the public config JSON holds -
|
|
30
|
+
// a number, or the string "latest" - which is why `startBlockSchema` only has
|
|
31
|
+
// to validate it rather than convert it.
|
|
32
|
+
@unboxed
|
|
33
|
+
type startBlock =
|
|
34
|
+
| Block(int)
|
|
35
|
+
| @as("latest") Latest
|
|
36
|
+
|
|
29
37
|
type sourceConfig =
|
|
30
38
|
| EvmSourceConfig({hypersync: option<string>, rpcs: array<evmRpcConfig>})
|
|
31
39
|
| FuelSourceConfig({hypersync: string})
|
|
32
|
-
| SvmSourceConfig({hypersync:
|
|
40
|
+
| SvmSourceConfig({hypersync: string})
|
|
33
41
|
// A `simulate` run: the items the test fed in, parsed against the chain's
|
|
34
42
|
// registrations. The source itself is built with the chain's address store,
|
|
35
43
|
// like every other source, so it can apply the same gates.
|
|
@@ -46,7 +54,11 @@ type chain = {
|
|
|
46
54
|
name: string,
|
|
47
55
|
id: ChainId.t,
|
|
48
56
|
ecosystem: Ecosystem.name,
|
|
49
|
-
|
|
57
|
+
// What config.yaml says, never rewritten. Once `Latest` is resolved against
|
|
58
|
+
// the chain's head the block lives in the database
|
|
59
|
+
// (`envio_chains.start_block`), and that is what every consumer past startup
|
|
60
|
+
// reads.
|
|
61
|
+
startBlock: startBlock,
|
|
50
62
|
endBlock?: int,
|
|
51
63
|
maxReorgDepth: int,
|
|
52
64
|
blockLag: int,
|
|
@@ -157,10 +169,36 @@ let chainContractSchema = S.schema(s =>
|
|
|
157
169
|
}
|
|
158
170
|
)
|
|
159
171
|
|
|
172
|
+
// For everything downstream of `StartBlockResolver`, which rewrites `Latest`
|
|
173
|
+
// into the chain's head before storage is initialized. The throw is an
|
|
174
|
+
// invariant check, not a case a user can reach.
|
|
175
|
+
let startBlockOrThrow = (chain: chain) =>
|
|
176
|
+
switch chain.startBlock {
|
|
177
|
+
| Block(startBlock) => startBlock
|
|
178
|
+
| Latest =>
|
|
179
|
+
JsError.throwWithMessage(
|
|
180
|
+
`Chain ${chain.id->ChainId.toString}: the "latest" start block was read before it was resolved. This is a bug in envio - please report it.`,
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// For the paths that have no chain to read a head from - the test indexer and
|
|
185
|
+
// simulated items - where `Latest` never gets resolved and every simulated
|
|
186
|
+
// block should be in range.
|
|
187
|
+
let startBlockOrZero = (chain: chain) =>
|
|
188
|
+
switch chain.startBlock {
|
|
189
|
+
| Block(startBlock) => startBlock
|
|
190
|
+
| Latest => 0
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let startBlockSchema = S.union([
|
|
194
|
+
S.int->S.shape(n => Block(n)),
|
|
195
|
+
S.literal("latest")->S.shape(_ => Latest),
|
|
196
|
+
])
|
|
197
|
+
|
|
160
198
|
let publicConfigChainSchema = S.schema(s =>
|
|
161
199
|
{
|
|
162
200
|
"id": s.matches(ChainId.schema),
|
|
163
|
-
"startBlock": s.matches(
|
|
201
|
+
"startBlock": s.matches(startBlockSchema),
|
|
164
202
|
"endBlock": s.matches(S.option(S.int)),
|
|
165
203
|
"maxReorgDepth": s.matches(S.option(S.int)),
|
|
166
204
|
"blockLag": s.matches(S.option(S.int)),
|
|
@@ -168,23 +206,37 @@ let publicConfigChainSchema = S.schema(s =>
|
|
|
168
206
|
"hypersync": s.matches(S.option(S.string)),
|
|
169
207
|
"rpcs": s.matches(S.option(S.array(rpcConfigSchema))),
|
|
170
208
|
// SVM source config
|
|
171
|
-
"rpc": s.matches(S.option(S.string)),
|
|
172
209
|
// Per-chain contract data (addresses and optional start block)
|
|
173
210
|
"contracts": s.matches(S.option(S.dict(chainContractSchema))),
|
|
174
211
|
}
|
|
175
212
|
)
|
|
176
213
|
|
|
214
|
+
type svmAccountSlotItem = {"name": option<string>, "optional": option<bool>}
|
|
215
|
+
|
|
216
|
+
let svmAccountSlotSchema: S.t<svmAccountSlotItem> = S.schema(s =>
|
|
217
|
+
{
|
|
218
|
+
"name": s.matches(S.option(S.string)),
|
|
219
|
+
"optional": s.matches(S.option(S.bool)),
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
let svmAccountSlotFromItem = (slot: svmAccountSlotItem): Internal.svmAccountSlot =>
|
|
224
|
+
switch (slot["name"], slot["optional"]) {
|
|
225
|
+
| (None, _) => Unnamed
|
|
226
|
+
| (Some(name), Some(true)) => Optional(name)
|
|
227
|
+
| (Some(name), _) => Required(name)
|
|
228
|
+
}
|
|
229
|
+
|
|
177
230
|
let svmEventDescriptorSchema = S.schema(s =>
|
|
178
231
|
{
|
|
179
232
|
"discriminator": s.matches(S.option(S.string)),
|
|
180
|
-
"accounts": s.matches(S.option(S.array(
|
|
233
|
+
"accounts": s.matches(S.option(S.array(svmAccountSlotSchema))),
|
|
181
234
|
"args": s.matches(S.option(S.json(~validate=false))),
|
|
182
235
|
}
|
|
183
236
|
)
|
|
184
237
|
|
|
185
238
|
let svmAbiSchema = S.schema(s =>
|
|
186
239
|
{
|
|
187
|
-
"programId": s.matches(S.string),
|
|
188
240
|
"definedTypes": s.matches(S.json(~validate=false)),
|
|
189
241
|
"source": s.matches(S.string),
|
|
190
242
|
}
|
|
@@ -640,7 +692,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
640
692
|
"eventSignatures": array<string>,
|
|
641
693
|
"events": option<array<_>>,
|
|
642
694
|
"svmAbi": option<{
|
|
643
|
-
"programId": string,
|
|
644
695
|
"definedTypes": JSON.t,
|
|
645
696
|
"source": string,
|
|
646
697
|
}>,
|
|
@@ -660,7 +711,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
660
711
|
contractConfig->(
|
|
661
712
|
Utils.magic: _ => {
|
|
662
713
|
"svmAbi": option<{
|
|
663
|
-
"programId": string,
|
|
664
714
|
"definedTypes": JSON.t,
|
|
665
715
|
"source": string,
|
|
666
716
|
}>,
|
|
@@ -736,7 +786,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
736
786
|
Utils.magic: _ => {
|
|
737
787
|
"svm": option<{
|
|
738
788
|
"discriminator": option<string>,
|
|
739
|
-
"accounts": option<array<
|
|
789
|
+
"accounts": option<array<svmAccountSlotItem>>,
|
|
740
790
|
"args": option<JSON.t>,
|
|
741
791
|
}>,
|
|
742
792
|
}
|
|
@@ -753,7 +803,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
753
803
|
~instructionName=eventName,
|
|
754
804
|
~programId,
|
|
755
805
|
~discriminator=svm["discriminator"],
|
|
756
|
-
~accounts=svm["accounts"]->Option.getOr([]),
|
|
806
|
+
~accounts=svm["accounts"]->Option.getOr([])->Array.map(svmAccountSlotFromItem),
|
|
757
807
|
~args=svm["args"]->Option.getOr(JSON.Null),
|
|
758
808
|
~definedTypes=svmDefinedTypes,
|
|
759
809
|
) :> Internal.eventConfig)
|
|
@@ -807,6 +857,15 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
807
857
|
let contracts =
|
|
808
858
|
contractDataByName
|
|
809
859
|
->Dict.toArray
|
|
860
|
+
// Svm programs are defined once for the project and placed per chain by
|
|
861
|
+
// `program_id`. A program the config left off this chain has nothing to
|
|
862
|
+
// index here, and no dynamic registration can add it later.
|
|
863
|
+
->Array.filter(((capitalizedName, _)) =>
|
|
864
|
+
switch ecosystemName {
|
|
865
|
+
| Ecosystem.Svm => chainContracts->Dict.get(capitalizedName)->Option.isSome
|
|
866
|
+
| _ => true
|
|
867
|
+
}
|
|
868
|
+
)
|
|
810
869
|
->Array.map(((capitalizedName, contractData)) => {
|
|
811
870
|
let chainContract = chainContracts->Dict.get(capitalizedName)
|
|
812
871
|
let rawAddresses =
|
|
@@ -914,14 +973,11 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
914
973
|
JsError.throwWithMessage(`Chain ${chainName} is missing hypersync endpoint in config`)
|
|
915
974
|
}
|
|
916
975
|
| Ecosystem.Svm =>
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
JsError.throwWithMessage(
|
|
921
|
-
`Chain ${chainName} is missing a data source: provide either an rpc endpoint or an experimental hypersync config`,
|
|
922
|
-
)
|
|
976
|
+
switch publicChainConfig["hypersync"] {
|
|
977
|
+
| Some(hypersync) => SvmSourceConfig({hypersync: hypersync})
|
|
978
|
+
| None =>
|
|
979
|
+
JsError.throwWithMessage(`Chain ${chainName} is missing hypersync endpoint in config`)
|
|
923
980
|
}
|
|
924
|
-
SvmSourceConfig({hypersync, rpc})
|
|
925
981
|
}
|
|
926
982
|
|
|
927
983
|
{
|
|
@@ -1168,7 +1224,6 @@ let stripSensitiveData = (json: JSON.t): JSON.t => {
|
|
|
1168
1224
|
switch chainJson {
|
|
1169
1225
|
| Object(chain) => {
|
|
1170
1226
|
chain->Utils.Dict.deleteInPlace("rpcs")
|
|
1171
|
-
chain->Utils.Dict.deleteInPlace("rpc")
|
|
1172
1227
|
chain->Utils.Dict.deleteInPlace("hypersync")
|
|
1173
1228
|
}
|
|
1174
1229
|
| _ => ()
|