envio 3.3.0-alpha.0 → 3.3.0-alpha.10
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 +10 -0
- package/index.d.ts +45 -25
- package/package.json +7 -7
- package/src/Address.res +5 -2
- package/src/Address.res.mjs +3 -1
- package/src/Api.res +1 -1
- package/src/Api.res.mjs +1 -1
- package/src/Batch.res +0 -6
- package/src/Batch.res.mjs +1 -12
- package/src/BatchProcessing.res +7 -12
- package/src/BatchProcessing.res.mjs +8 -7
- package/src/Bin.res +3 -0
- package/src/Bin.res.mjs +4 -0
- package/src/ChainFetching.res +48 -16
- package/src/ChainFetching.res.mjs +36 -17
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +628 -149
- package/src/ChainState.res.mjs +440 -102
- package/src/ChainState.resi +74 -4
- package/src/Config.res +61 -33
- package/src/Config.res.mjs +50 -10
- package/src/ContractRegisterContext.res +2 -12
- package/src/ContractRegisterContext.res.mjs +3 -5
- package/src/Core.res +14 -3
- package/src/CrossChainState.res +142 -93
- package/src/CrossChainState.res.mjs +88 -60
- package/src/CrossChainState.resi +2 -10
- package/src/Ecosystem.res +5 -6
- package/src/Ecosystem.res.mjs +3 -3
- package/src/Env.res +0 -7
- package/src/Env.res.mjs +0 -6
- package/src/Envio.res +17 -11
- package/src/EnvioGlobal.res +53 -0
- package/src/EnvioGlobal.res.mjs +31 -0
- package/src/EventConfigBuilder.res +177 -88
- package/src/EventConfigBuilder.res.mjs +99 -42
- package/src/EventProcessing.res +74 -28
- package/src/EventProcessing.res.mjs +55 -29
- package/src/ExitOnCaughtUp.res +10 -2
- package/src/ExitOnCaughtUp.res.mjs +10 -1
- package/src/FetchState.res +558 -300
- package/src/FetchState.res.mjs +437 -423
- package/src/HandlerLoader.res +8 -104
- package/src/HandlerLoader.res.mjs +2 -81
- package/src/HandlerRegister.res +518 -144
- package/src/HandlerRegister.res.mjs +285 -133
- package/src/HandlerRegister.resi +24 -8
- package/src/IndexerLoop.res +2 -3
- package/src/IndexerLoop.res.mjs +1 -1
- package/src/IndexerState.res +10 -11
- package/src/IndexerState.res.mjs +14 -11
- package/src/IndexerState.resi +2 -5
- package/src/IndexingAddresses.res +108 -0
- package/src/IndexingAddresses.res.mjs +101 -0
- package/src/IndexingAddresses.resi +34 -0
- package/src/Internal.res +155 -46
- package/src/Internal.res.mjs +30 -2
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +6 -6
- package/src/LogSelection.res +102 -165
- package/src/LogSelection.res.mjs +101 -116
- package/src/Main.res +71 -236
- package/src/Main.res.mjs +60 -145
- package/src/Metrics.res +74 -0
- package/src/Metrics.res.mjs +75 -0
- package/src/PgStorage.res +4 -4
- package/src/PgStorage.res.mjs +5 -5
- package/src/Prometheus.res +10 -74
- package/src/Prometheus.res.mjs +124 -186
- package/src/PruneStaleHistory.res +2 -2
- package/src/PruneStaleHistory.res.mjs +3 -3
- package/src/RawEvent.res +7 -2
- package/src/RawEvent.res.mjs +4 -4
- package/src/Rollback.res +3 -3
- package/src/Rollback.res.mjs +4 -4
- package/src/RollbackCommit.res +4 -1
- package/src/RollbackCommit.res.mjs +3 -2
- package/src/SimulateDeadInputTracker.res +85 -0
- package/src/SimulateDeadInputTracker.res.mjs +73 -0
- package/src/SimulateDeadInputTracker.resi +12 -0
- package/src/SimulateItems.res +91 -23
- package/src/SimulateItems.res.mjs +55 -32
- package/src/TestIndexer.res +54 -40
- package/src/TestIndexer.res.mjs +37 -30
- package/src/bindings/Performance.res +7 -0
- package/src/bindings/Performance.res.mjs +21 -0
- package/src/bindings/Performance.resi +7 -0
- package/src/bindings/Viem.res +0 -41
- package/src/bindings/Viem.res.mjs +1 -43
- package/src/sources/BlockStore.res +46 -0
- package/src/sources/BlockStore.res.mjs +24 -0
- package/src/sources/EventRouter.res +36 -33
- package/src/sources/EventRouter.res.mjs +13 -14
- package/src/sources/Evm.res +83 -72
- package/src/sources/Evm.res.mjs +53 -69
- package/src/sources/EvmChain.res +18 -20
- package/src/sources/EvmChain.res.mjs +15 -15
- package/src/sources/EvmRpcClient.res +74 -5
- package/src/sources/EvmRpcClient.res.mjs +13 -4
- package/src/sources/FieldMask.res +39 -0
- package/src/sources/FieldMask.res.mjs +42 -0
- package/src/sources/Fuel.res +9 -6
- package/src/sources/Fuel.res.mjs +5 -10
- package/src/sources/HyperFuelSource.res +46 -50
- package/src/sources/HyperFuelSource.res.mjs +64 -54
- package/src/sources/HyperSync.res +39 -7
- package/src/sources/HyperSync.res.mjs +57 -37
- package/src/sources/HyperSync.resi +12 -2
- package/src/sources/HyperSyncClient.res +22 -111
- package/src/sources/HyperSyncClient.res.mjs +2 -30
- package/src/sources/HyperSyncHeightStream.res +1 -8
- package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
- package/src/sources/HyperSyncSource.res +94 -107
- package/src/sources/HyperSyncSource.res.mjs +82 -86
- package/src/sources/Rpc.res +15 -47
- package/src/sources/Rpc.res.mjs +25 -56
- package/src/sources/RpcSource.res +320 -467
- package/src/sources/RpcSource.res.mjs +268 -385
- package/src/sources/RpcWebSocketHeightStream.res +0 -5
- package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
- package/src/sources/SimulateSource.res +10 -4
- package/src/sources/SimulateSource.res.mjs +16 -6
- package/src/sources/Source.res +34 -6
- package/src/sources/SourceManager.res +86 -27
- package/src/sources/SourceManager.res.mjs +84 -47
- package/src/sources/SourceManager.resi +12 -3
- package/src/sources/Svm.res +31 -16
- package/src/sources/Svm.res.mjs +36 -13
- package/src/sources/SvmHyperSyncClient.res +5 -32
- package/src/sources/SvmHyperSyncSource.res +159 -134
- package/src/sources/SvmHyperSyncSource.res.mjs +164 -129
- package/src/sources/TransactionStore.res +49 -0
- package/src/sources/TransactionStore.res.mjs +30 -0
- package/src/tui/Tui.res +13 -16
- package/src/tui/Tui.res.mjs +12 -14
- package/svm.schema.json +49 -37
- package/src/bindings/Hrtime.res +0 -58
- package/src/bindings/Hrtime.res.mjs +0 -90
- package/src/bindings/Hrtime.resi +0 -30
package/evm.schema.json
CHANGED
|
@@ -548,6 +548,16 @@
|
|
|
548
548
|
"null"
|
|
549
549
|
]
|
|
550
550
|
},
|
|
551
|
+
"headers": {
|
|
552
|
+
"description": "Optional HTTP headers sent with every request to this RPC endpoint, e.g. an Authorization bearer token for gated endpoints. Values support ${ENV_VAR} interpolation.",
|
|
553
|
+
"type": [
|
|
554
|
+
"object",
|
|
555
|
+
"null"
|
|
556
|
+
],
|
|
557
|
+
"additionalProperties": {
|
|
558
|
+
"type": "string"
|
|
559
|
+
}
|
|
560
|
+
},
|
|
551
561
|
"initial_block_interval": {
|
|
552
562
|
"description": "The starting interval in range of blocks per query",
|
|
553
563
|
"type": [
|
package/index.d.ts
CHANGED
|
@@ -979,13 +979,24 @@ export type SvmInstructionParams = {
|
|
|
979
979
|
readonly extraAccounts: readonly string[];
|
|
980
980
|
};
|
|
981
981
|
|
|
982
|
-
/**
|
|
982
|
+
/** Permissive fallback shape for an instruction's `block`. The generated
|
|
983
|
+
* per-instruction type narrows this to `slot`/`hash` (always present) and
|
|
984
|
+
* `time` (always present but possibly `undefined`), plus the selected
|
|
985
|
+
* `field_selection.block_fields`. */
|
|
983
986
|
export type SvmInstructionBlock = {
|
|
984
987
|
/** Slot this instruction's block was matched in. */
|
|
985
988
|
readonly slot: number;
|
|
986
|
-
|
|
987
|
-
|
|
989
|
+
/** Unix block time (seconds). Absent when HyperSync/Solana doesn't report a
|
|
990
|
+
* block time for this slot. */
|
|
991
|
+
readonly time?: number;
|
|
992
|
+
/** Block hash. */
|
|
988
993
|
readonly hash: string;
|
|
994
|
+
/** Block height. Select via `field_selection.block_fields`. */
|
|
995
|
+
readonly height?: number;
|
|
996
|
+
/** Parent slot. Select via `field_selection.block_fields`. */
|
|
997
|
+
readonly parentSlot?: number;
|
|
998
|
+
/** Parent block hash. Select via `field_selection.block_fields`. */
|
|
999
|
+
readonly parentHash?: string;
|
|
989
1000
|
};
|
|
990
1001
|
|
|
991
1002
|
export type SvmTokenBalance = {
|
|
@@ -997,24 +1008,6 @@ export type SvmTokenBalance = {
|
|
|
997
1008
|
readonly postAmount?: string;
|
|
998
1009
|
};
|
|
999
1010
|
|
|
1000
|
-
/** Parent transaction surfaced when an instruction's
|
|
1001
|
-
* `include_transaction` flag is `true`. */
|
|
1002
|
-
export type SvmTransaction = {
|
|
1003
|
-
readonly signatures: readonly string[];
|
|
1004
|
-
readonly feePayer?: string;
|
|
1005
|
-
readonly success?: boolean;
|
|
1006
|
-
readonly err?: string;
|
|
1007
|
-
/** Lamports. */
|
|
1008
|
-
readonly fee?: bigint;
|
|
1009
|
-
readonly computeUnitsConsumed?: bigint;
|
|
1010
|
-
readonly accountKeys: readonly string[];
|
|
1011
|
-
readonly recentBlockhash?: string;
|
|
1012
|
-
readonly version?: string;
|
|
1013
|
-
/** SPL Token / Token-2022 balance snapshots for this transaction.
|
|
1014
|
-
* Present when `include_token_balances` is `true`. */
|
|
1015
|
-
readonly tokenBalances?: readonly SvmTokenBalance[];
|
|
1016
|
-
};
|
|
1017
|
-
|
|
1018
1011
|
export type SvmLog = {
|
|
1019
1012
|
readonly kind: string;
|
|
1020
1013
|
readonly message: string;
|
|
@@ -1033,6 +1026,8 @@ export type SvmLog = {
|
|
|
1033
1026
|
* are base58 strings. */
|
|
1034
1027
|
export type SvmInstruction<
|
|
1035
1028
|
Params extends SvmInstructionParams = SvmInstructionParams,
|
|
1029
|
+
Tx = SvmTransaction,
|
|
1030
|
+
Block = SvmInstructionBlock,
|
|
1036
1031
|
> = {
|
|
1037
1032
|
/** Program name as declared under `programs[].name` in `config.yaml`. */
|
|
1038
1033
|
readonly programName: string;
|
|
@@ -1050,12 +1045,20 @@ export type SvmInstruction<
|
|
|
1050
1045
|
readonly d8?: string;
|
|
1051
1046
|
/** Borsh-decoded params. Present when a schema is configured and matched. */
|
|
1052
1047
|
readonly params?: Params;
|
|
1053
|
-
/**
|
|
1054
|
-
|
|
1048
|
+
/** Parent transaction. Carries only the fields selected via this
|
|
1049
|
+
* instruction's `field_selection`; unselected fields are typed as
|
|
1050
|
+
* `FieldNotSelected<...>` so reading them is a compile error. Always present
|
|
1051
|
+
* (`{}` when no fields are selected). */
|
|
1052
|
+
readonly transaction: Tx;
|
|
1055
1053
|
/** Present when the instruction's `include_logs` is `true`; only logs
|
|
1056
1054
|
* scoped to this exact instruction (matching `instruction_address`). */
|
|
1057
1055
|
readonly logs?: readonly SvmLog[];
|
|
1058
|
-
|
|
1056
|
+
/** The block this instruction's slot belongs to. Carries `slot`/`hash`
|
|
1057
|
+
* (always present) and `time` (always present but possibly `undefined`),
|
|
1058
|
+
* plus the fields selected via this instruction's
|
|
1059
|
+
* `field_selection.block_fields`; unselected fields are typed as
|
|
1060
|
+
* `FieldNotSelected<...>`. */
|
|
1061
|
+
readonly block: Block;
|
|
1059
1062
|
};
|
|
1060
1063
|
|
|
1061
1064
|
/** Arguments passed to handlers registered via `indexer.onInstruction`. */
|
|
@@ -1327,7 +1330,11 @@ type SvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1327
1330
|
handler: (
|
|
1328
1331
|
args: SvmOnInstructionHandlerArgs<
|
|
1329
1332
|
Config,
|
|
1330
|
-
SvmInstruction<
|
|
1333
|
+
SvmInstruction<
|
|
1334
|
+
SvmParamsFromProgramTable<Programs[P][I]>,
|
|
1335
|
+
Programs[P][I]["transaction"],
|
|
1336
|
+
Programs[P][I]["block"]
|
|
1337
|
+
>
|
|
1331
1338
|
>,
|
|
1332
1339
|
) => Promise<void>,
|
|
1333
1340
|
) => void;
|
|
@@ -1695,6 +1702,7 @@ type EvmContractsT = GlobalConfig extends { evm: { contracts: infer X extends
|
|
|
1695
1702
|
type FuelChainsT = GlobalConfig extends { fuel: { chains: infer X extends Record<string, { id: number }> } } ? X : {};
|
|
1696
1703
|
type FuelContractsT = GlobalConfig extends { fuel: { contracts: infer X extends Record<string, Record<string, any>> } } ? X : {};
|
|
1697
1704
|
type SvmChainsT = GlobalConfig extends { svm: { chains: infer X extends Record<string, { id: number }> } } ? X : {};
|
|
1705
|
+
type SvmProgramsT = GlobalConfig extends { svm: { programs: infer X extends Record<string, Record<string, any>> } } ? X : {};
|
|
1698
1706
|
type EntitiesT = GlobalConfig extends { entities: infer X extends Record<string, object> } ? X : {};
|
|
1699
1707
|
type EnumsT = GlobalConfig extends { enums: infer X extends Record<string, any> } ? X : {};
|
|
1700
1708
|
|
|
@@ -1716,6 +1724,18 @@ export type FuelChainId = IsEmptyObject<FuelChainsT> extends true ? NotConfigure
|
|
|
1716
1724
|
/** Union of all configured SVM chain IDs. */
|
|
1717
1725
|
export type SvmChainId = IsEmptyObject<SvmChainsT> extends true ? NotConfigured<"SvmChainId", "Configure SVM chains"> : SvmChainsT [keyof SvmChainsT ]["id"];
|
|
1718
1726
|
|
|
1727
|
+
/** The SVM parent-transaction type generated from this project's
|
|
1728
|
+
* `field_selection`: the union of every instruction's `transaction` shape,
|
|
1729
|
+
* with unselected fields typed as `FieldNotSelected<...>`. Resolves to a
|
|
1730
|
+
* `NotConfigured` hint until `envio codegen` augments {@link Global}. */
|
|
1731
|
+
export type SvmTransaction = IsEmptyObject<SvmProgramsT> extends true
|
|
1732
|
+
? NotConfigured<"SvmTransaction", "Configure SVM programs">
|
|
1733
|
+
: {
|
|
1734
|
+
[P in keyof SvmProgramsT]: {
|
|
1735
|
+
[I in keyof SvmProgramsT[P]]: SvmProgramsT[P][I]["transaction"];
|
|
1736
|
+
}[keyof SvmProgramsT[P]];
|
|
1737
|
+
}[keyof SvmProgramsT];
|
|
1738
|
+
|
|
1719
1739
|
/** Lookup an EVM event type by contract and event name. Without generics,
|
|
1720
1740
|
* resolves to the discriminated union of every EVM event in the project. */
|
|
1721
1741
|
export type EvmEvent<
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.3.0-alpha.
|
|
3
|
+
"version": "3.3.0-alpha.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"yargs": "17.7.2",
|
|
58
58
|
"@rescript/runtime": "12.2.0",
|
|
59
59
|
"rescript-schema": "9.5.1",
|
|
60
|
-
"viem": "2.
|
|
60
|
+
"viem": "2.54.0",
|
|
61
61
|
"dotenv": "16.4.5",
|
|
62
62
|
"date-fns": "3.3.1",
|
|
63
63
|
"@rescript/react": "0.14.1",
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
"tsx": "4.21.0"
|
|
70
70
|
},
|
|
71
71
|
"optionalDependencies": {
|
|
72
|
-
"envio-linux-x64": "3.3.0-alpha.
|
|
73
|
-
"envio-linux-x64-musl": "3.3.0-alpha.
|
|
74
|
-
"envio-linux-arm64": "3.3.0-alpha.
|
|
75
|
-
"envio-darwin-x64": "3.3.0-alpha.
|
|
76
|
-
"envio-darwin-arm64": "3.3.0-alpha.
|
|
72
|
+
"envio-linux-x64": "3.3.0-alpha.10",
|
|
73
|
+
"envio-linux-x64-musl": "3.3.0-alpha.10",
|
|
74
|
+
"envio-linux-arm64": "3.3.0-alpha.10",
|
|
75
|
+
"envio-darwin-x64": "3.3.0-alpha.10",
|
|
76
|
+
"envio-darwin-arm64": "3.3.0-alpha.10"
|
|
77
77
|
}
|
|
78
78
|
}
|
package/src/Address.res
CHANGED
|
@@ -11,8 +11,11 @@ module Evm = {
|
|
|
11
11
|
external fromStringOrThrow: string => t = "getAddress"
|
|
12
12
|
|
|
13
13
|
// NOTE: the function is named to be overshadowed by the one below, so that we don't have to import viem in the handler code
|
|
14
|
+
// strict:false checks only the 20-byte hex shape, not EIP-55 checksum casing —
|
|
15
|
+
// since we're about to lowercase it anyway, an all-uppercase or wrongly-cased
|
|
16
|
+
// input address is just as valid as one that's already lowercase.
|
|
14
17
|
@module("viem")
|
|
15
|
-
external fromStringLowercaseOrThrow: string => bool = "isAddress"
|
|
18
|
+
external fromStringLowercaseOrThrow: (string, {"strict": bool}) => bool = "isAddress"
|
|
16
19
|
|
|
17
20
|
// Reassign since the function might be used in the handler code
|
|
18
21
|
// and we don't want to have a "viem" import there. It's needed to keep "viem" a dependency
|
|
@@ -20,7 +23,7 @@ module Evm = {
|
|
|
20
23
|
// Also, we want a custom error message, which is searchable in our codebase.
|
|
21
24
|
// Validate that the string is a proper address but return a lowercased value
|
|
22
25
|
let fromStringLowercaseOrThrow = string => {
|
|
23
|
-
if fromStringLowercaseOrThrow(string) {
|
|
26
|
+
if fromStringLowercaseOrThrow(string, {"strict": false}) {
|
|
24
27
|
unsafeFromString(string->String.toLowerCase)
|
|
25
28
|
} else {
|
|
26
29
|
JsError.throwWithMessage(
|
package/src/Address.res.mjs
CHANGED
|
@@ -7,7 +7,9 @@ import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
|
7
7
|
let schema = S$RescriptSchema.setName(S$RescriptSchema.string, "Address");
|
|
8
8
|
|
|
9
9
|
function fromStringLowercaseOrThrow(string) {
|
|
10
|
-
if (Viem.isAddress(string
|
|
10
|
+
if (Viem.isAddress(string, {
|
|
11
|
+
strict: false
|
|
12
|
+
})) {
|
|
11
13
|
return string.toLowerCase();
|
|
12
14
|
} else {
|
|
13
15
|
return Stdlib_JsError.throwWithMessage(`Address "` + string + `" is invalid. Expected a 20-byte hex string starting with 0x.`);
|
package/src/Api.res
CHANGED
|
@@ -9,7 +9,7 @@ let createTestIndexer: unit => unknown = () => {
|
|
|
9
9
|
NodeJs.Path.getDirname(NodeJs.ImportMeta.importMeta),
|
|
10
10
|
"TestIndexerWorker.res.mjs",
|
|
11
11
|
)->NodeJs.Path.toString
|
|
12
|
-
TestIndexer.makeCreateTestIndexer(~config=Config.
|
|
12
|
+
TestIndexer.makeCreateTestIndexer(~config=Config.load(), ~workerPath)()->(
|
|
13
13
|
Utils.magic: TestIndexer.t<'a> => unknown
|
|
14
14
|
)
|
|
15
15
|
}
|
package/src/Api.res.mjs
CHANGED
|
@@ -10,7 +10,7 @@ let indexer = Main.getGlobalIndexer();
|
|
|
10
10
|
|
|
11
11
|
function createTestIndexer() {
|
|
12
12
|
let workerPath = Path.join(NodeJs.Path.getDirname(import.meta), "TestIndexerWorker.res.mjs");
|
|
13
|
-
return TestIndexer.makeCreateTestIndexer(Config.
|
|
13
|
+
return TestIndexer.makeCreateTestIndexer(Config.load(), workerPath)();
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export {
|
package/src/Batch.res
CHANGED
|
@@ -34,12 +34,6 @@ type t = {
|
|
|
34
34
|
checkpointEventsProcessed: array<int>,
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
let hasReadyItem = (fetchStates: array<FetchState.t>) => {
|
|
38
|
-
fetchStates->Array.some(fetchState => {
|
|
39
|
-
fetchState->FetchState.isActivelyIndexing && fetchState->FetchState.hasReadyItem
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
37
|
let getProgressedChainsById = {
|
|
44
38
|
let getChainAfterBatchIfProgressed = (
|
|
45
39
|
~chainBeforeBatch: chainBeforeBatch,
|
package/src/Batch.res.mjs
CHANGED
|
@@ -4,16 +4,6 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
4
4
|
import * as FetchState from "./FetchState.res.mjs";
|
|
5
5
|
import * as ReorgDetection from "./ReorgDetection.res.mjs";
|
|
6
6
|
|
|
7
|
-
function hasReadyItem(fetchStates) {
|
|
8
|
-
return fetchStates.some(fetchState => {
|
|
9
|
-
if (FetchState.isActivelyIndexing(fetchState)) {
|
|
10
|
-
return FetchState.hasReadyItem(fetchState);
|
|
11
|
-
} else {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
7
|
function getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, fetchStateAfterBatch, batchSize) {
|
|
18
8
|
if (chainBeforeBatch.progressBlockNumber < progressBlockNumberAfterBatch) {
|
|
19
9
|
return {
|
|
@@ -37,7 +27,7 @@ function getProgressedChainsById(chainsBeforeBatch, batchSizePerChain, progressB
|
|
|
37
27
|
let progressedChain;
|
|
38
28
|
if (batchSize !== undefined) {
|
|
39
29
|
let leftItems = fetchState.buffer.slice(batchSize);
|
|
40
|
-
progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, FetchState.updateInternal(fetchState, undefined,
|
|
30
|
+
progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, FetchState.updateInternal(fetchState, undefined, leftItems, undefined, undefined), batchSize);
|
|
41
31
|
} else {
|
|
42
32
|
progressedChain = getChainAfterBatchIfProgressed(chainBeforeBatch, progressBlockNumberAfterBatch, chainBeforeBatch.fetchState, 0);
|
|
43
33
|
}
|
|
@@ -208,7 +198,6 @@ function findLastEventItem(batch, chainId) {
|
|
|
208
198
|
}
|
|
209
199
|
|
|
210
200
|
export {
|
|
211
|
-
hasReadyItem,
|
|
212
201
|
getProgressedChainsById,
|
|
213
202
|
addReorgCheckpoints,
|
|
214
203
|
prepareBatch,
|
package/src/BatchProcessing.res
CHANGED
|
@@ -77,15 +77,7 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
|
77
77
|
state
|
|
78
78
|
->IndexerState.chainStates
|
|
79
79
|
->Dict.valuesToArray
|
|
80
|
-
->Array.every(cs =>
|
|
81
|
-
let fetchState = switch progressedChainsById->Utils.Dict.dangerouslyGetByIntNonOption(
|
|
82
|
-
(cs->ChainState.fetchState).chainId,
|
|
83
|
-
) {
|
|
84
|
-
| Some(chainAfterBatch) => chainAfterBatch.fetchState
|
|
85
|
-
| None => cs->ChainState.fetchState
|
|
86
|
-
}
|
|
87
|
-
fetchState->FetchState.isReadyToEnterReorgThreshold
|
|
88
|
-
})
|
|
80
|
+
->Array.every(cs => cs->ChainState.isReadyToEnterReorgThresholdAfterBatch(~batch))
|
|
89
81
|
|
|
90
82
|
if shouldEnterReorgThreshold {
|
|
91
83
|
IndexerState.enterReorgThreshold(state)
|
|
@@ -135,6 +127,11 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
|
135
127
|
| Ok() =>
|
|
136
128
|
state->IndexerState.recordProcessedBatch
|
|
137
129
|
|
|
130
|
+
switch state->IndexerState.simulateDeadInputTracker {
|
|
131
|
+
| Some(tracker) => tracker->SimulateDeadInputTracker.recordProcessed(~batch)
|
|
132
|
+
| None => ()
|
|
133
|
+
}
|
|
134
|
+
|
|
138
135
|
if state->IndexerState.isResolvingReorg {
|
|
139
136
|
// A reorg landed while this batch was processing. Apply its progress so
|
|
140
137
|
// the rollback diff is computed against up-to-date chain progress, but
|
|
@@ -171,9 +168,7 @@ and processNextBatch = async (state: IndexerState.t, ~scheduleFetch): unit => {
|
|
|
171
168
|
state
|
|
172
169
|
->IndexerState.chainStates
|
|
173
170
|
->Dict.valuesToArray
|
|
174
|
-
->Array.every(
|
|
175
|
-
cs->ChainState.isProgressAtHead && (cs->ChainState.fetchState).endBlock->Option.isNone
|
|
176
|
-
)
|
|
171
|
+
->Array.every(ChainState.isAtHeadWithoutEndBlock)
|
|
177
172
|
) {
|
|
178
173
|
IndexerState.errorExit(
|
|
179
174
|
state,
|
|
@@ -4,16 +4,16 @@ import * as Utils from "./Utils.res.mjs";
|
|
|
4
4
|
import * as Config from "./Config.res.mjs";
|
|
5
5
|
import * as Logging from "./Logging.res.mjs";
|
|
6
6
|
import * as ChainState from "./ChainState.res.mjs";
|
|
7
|
-
import * as FetchState from "./FetchState.res.mjs";
|
|
8
7
|
import * as IndexerState from "./IndexerState.res.mjs";
|
|
9
8
|
import * as ChainMetadata from "./ChainMetadata.res.mjs";
|
|
10
9
|
import * as ErrorHandling from "./ErrorHandling.res.mjs";
|
|
11
10
|
import * as InMemoryStore from "./InMemoryStore.res.mjs";
|
|
12
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
13
11
|
import * as ExitOnCaughtUp from "./ExitOnCaughtUp.res.mjs";
|
|
14
12
|
import * as EventProcessing from "./EventProcessing.res.mjs";
|
|
13
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
15
14
|
import * as PruneStaleHistory from "./PruneStaleHistory.res.mjs";
|
|
16
15
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
16
|
+
import * as SimulateDeadInputTracker from "./SimulateDeadInputTracker.res.mjs";
|
|
17
17
|
|
|
18
18
|
function yieldTick() {
|
|
19
19
|
return new Promise((resolve, param) => {
|
|
@@ -30,10 +30,7 @@ async function processNextBatch(state, scheduleFetch) {
|
|
|
30
30
|
let batch = IndexerState.createBatch(state, IndexerState.processedCheckpointId(state), IndexerState.config(state).batchSize, isRollbackBatch);
|
|
31
31
|
let progressedChainsById = batch.progressedChainsById;
|
|
32
32
|
let isBelowReorgThreshold = !isInReorgThresholdBeforeUpdate && IndexerState.config(state).shouldRollbackOnReorg;
|
|
33
|
-
let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(cs =>
|
|
34
|
-
let chainAfterBatch = progressedChainsById[ChainState.fetchState(cs).chainId];
|
|
35
|
-
return FetchState.isReadyToEnterReorgThreshold(chainAfterBatch !== undefined ? chainAfterBatch.fetchState : ChainState.fetchState(cs));
|
|
36
|
-
});
|
|
33
|
+
let shouldEnterReorgThreshold = isBelowReorgThreshold && Object.values(IndexerState.chainStates(state)).every(cs => ChainState.isReadyToEnterReorgThresholdAfterBatch(cs, batch));
|
|
37
34
|
if (shouldEnterReorgThreshold) {
|
|
38
35
|
IndexerState.enterReorgThreshold(state);
|
|
39
36
|
}
|
|
@@ -60,6 +57,10 @@ async function processNextBatch(state, scheduleFetch) {
|
|
|
60
57
|
return IndexerState.errorExit(state, errHandler._0);
|
|
61
58
|
}
|
|
62
59
|
IndexerState.recordProcessedBatch(state);
|
|
60
|
+
let tracker = IndexerState.simulateDeadInputTracker(state);
|
|
61
|
+
if (tracker !== undefined) {
|
|
62
|
+
SimulateDeadInputTracker.recordProcessed(Primitive_option.valFromOption(tracker), batch);
|
|
63
|
+
}
|
|
63
64
|
if (IndexerState.isResolvingReorg(state)) {
|
|
64
65
|
return IndexerState.applyBatchProgress(state, batch);
|
|
65
66
|
}
|
|
@@ -75,7 +76,7 @@ async function processNextBatch(state, scheduleFetch) {
|
|
|
75
76
|
}
|
|
76
77
|
if (allCaughtUp && !IndexerState.keepProcessAlive(state)) {
|
|
77
78
|
return await ExitOnCaughtUp.run(state);
|
|
78
|
-
} else if (!allCaughtUp && IndexerState.exitAfterFirstEventBlock(state) && Object.values(IndexerState.chainStates(state)).every(
|
|
79
|
+
} else if (!allCaughtUp && IndexerState.exitAfterFirstEventBlock(state) && Object.values(IndexerState.chainStates(state)).every(ChainState.isAtHeadWithoutEndBlock)) {
|
|
79
80
|
return IndexerState.errorExit(state, ErrorHandling.make(new Error("No events found between startBlock and chain head. Cannot auto-detect endBlock."), undefined, undefined));
|
|
80
81
|
} else {
|
|
81
82
|
ChainMetadata.stage(state);
|
package/src/Bin.res
CHANGED
|
@@ -71,6 +71,9 @@ let run = async args => {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
} catch {
|
|
74
|
+
| Main.FatalError(_) =>
|
|
75
|
+
// Already logged with full context by Main.start's onError; just exit.
|
|
76
|
+
NodeJs.process->NodeJs.exitWithCode(Failure)
|
|
74
77
|
| exn =>
|
|
75
78
|
// Log just the exception's own message — wrapping it in "Failed at
|
|
76
79
|
// initialization" and pino's err serializer buries the real cause under
|
package/src/Bin.res.mjs
CHANGED
|
@@ -79,6 +79,10 @@ async function run(args) {
|
|
|
79
79
|
}
|
|
80
80
|
} catch (raw_exn) {
|
|
81
81
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
82
|
+
if (exn.RE_EXN_ID === Main.FatalError) {
|
|
83
|
+
Process.exit(1);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
82
86
|
let e = Primitive_exceptions.internalToException(exn);
|
|
83
87
|
let message = e.RE_EXN_ID === "JsExn" ? Stdlib_Option.getOr(Stdlib_JsExn.message(e._1), "Failed at initialization") : "Failed at initialization";
|
|
84
88
|
Logging.error(message);
|
package/src/ChainFetching.res
CHANGED
|
@@ -11,7 +11,19 @@ type partitionQueryResponse = {
|
|
|
11
11
|
let runContractRegistersOrThrow = async (
|
|
12
12
|
~itemsWithContractRegister: array<Internal.item>,
|
|
13
13
|
~config: Config.t,
|
|
14
|
+
~transactionStore: option<TransactionStore.t>,
|
|
15
|
+
~blockStore: option<BlockStore.t>,
|
|
14
16
|
) => {
|
|
17
|
+
// contractRegister handlers can read event.transaction and event.block, so
|
|
18
|
+
// materialise the selected fields onto the payloads before running them. All
|
|
19
|
+
// items belong to the chain being fetched, hence its single page stores.
|
|
20
|
+
await ChainState.materializePageItems(
|
|
21
|
+
~items=itemsWithContractRegister,
|
|
22
|
+
~transactionStore,
|
|
23
|
+
~blockStore,
|
|
24
|
+
~ecosystem=config.ecosystem.name,
|
|
25
|
+
)
|
|
26
|
+
|
|
15
27
|
let itemsWithDcs = []
|
|
16
28
|
|
|
17
29
|
let onRegister = (~item: Internal.item, ~contractAddress, ~contractName) => {
|
|
@@ -38,8 +50,8 @@ let runContractRegistersOrThrow = async (
|
|
|
38
50
|
let item = itemsWithContractRegister->Array.getUnsafe(idx)
|
|
39
51
|
let eventItem = item->Internal.castUnsafeEventItem
|
|
40
52
|
let contractRegister = switch eventItem {
|
|
41
|
-
| {
|
|
42
|
-
| {
|
|
53
|
+
| {onEventRegistration: {contractRegister: Some(contractRegister)}} => contractRegister
|
|
54
|
+
| {onEventRegistration: {contractRegister: None, eventConfig: {name: eventName}}} =>
|
|
43
55
|
// Unexpected case, since we should pass only events with contract register to this function
|
|
44
56
|
JsError.throwWithMessage("Contract register is not set for event " ++ eventName)
|
|
45
57
|
}
|
|
@@ -106,6 +118,8 @@ let rec onQueryResponse = async (
|
|
|
106
118
|
let chainState = state->IndexerState.getChainState(~chain)
|
|
107
119
|
let {
|
|
108
120
|
parsedQueueItems,
|
|
121
|
+
transactionStore,
|
|
122
|
+
blockStore,
|
|
109
123
|
latestFetchedBlockNumber,
|
|
110
124
|
latestFetchedBlockTimestamp,
|
|
111
125
|
stats,
|
|
@@ -114,7 +128,7 @@ let rec onQueryResponse = async (
|
|
|
114
128
|
fromBlockQueried,
|
|
115
129
|
} = response
|
|
116
130
|
|
|
117
|
-
if knownHeight >
|
|
131
|
+
if knownHeight > chainState->ChainState.knownHeight {
|
|
118
132
|
Prometheus.SourceHeight.set(
|
|
119
133
|
~blockNumber=knownHeight,
|
|
120
134
|
~chainId=(chainState->ChainState.chainConfig).id,
|
|
@@ -133,6 +147,20 @@ let rec onQueryResponse = async (
|
|
|
133
147
|
~blockRangeSize=latestFetchedBlockNumber - fromBlockQueried + 1,
|
|
134
148
|
)
|
|
135
149
|
|
|
150
|
+
let numContractRegisterEvents = parsedQueueItems->Array.reduce(0, (count, item) => {
|
|
151
|
+
let eventItem = item->Internal.castUnsafeEventItem
|
|
152
|
+
eventItem.onEventRegistration.contractRegister !== None ? count + 1 : count
|
|
153
|
+
})
|
|
154
|
+
Logging.trace({
|
|
155
|
+
"msg": "Finished querying",
|
|
156
|
+
"chainId": chain->ChainMap.Chain.toChainId,
|
|
157
|
+
"partitionId": query.partitionId,
|
|
158
|
+
"fromBlock": fromBlockQueried,
|
|
159
|
+
"toBlock": latestFetchedBlockNumber,
|
|
160
|
+
"numEvents": parsedQueueItems->Array.length,
|
|
161
|
+
"numContractRegisterEvents": numContractRegisterEvents,
|
|
162
|
+
})
|
|
163
|
+
|
|
136
164
|
let reorgResult = chainState->ChainState.registerReorgGuard(~blockHashes, ~knownHeight)
|
|
137
165
|
|
|
138
166
|
let rollbackWithReorgDetectedBlockNumber = switch reorgResult {
|
|
@@ -189,7 +217,7 @@ let rec onQueryResponse = async (
|
|
|
189
217
|
for idx in 0 to parsedQueueItems->Array.length - 1 {
|
|
190
218
|
let item = parsedQueueItems->Array.getUnsafe(idx)
|
|
191
219
|
let eventItem = item->Internal.castUnsafeEventItem
|
|
192
|
-
if eventItem.
|
|
220
|
+
if eventItem.onEventRegistration.contractRegister !== None {
|
|
193
221
|
itemsWithContractRegister->Array.push(item)
|
|
194
222
|
}
|
|
195
223
|
// TODO: Don't really need to keep it in the queue
|
|
@@ -212,6 +240,8 @@ let rec onQueryResponse = async (
|
|
|
212
240
|
blockTimestamp: latestFetchedBlockTimestamp,
|
|
213
241
|
},
|
|
214
242
|
~query,
|
|
243
|
+
~transactionStore,
|
|
244
|
+
~blockStore,
|
|
215
245
|
)
|
|
216
246
|
ChainMetadata.stage(state)
|
|
217
247
|
scheduleFetch()
|
|
@@ -224,6 +254,8 @@ let rec onQueryResponse = async (
|
|
|
224
254
|
switch await runContractRegistersOrThrow(
|
|
225
255
|
~itemsWithContractRegister,
|
|
226
256
|
~config=state->IndexerState.config,
|
|
257
|
+
~transactionStore,
|
|
258
|
+
~blockStore,
|
|
227
259
|
) {
|
|
228
260
|
| exception exn => IndexerState.errorExit(state, exn->ErrorHandling.make)
|
|
229
261
|
| newItemsWithDcs => proceed(~newItemsWithDcs)
|
|
@@ -240,6 +272,8 @@ and applyQueryResponse = (
|
|
|
240
272
|
~knownHeight,
|
|
241
273
|
~latestFetchedBlock,
|
|
242
274
|
~query,
|
|
275
|
+
~transactionStore,
|
|
276
|
+
~blockStore,
|
|
243
277
|
) => {
|
|
244
278
|
let chainState = state->IndexerState.getChainState(~chain)
|
|
245
279
|
let wasFetchingAtHead = chainState->ChainState.isFetchingAtHead
|
|
@@ -250,6 +284,8 @@ and applyQueryResponse = (
|
|
|
250
284
|
~newItems,
|
|
251
285
|
~newItemsWithDcs,
|
|
252
286
|
~knownHeight,
|
|
287
|
+
~transactionStore,
|
|
288
|
+
~blockStore,
|
|
253
289
|
)
|
|
254
290
|
|
|
255
291
|
// In auto-exit mode, set endBlock to the first event's block when events arrive.
|
|
@@ -294,7 +330,7 @@ let finishWaitingForNewBlock = (
|
|
|
294
330
|
->IndexerState.chainStates
|
|
295
331
|
->Dict.valuesToArray
|
|
296
332
|
->Array.every(cs => {
|
|
297
|
-
cs->ChainState.
|
|
333
|
+
cs->ChainState.isReadyToEnterReorgThreshold
|
|
298
334
|
})
|
|
299
335
|
|
|
300
336
|
// Kick processing in case there are block handlers to run.
|
|
@@ -308,8 +344,7 @@ let finishWaitingForNewBlock = (
|
|
|
308
344
|
let fetchChain = async (
|
|
309
345
|
state: IndexerState.t,
|
|
310
346
|
chain,
|
|
311
|
-
~
|
|
312
|
-
~bufferLimit,
|
|
347
|
+
~action,
|
|
313
348
|
~stateId,
|
|
314
349
|
~scheduleFetch,
|
|
315
350
|
~scheduleProcessing,
|
|
@@ -317,19 +352,17 @@ let fetchChain = async (
|
|
|
317
352
|
) => {
|
|
318
353
|
let chainState = state->IndexerState.getChainState(~chain)
|
|
319
354
|
if !(state->IndexerState.isResolvingReorg) && !(state->IndexerState.isStopped) {
|
|
320
|
-
let fetchState = chainState->ChainState.fetchState
|
|
321
355
|
let isRealtime = state->IndexerState.isRealtime
|
|
322
356
|
let sourceManager = chainState->ChainState.sourceManager
|
|
323
357
|
|
|
324
|
-
// Only affects the WaitingForNewBlock branch of
|
|
358
|
+
// Only affects the WaitingForNewBlock branch of dispatch, where
|
|
325
359
|
// there's nothing to fetch. During backfill any such chain is idle.
|
|
326
360
|
let reducedPolling = !isRealtime
|
|
327
361
|
|
|
328
362
|
// Owns its error boundary: launch doesn't catch, so any failure here (the
|
|
329
|
-
// query, response handling, or
|
|
363
|
+
// query, response handling, or dispatch itself) must stop the indexer.
|
|
330
364
|
try {
|
|
331
|
-
await
|
|
332
|
-
~fetchState,
|
|
365
|
+
await chainState->ChainState.dispatch(
|
|
333
366
|
~waitForNewBlock=(~knownHeight) =>
|
|
334
367
|
sourceManager->SourceManager.waitForNewBlock(~knownHeight, ~isRealtime, ~reducedPolling),
|
|
335
368
|
~onNewBlock=(~knownHeight) =>
|
|
@@ -343,12 +376,12 @@ let fetchChain = async (
|
|
|
343
376
|
),
|
|
344
377
|
~executeQuery=async query => {
|
|
345
378
|
// Caught here (not just by the outer try) so the query promise never
|
|
346
|
-
// rejects:
|
|
379
|
+
// rejects: dispatch spins a side-chain off it that would otherwise
|
|
347
380
|
// become an unhandled rejection.
|
|
348
381
|
try {
|
|
349
382
|
let response = await sourceManager->SourceManager.executeQuery(
|
|
350
383
|
~query,
|
|
351
|
-
~knownHeight=
|
|
384
|
+
~knownHeight=chainState->ChainState.knownHeight,
|
|
352
385
|
~isRealtime,
|
|
353
386
|
)
|
|
354
387
|
await onQueryResponse(
|
|
@@ -363,8 +396,7 @@ let fetchChain = async (
|
|
|
363
396
|
| exn => IndexerState.errorExit(state, exn->ErrorHandling.make)
|
|
364
397
|
}
|
|
365
398
|
},
|
|
366
|
-
~
|
|
367
|
-
~bufferLimit,
|
|
399
|
+
~action,
|
|
368
400
|
~stateId,
|
|
369
401
|
)
|
|
370
402
|
} catch {
|