envio 3.0.2-svm-alpha.2 → 3.1.0-rc.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/index.d.ts +1 -167
- package/package.json +6 -6
- package/rescript.json +1 -1
- package/src/ChainFetcher.res +1 -25
- package/src/ChainFetcher.res.mjs +1 -19
- package/src/ChainManager.res +7 -4
- package/src/ChainManager.res.mjs +2 -4
- package/src/Config.res +11 -159
- package/src/Config.res.mjs +16 -60
- package/src/Core.res +4 -25
- package/src/Ctx.res +1 -0
- package/src/Envio.res +0 -103
- package/src/EventConfigBuilder.res +0 -52
- package/src/EventConfigBuilder.res.mjs +0 -32
- package/src/EventProcessing.res +5 -29
- package/src/EventProcessing.res.mjs +11 -20
- package/src/EventUtils.res +0 -9
- package/src/EventUtils.res.mjs +0 -6
- package/src/GlobalState.res +20 -29
- package/src/GlobalState.res.mjs +9 -25
- package/src/HandlerLoader.res +1 -12
- package/src/HandlerLoader.res.mjs +1 -6
- package/src/Hasura.res +0 -43
- package/src/Hasura.res.mjs +0 -38
- package/src/InMemoryStore.res +181 -45
- package/src/InMemoryStore.res.mjs +143 -40
- package/src/InMemoryTable.res +147 -247
- package/src/InMemoryTable.res.mjs +131 -230
- package/src/Internal.res +6 -62
- package/src/Internal.res.mjs +6 -0
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +5 -5
- package/src/Main.res +5 -53
- package/src/Main.res.mjs +4 -33
- package/src/Persistence.res +7 -132
- package/src/Persistence.res.mjs +1 -102
- package/src/PgStorage.res +52 -39
- package/src/PgStorage.res.mjs +48 -28
- package/src/ReorgDetection.res +35 -58
- package/src/ReorgDetection.res.mjs +21 -29
- package/src/SimulateItems.res +10 -23
- package/src/SimulateItems.res.mjs +6 -21
- package/src/Sink.res +2 -2
- package/src/Sink.res.mjs +1 -1
- package/src/TableIndices.res +9 -2
- package/src/TableIndices.res.mjs +7 -1
- package/src/TestIndexer.res +53 -60
- package/src/TestIndexer.res.mjs +54 -59
- package/src/TestIndexerProxyStorage.res +4 -14
- package/src/TestIndexerProxyStorage.res.mjs +1 -5
- package/src/UserContext.res +2 -4
- package/src/UserContext.res.mjs +4 -5
- package/src/Utils.res +0 -2
- package/src/Utils.res.mjs +0 -3
- package/src/bindings/ClickHouse.res +45 -38
- package/src/bindings/ClickHouse.res.mjs +16 -17
- package/src/db/InternalTable.res +59 -18
- package/src/db/InternalTable.res.mjs +47 -16
- package/src/sources/EventRouter.res +0 -65
- package/src/sources/EventRouter.res.mjs +0 -43
- package/src/sources/HyperFuelSource.res +14 -57
- package/src/sources/HyperFuelSource.res.mjs +18 -38
- package/src/sources/HyperSync.res +36 -101
- package/src/sources/HyperSync.res.mjs +42 -96
- package/src/sources/HyperSync.resi +4 -22
- package/src/sources/HyperSyncClient.res +49 -56
- package/src/sources/HyperSyncClient.res.mjs +16 -18
- package/src/sources/HyperSyncSource.res +70 -137
- package/src/sources/HyperSyncSource.res.mjs +61 -110
- package/src/sources/RpcSource.res +32 -15
- package/src/sources/RpcSource.res.mjs +44 -30
- package/src/sources/SimulateSource.res +1 -7
- package/src/sources/SimulateSource.res.mjs +1 -7
- package/src/sources/Source.res +8 -1
- package/src/sources/SourceManager.res +9 -0
- package/src/sources/SourceManager.res.mjs +10 -0
- package/src/sources/SourceManager.resi +2 -0
- package/src/sources/Svm.res +2 -2
- package/src/sources/Svm.res.mjs +2 -3
- package/src/tui/Tui.res +2 -9
- package/src/tui/Tui.res.mjs +4 -19
- package/src/tui/components/TuiData.res +0 -3
- package/svm.schema.json +0 -424
- package/src/SvmTypes.res +0 -9
- package/src/SvmTypes.res.mjs +0 -14
- package/src/sources/HyperSyncSolanaClient.res +0 -251
- package/src/sources/HyperSyncSolanaClient.res.mjs +0 -25
- package/src/sources/HyperSyncSolanaSource.res +0 -588
- package/src/sources/HyperSyncSolanaSource.res.mjs +0 -502
package/index.d.ts
CHANGED
|
@@ -959,137 +959,6 @@ export type SvmOnSlotOptions<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
959
959
|
readonly where?: (args: SvmOnSlotWhereArgs<Config>) => SvmOnSlotWhereResult;
|
|
960
960
|
};
|
|
961
961
|
|
|
962
|
-
// ============== SVM onInstruction types ==============
|
|
963
|
-
|
|
964
|
-
/** Borsh-decoded view of an instruction. Present whenever a `ProgramSchema`
|
|
965
|
-
* was attached to the program (bundled, Anchor IDL, or hand-written
|
|
966
|
-
* `accounts`/`args` in YAML). Absent when no schema applies or the
|
|
967
|
-
* discriminator didn't match any registered instruction. */
|
|
968
|
-
export type SvmDecodedInstruction = {
|
|
969
|
-
/** Schema-declared instruction name. */
|
|
970
|
-
readonly name: string;
|
|
971
|
-
/** Borsh-decoded args object. POC types this as `unknown`; narrow with a
|
|
972
|
-
* locally-declared type until the typed-args codegen lands. */
|
|
973
|
-
readonly args: unknown;
|
|
974
|
-
/** Named accounts in schema order. Keys are exactly the schema-declared
|
|
975
|
-
* names; values are base58 pubkeys. */
|
|
976
|
-
readonly accounts: Readonly<Record<string, string>>;
|
|
977
|
-
/** Accounts beyond the schema's named list (Anchor `remaining_accounts`,
|
|
978
|
-
* IDL drift). Empty when counts match the schema. */
|
|
979
|
-
readonly extraAccounts: readonly string[];
|
|
980
|
-
};
|
|
981
|
-
|
|
982
|
-
/** A single Solana instruction matched by the indexer.
|
|
983
|
-
*
|
|
984
|
-
* `data` and discriminator prefixes are `0x`-prefixed hex strings; accounts
|
|
985
|
-
* are base58 strings. When a Borsh schema is configured (bundled, Anchor
|
|
986
|
-
* IDL, or hand-written YAML), `decoded` carries the named-accounts +
|
|
987
|
-
* decoded-args view. */
|
|
988
|
-
export type SvmInstruction = {
|
|
989
|
-
readonly programId: string;
|
|
990
|
-
readonly data: string;
|
|
991
|
-
readonly accounts: readonly string[];
|
|
992
|
-
readonly instructionAddress: readonly number[];
|
|
993
|
-
readonly isInner: boolean;
|
|
994
|
-
readonly d1?: string;
|
|
995
|
-
readonly d2?: string;
|
|
996
|
-
readonly d4?: string;
|
|
997
|
-
readonly d8?: string;
|
|
998
|
-
readonly decoded?: SvmDecodedInstruction;
|
|
999
|
-
};
|
|
1000
|
-
|
|
1001
|
-
export type SvmTokenBalance = {
|
|
1002
|
-
readonly account?: string;
|
|
1003
|
-
readonly mint?: string;
|
|
1004
|
-
readonly owner?: string;
|
|
1005
|
-
/** u64 decimal string. Cast with BigInt(...) for arithmetic. */
|
|
1006
|
-
readonly preAmount?: string;
|
|
1007
|
-
readonly postAmount?: string;
|
|
1008
|
-
};
|
|
1009
|
-
|
|
1010
|
-
/** Parent transaction surfaced when an instruction's
|
|
1011
|
-
* `include_transaction` flag is `true`. */
|
|
1012
|
-
export type SvmTransaction = {
|
|
1013
|
-
readonly signatures: readonly string[];
|
|
1014
|
-
readonly feePayer?: string;
|
|
1015
|
-
readonly success?: boolean;
|
|
1016
|
-
readonly err?: string;
|
|
1017
|
-
/** Lamports. */
|
|
1018
|
-
readonly fee?: bigint;
|
|
1019
|
-
readonly computeUnitsConsumed?: bigint;
|
|
1020
|
-
readonly accountKeys: readonly string[];
|
|
1021
|
-
readonly recentBlockhash?: string;
|
|
1022
|
-
readonly version?: string;
|
|
1023
|
-
/** SPL Token / Token-2022 balance snapshots for this transaction.
|
|
1024
|
-
* Present when `include_token_balances` is `true`. */
|
|
1025
|
-
readonly tokenBalances?: readonly SvmTokenBalance[];
|
|
1026
|
-
};
|
|
1027
|
-
|
|
1028
|
-
export type SvmLog = {
|
|
1029
|
-
readonly kind: string;
|
|
1030
|
-
readonly message: string;
|
|
1031
|
-
};
|
|
1032
|
-
|
|
1033
|
-
/** A single Solana instruction event delivered to a handler. Parameterised
|
|
1034
|
-
* over `Decoded` so the per-(program, instruction) overload of
|
|
1035
|
-
* `onInstruction` can narrow `event.instruction.decoded` to the
|
|
1036
|
-
* codegen-generated `{ args, accounts }` shape. */
|
|
1037
|
-
export type SvmInstructionEvent<
|
|
1038
|
-
Decoded extends SvmDecodedInstruction = SvmDecodedInstruction,
|
|
1039
|
-
> = {
|
|
1040
|
-
readonly contractName: string;
|
|
1041
|
-
readonly eventName: string;
|
|
1042
|
-
readonly instruction: Omit<SvmInstruction, "decoded"> & {
|
|
1043
|
-
readonly decoded?: Decoded;
|
|
1044
|
-
};
|
|
1045
|
-
/** Present when the instruction's `include_transaction` is `true`. */
|
|
1046
|
-
readonly transaction?: SvmTransaction;
|
|
1047
|
-
/** Present when the instruction's `include_logs` is `true`; only logs
|
|
1048
|
-
* scoped to this exact instruction (matching `instruction_address`). */
|
|
1049
|
-
readonly logs?: readonly SvmLog[];
|
|
1050
|
-
readonly slot: number;
|
|
1051
|
-
readonly blockTime?: number;
|
|
1052
|
-
};
|
|
1053
|
-
|
|
1054
|
-
/** Arguments passed to handlers registered via `indexer.onInstruction`. */
|
|
1055
|
-
export type SvmOnInstructionHandlerArgs<
|
|
1056
|
-
Config extends IndexerConfigTypes = GlobalConfig,
|
|
1057
|
-
Event extends SvmInstructionEvent = SvmInstructionEvent,
|
|
1058
|
-
> = {
|
|
1059
|
-
readonly event: Event;
|
|
1060
|
-
readonly context: SvmOnSlotContext<Config>;
|
|
1061
|
-
};
|
|
1062
|
-
|
|
1063
|
-
/** Shape extracted from `Global.config.svm.programs[P][I]`. The codegen
|
|
1064
|
-
* emits `{ args: ...; accounts: ... }` per (program, instruction); this
|
|
1065
|
-
* helper turns that into a `SvmDecodedInstruction`-compatible record. */
|
|
1066
|
-
type SvmDecodedFromProgramTable<TInstr> = TInstr extends {
|
|
1067
|
-
args: infer A;
|
|
1068
|
-
accounts: infer Acc extends Readonly<Record<string, string>>;
|
|
1069
|
-
}
|
|
1070
|
-
? {
|
|
1071
|
-
readonly name: string;
|
|
1072
|
-
readonly args: A;
|
|
1073
|
-
readonly accounts: Acc;
|
|
1074
|
-
readonly extraAccounts: readonly string[];
|
|
1075
|
-
}
|
|
1076
|
-
: SvmDecodedInstruction;
|
|
1077
|
-
|
|
1078
|
-
/** Options for an SVM `indexer.onInstruction` registration. */
|
|
1079
|
-
export type SvmOnInstructionOptions<P extends string = string, I extends string = string> = {
|
|
1080
|
-
/** Program name as declared under `chains[].programs[].name` in
|
|
1081
|
-
* `config.yaml`. */
|
|
1082
|
-
readonly program: P;
|
|
1083
|
-
/** Instruction name as declared under
|
|
1084
|
-
* `chains[].programs[].instructions[].name` in `config.yaml`. */
|
|
1085
|
-
readonly instruction: I;
|
|
1086
|
-
};
|
|
1087
|
-
|
|
1088
|
-
/** Handler function for an SVM `indexer.onInstruction` registration. */
|
|
1089
|
-
export type SvmOnInstructionHandler<
|
|
1090
|
-
Config extends IndexerConfigTypes = GlobalConfig,
|
|
1091
|
-
> = (args: SvmOnInstructionHandlerArgs<Config>) => Promise<void>;
|
|
1092
|
-
|
|
1093
962
|
// ============== Indexer Types ==============
|
|
1094
963
|
|
|
1095
964
|
// Helper: Check if an ecosystem is configured. Single-ecosystem indexers only
|
|
@@ -1267,7 +1136,7 @@ type FuelEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1267
1136
|
: never
|
|
1268
1137
|
: never;
|
|
1269
1138
|
|
|
1270
|
-
// SVM ecosystem type — chains plus
|
|
1139
|
+
// SVM ecosystem type — chains plus onSlot handler method. SVM has no onEvent yet.
|
|
1271
1140
|
type SvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
1272
1141
|
"svm" extends keyof Config
|
|
1273
1142
|
? Config["svm"] extends { chains: infer Chains }
|
|
@@ -1291,41 +1160,7 @@ type SvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
|
|
|
1291
1160
|
options: SvmOnSlotOptions<Config>,
|
|
1292
1161
|
handler: SvmOnSlotHandler<Config>,
|
|
1293
1162
|
) => void;
|
|
1294
|
-
} & (Config["svm"] extends {
|
|
1295
|
-
programs: infer Programs extends Record<string, Record<string, any>>;
|
|
1296
1163
|
}
|
|
1297
|
-
? {
|
|
1298
|
-
/**
|
|
1299
|
-
* Register an instruction handler. Dispatch matches on
|
|
1300
|
-
* `(programId, discriminator)` from the YAML config.
|
|
1301
|
-
* `event.instruction.decoded.args` and
|
|
1302
|
-
* `event.instruction.decoded.accounts` are typed from the
|
|
1303
|
-
* program's Borsh schema (Anchor IDL, bundled, or
|
|
1304
|
-
* hand-written `accounts`/`args` in YAML). `decoded` stays
|
|
1305
|
-
* optional at runtime because schema-matching can fail on
|
|
1306
|
-
* IDL drift or unknown discriminators.
|
|
1307
|
-
*/
|
|
1308
|
-
readonly onInstruction: <
|
|
1309
|
-
P extends keyof Programs & string,
|
|
1310
|
-
I extends keyof Programs[P] & string,
|
|
1311
|
-
>(
|
|
1312
|
-
options: SvmOnInstructionOptions<P, I>,
|
|
1313
|
-
handler: (
|
|
1314
|
-
args: SvmOnInstructionHandlerArgs<
|
|
1315
|
-
Config,
|
|
1316
|
-
SvmInstructionEvent<SvmDecodedFromProgramTable<Programs[P][I]>>
|
|
1317
|
-
>,
|
|
1318
|
-
) => Promise<void>,
|
|
1319
|
-
) => void;
|
|
1320
|
-
}
|
|
1321
|
-
: {
|
|
1322
|
-
/** Untyped fallback for indexers with no `programs` in
|
|
1323
|
-
* config. `decoded` stays the generic shape. */
|
|
1324
|
-
readonly onInstruction: (
|
|
1325
|
-
options: SvmOnInstructionOptions,
|
|
1326
|
-
handler: SvmOnInstructionHandler<Config>,
|
|
1327
|
-
) => void;
|
|
1328
|
-
})
|
|
1329
1164
|
: never
|
|
1330
1165
|
: never
|
|
1331
1166
|
: never;
|
|
@@ -1341,7 +1176,6 @@ type CodegenRequiredHint =
|
|
|
1341
1176
|
"Run 'envio codegen' to generate handler types from config.yaml. Without codegen, the indexer has no contracts, chains, or events to register handlers for.";
|
|
1342
1177
|
type CodegenRequiredFallback = {
|
|
1343
1178
|
readonly onEvent: (...hint: CodegenRequiredHint[]) => void;
|
|
1344
|
-
readonly onInstruction: (...hint: CodegenRequiredHint[]) => void;
|
|
1345
1179
|
readonly onBlock: (...hint: CodegenRequiredHint[]) => void;
|
|
1346
1180
|
readonly onSlot: (...hint: CodegenRequiredHint[]) => void;
|
|
1347
1181
|
readonly contractRegister: (...hint: CodegenRequiredHint[]) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0-rc.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
6
6
|
"bin": "./bin.mjs",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"tsx": "4.21.0"
|
|
71
71
|
},
|
|
72
72
|
"optionalDependencies": {
|
|
73
|
-
"envio-linux-x64": "3.0
|
|
74
|
-
"envio-linux-x64-musl": "3.0
|
|
75
|
-
"envio-linux-arm64": "3.0
|
|
76
|
-
"envio-darwin-x64": "3.0
|
|
77
|
-
"envio-darwin-arm64": "3.0
|
|
73
|
+
"envio-linux-x64": "3.1.0-rc.0",
|
|
74
|
+
"envio-linux-x64-musl": "3.1.0-rc.0",
|
|
75
|
+
"envio-linux-arm64": "3.1.0-rc.0",
|
|
76
|
+
"envio-darwin-x64": "3.1.0-rc.0",
|
|
77
|
+
"envio-darwin-arm64": "3.1.0-rc.0"
|
|
78
78
|
}
|
|
79
79
|
}
|
package/rescript.json
CHANGED
package/src/ChainFetcher.res
CHANGED
|
@@ -229,31 +229,7 @@ let make = (
|
|
|
229
229
|
~lowercaseAddresses,
|
|
230
230
|
)
|
|
231
231
|
| Config.FuelSourceConfig({hypersync}) => [HyperFuelSource.make({chain, endpointUrl: hypersync})]
|
|
232
|
-
| Config.SvmSourceConfig({
|
|
233
|
-
switch hypersync {
|
|
234
|
-
| None => [Svm.makeRPCSource(~chain, ~rpc)]
|
|
235
|
-
| Some(hypersyncUrl) =>
|
|
236
|
-
// HyperSync drives instruction sync; RPC remains the height oracle
|
|
237
|
-
// (Svm.makeRPCSource's `getFinalizedSlot` route) and the fallback.
|
|
238
|
-
let svmEventConfigs =
|
|
239
|
-
chainConfig.contracts
|
|
240
|
-
->Array.flatMap(contract => contract.events)
|
|
241
|
-
->(
|
|
242
|
-
Utils.magic: array<Internal.eventConfig> => array<Internal.svmInstructionEventConfig>
|
|
243
|
-
)
|
|
244
|
-
let apiToken = Env.envioApiToken
|
|
245
|
-
[
|
|
246
|
-
HyperSyncSolanaSource.make({
|
|
247
|
-
chain,
|
|
248
|
-
endpointUrl: hypersyncUrl,
|
|
249
|
-
apiToken,
|
|
250
|
-
eventConfigs: svmEventConfigs,
|
|
251
|
-
clientMaxRetries: Env.hyperSyncClientMaxRetries,
|
|
252
|
-
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
|
|
253
|
-
}),
|
|
254
|
-
Svm.makeRPCSource(~chain, ~rpc, ~sourceFor=Fallback),
|
|
255
|
-
]
|
|
256
|
-
}
|
|
232
|
+
| Config.SvmSourceConfig({rpc}) => [Svm.makeRPCSource(~chain, ~rpc)]
|
|
257
233
|
// For tests: use ready-to-use sources directly
|
|
258
234
|
| Config.CustomSources(sources) => sources
|
|
259
235
|
}
|
package/src/ChainFetcher.res.mjs
CHANGED
|
@@ -21,7 +21,6 @@ import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
|
21
21
|
import * as HyperFuelSource from "./sources/HyperFuelSource.res.mjs";
|
|
22
22
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
23
23
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
24
|
-
import * as HyperSyncSolanaSource from "./sources/HyperSyncSolanaSource.res.mjs";
|
|
25
24
|
import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
|
|
26
25
|
|
|
27
26
|
function configAddresses(chainConfig) {
|
|
@@ -133,24 +132,7 @@ function make(chainConfig, indexingAddresses, startBlock, endBlock, firstEventBl
|
|
|
133
132
|
})];
|
|
134
133
|
break;
|
|
135
134
|
case "SvmSourceConfig" :
|
|
136
|
-
|
|
137
|
-
let hypersync = sources.hypersync;
|
|
138
|
-
if (hypersync !== undefined) {
|
|
139
|
-
let svmEventConfigs = chainConfig.contracts.flatMap(contract => contract.events);
|
|
140
|
-
sources$1 = [
|
|
141
|
-
HyperSyncSolanaSource.make({
|
|
142
|
-
chain: chain,
|
|
143
|
-
endpointUrl: hypersync,
|
|
144
|
-
apiToken: Env.envioApiToken,
|
|
145
|
-
eventConfigs: svmEventConfigs,
|
|
146
|
-
clientMaxRetries: Env.hyperSyncClientMaxRetries,
|
|
147
|
-
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis
|
|
148
|
-
}),
|
|
149
|
-
Svm.makeRPCSource(chain, rpc, "Fallback")
|
|
150
|
-
];
|
|
151
|
-
} else {
|
|
152
|
-
sources$1 = [Svm.makeRPCSource(chain, rpc, undefined)];
|
|
153
|
-
}
|
|
135
|
+
sources$1 = [Svm.makeRPCSource(chain, sources.rpc)];
|
|
154
136
|
break;
|
|
155
137
|
case "CustomSources" :
|
|
156
138
|
sources$1 = sources._0;
|
package/src/ChainManager.res
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
type t = {
|
|
2
|
-
committedCheckpointId: bigint,
|
|
3
2
|
chainFetchers: ChainMap.t<ChainFetcher.t>,
|
|
4
3
|
isInReorgThreshold: bool,
|
|
5
4
|
// True once every chain has caught up to head/endBlock. Monotonic during a run.
|
|
@@ -106,7 +105,6 @@ let makeFromDbState = (
|
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
{
|
|
109
|
-
committedCheckpointId: initialState.checkpointId,
|
|
110
108
|
chainFetchers,
|
|
111
109
|
isInReorgThreshold,
|
|
112
110
|
isRealtime,
|
|
@@ -135,9 +133,14 @@ let nextItemIsNone = (chainManager: t): bool => {
|
|
|
135
133
|
)
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
let createBatch = (
|
|
136
|
+
let createBatch = (
|
|
137
|
+
chainManager: t,
|
|
138
|
+
~committedCheckpointId,
|
|
139
|
+
~batchSizeTarget: int,
|
|
140
|
+
~isRollback: bool,
|
|
141
|
+
): Batch.t => {
|
|
139
142
|
Batch.make(
|
|
140
|
-
~checkpointIdBeforeBatch=
|
|
143
|
+
~checkpointIdBeforeBatch=committedCheckpointId->BigInt.add(
|
|
141
144
|
// Since for rollback we have a diff checkpoint id.
|
|
142
145
|
// This is needed to currectly overwrite old state
|
|
143
146
|
// in an append-only ClickHouse insert.
|
package/src/ChainManager.res.mjs
CHANGED
|
@@ -77,7 +77,6 @@ function makeFromDbState(initialState, config, registrations, reducedPollingInte
|
|
|
77
77
|
Prometheus.ProgressReady.setAllReady();
|
|
78
78
|
}
|
|
79
79
|
return {
|
|
80
|
-
committedCheckpointId: initialState.checkpointId,
|
|
81
80
|
chainFetchers: chainFetchers,
|
|
82
81
|
isInReorgThreshold: isInReorgThreshold,
|
|
83
82
|
isRealtime: isRealtime
|
|
@@ -90,7 +89,6 @@ function getChainFetcher(chainManager, chain) {
|
|
|
90
89
|
|
|
91
90
|
function setChainFetcher(chainManager, chainFetcher) {
|
|
92
91
|
return {
|
|
93
|
-
committedCheckpointId: chainManager.committedCheckpointId,
|
|
94
92
|
chainFetchers: ChainMap.set(chainManager.chainFetchers, ChainMap.Chain.makeUnsafe(chainFetcher.chainConfig.id), chainFetcher),
|
|
95
93
|
isInReorgThreshold: chainManager.isInReorgThreshold,
|
|
96
94
|
isRealtime: chainManager.isRealtime
|
|
@@ -101,8 +99,8 @@ function nextItemIsNone(chainManager) {
|
|
|
101
99
|
return !Batch.hasReadyItem(ChainMap.map(chainManager.chainFetchers, cf => cf.fetchState));
|
|
102
100
|
}
|
|
103
101
|
|
|
104
|
-
function createBatch(chainManager, batchSizeTarget, isRollback) {
|
|
105
|
-
return Batch.make(
|
|
102
|
+
function createBatch(chainManager, committedCheckpointId, batchSizeTarget, isRollback) {
|
|
103
|
+
return Batch.make(committedCheckpointId + (
|
|
106
104
|
isRollback ? 1n : 0n
|
|
107
105
|
), ChainMap.map(chainManager.chainFetchers, cf => ({
|
|
108
106
|
fetchState: cf.fetchState,
|
package/src/Config.res
CHANGED
|
@@ -28,7 +28,7 @@ type evmRpcConfig = {
|
|
|
28
28
|
type sourceConfig =
|
|
29
29
|
| EvmSourceConfig({hypersync: option<string>, rpcs: array<evmRpcConfig>})
|
|
30
30
|
| FuelSourceConfig({hypersync: string})
|
|
31
|
-
| SvmSourceConfig({
|
|
31
|
+
| SvmSourceConfig({rpc: string})
|
|
32
32
|
// For tests: pass custom sources directly
|
|
33
33
|
| CustomSources(array<Source.t>)
|
|
34
34
|
|
|
@@ -194,39 +194,6 @@ let publicConfigChainSchema = S.schema(s =>
|
|
|
194
194
|
}
|
|
195
195
|
)
|
|
196
196
|
|
|
197
|
-
let svmEventDescriptorSchema = S.schema(s =>
|
|
198
|
-
{
|
|
199
|
-
"discriminator": s.matches(S.option(S.string)),
|
|
200
|
-
"discriminatorByteLen": s.matches(S.int),
|
|
201
|
-
"includeTransaction": s.matches(S.bool),
|
|
202
|
-
"includeLogs": s.matches(S.bool),
|
|
203
|
-
"includeTokenBalances": s.matches(S.bool),
|
|
204
|
-
"accountFilters": s.matches(
|
|
205
|
-
S.option(
|
|
206
|
-
S.array(
|
|
207
|
-
S.schema(
|
|
208
|
-
s => {
|
|
209
|
-
"position": s.matches(S.int),
|
|
210
|
-
"values": s.matches(S.array(S.string)),
|
|
211
|
-
},
|
|
212
|
-
),
|
|
213
|
-
),
|
|
214
|
-
),
|
|
215
|
-
),
|
|
216
|
-
"isInner": s.matches(S.option(S.bool)),
|
|
217
|
-
"accounts": s.matches(S.option(S.array(S.string))),
|
|
218
|
-
"args": s.matches(S.option(S.json(~validate=false))),
|
|
219
|
-
}
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
let svmAbiSchema = S.schema(s =>
|
|
223
|
-
{
|
|
224
|
-
"programId": s.matches(S.string),
|
|
225
|
-
"definedTypes": s.matches(S.json(~validate=false)),
|
|
226
|
-
"source": s.matches(S.string),
|
|
227
|
-
}
|
|
228
|
-
)
|
|
229
|
-
|
|
230
197
|
let contractEventItemSchema = S.schema(s =>
|
|
231
198
|
{
|
|
232
199
|
"name": s.matches(S.string),
|
|
@@ -235,7 +202,6 @@ let contractEventItemSchema = S.schema(s =>
|
|
|
235
202
|
"kind": s.matches(S.option(S.string)),
|
|
236
203
|
"blockFields": s.matches(S.option(S.array(Internal.evmBlockFieldSchema))),
|
|
237
204
|
"transactionFields": s.matches(S.option(S.array(Internal.evmTransactionFieldSchema))),
|
|
238
|
-
"svm": s.matches(S.option(svmEventDescriptorSchema)),
|
|
239
205
|
}
|
|
240
206
|
)
|
|
241
207
|
|
|
@@ -245,8 +211,6 @@ let contractConfigSchema = S.schema(s =>
|
|
|
245
211
|
"handler": s.matches(S.option(S.string)),
|
|
246
212
|
// EVM-specific: event signatures for HyperSync queries
|
|
247
213
|
"events": s.matches(S.option(S.array(contractEventItemSchema))),
|
|
248
|
-
// SVM-only: program-level Borsh schema (defined-types registry, source).
|
|
249
|
-
"svmAbi": s.matches(S.option(svmAbiSchema)),
|
|
250
214
|
}
|
|
251
215
|
)
|
|
252
216
|
|
|
@@ -254,10 +218,6 @@ let publicConfigEcosystemSchema = S.schema(s =>
|
|
|
254
218
|
{
|
|
255
219
|
"chains": s.matches(S.dict(publicConfigChainSchema)),
|
|
256
220
|
"contracts": s.matches(S.option(S.dict(contractConfigSchema))),
|
|
257
|
-
// SVM-only alias: programs are the SVM analog of EVM/Fuel contracts.
|
|
258
|
-
// Parsed via the same `contractConfigSchema` and read in `fromPublic`'s
|
|
259
|
-
// `publicContractsConfig` switch.
|
|
260
|
-
"programs": s.matches(S.option(S.dict(contractConfigSchema))),
|
|
261
221
|
}
|
|
262
222
|
)
|
|
263
223
|
|
|
@@ -558,19 +518,10 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
558
518
|
| None => false
|
|
559
519
|
}
|
|
560
520
|
|
|
561
|
-
// Parse contract configs (ABIs, events, handlers)
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
let publicContractsConfig = switch (
|
|
566
|
-
ecosystemName,
|
|
567
|
-
publicConfig["evm"],
|
|
568
|
-
publicConfig["fuel"],
|
|
569
|
-
publicConfig["svm"],
|
|
570
|
-
) {
|
|
571
|
-
| (Ecosystem.Evm, Some(evm), _, _) => evm["contracts"]
|
|
572
|
-
| (Ecosystem.Fuel, _, Some(fuel), _) => fuel["contracts"]
|
|
573
|
-
| (Ecosystem.Svm, _, _, Some(svm)) => svm["programs"]
|
|
521
|
+
// Parse contract configs (ABIs, events, handlers)
|
|
522
|
+
let publicContractsConfig = switch (ecosystemName, publicConfig["evm"], publicConfig["fuel"]) {
|
|
523
|
+
| (Ecosystem.Evm, Some(evm), _) => evm["contracts"]
|
|
524
|
+
| (Ecosystem.Fuel, _, Some(fuel)) => fuel["contracts"]
|
|
574
525
|
| _ => None
|
|
575
526
|
}
|
|
576
527
|
|
|
@@ -588,12 +539,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
588
539
|
| None => (Utils.Set.fromArray(EventConfigBuilder.alwaysIncludedBlockFields), Utils.Set.make())
|
|
589
540
|
}
|
|
590
541
|
|
|
591
|
-
let contractDataByName: dict<{
|
|
592
|
-
"abi": EvmTypes.Abi.t,
|
|
593
|
-
"eventSignatures": array<string>,
|
|
594
|
-
"events": option<array<_>>,
|
|
595
|
-
"svmAbi": option<{"programId": string, "definedTypes": JSON.t, "source": string}>,
|
|
596
|
-
}> = Dict.make()
|
|
542
|
+
let contractDataByName: dict<{"abi": EvmTypes.Abi.t, "events": option<array<_>>}> = Dict.make()
|
|
597
543
|
switch publicContractsConfig {
|
|
598
544
|
| Some(contractsDict) =>
|
|
599
545
|
contractsDict
|
|
@@ -601,44 +547,21 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
601
547
|
->Array.forEach(((contractName, contractConfig)) => {
|
|
602
548
|
let capitalizedName = contractName->Utils.String.capitalize
|
|
603
549
|
let abi = contractConfig["abi"]->(Utils.magic: JSON.t => EvmTypes.Abi.t)
|
|
604
|
-
let eventSignatures = switch contractConfig["events"] {
|
|
605
|
-
| Some(events) => events->Array.map(eventItem => eventItem["sighash"])
|
|
606
|
-
| None => []
|
|
607
|
-
}
|
|
608
|
-
let widened =
|
|
609
|
-
contractConfig->(
|
|
610
|
-
Utils.magic: _ => {
|
|
611
|
-
"svmAbi": option<{"programId": string, "definedTypes": JSON.t, "source": string}>,
|
|
612
|
-
}
|
|
613
|
-
)
|
|
614
550
|
contractDataByName->Dict.set(
|
|
615
551
|
capitalizedName,
|
|
616
|
-
{
|
|
617
|
-
"abi": abi,
|
|
618
|
-
"eventSignatures": eventSignatures,
|
|
619
|
-
"events": contractConfig["events"],
|
|
620
|
-
"svmAbi": widened["svmAbi"],
|
|
621
|
-
},
|
|
552
|
+
{"abi": abi, "events": contractConfig["events"]},
|
|
622
553
|
)
|
|
623
554
|
})
|
|
624
555
|
| None => ()
|
|
625
556
|
}
|
|
626
557
|
|
|
627
|
-
// Build event configs for a contract from JSON event items
|
|
628
|
-
//
|
|
629
|
-
// `~addresses` is the chain-side address list. For SVM programs it's the
|
|
630
|
-
// single base58 program_id — wired onto each instruction's event config so
|
|
631
|
-
// the source can build `(programId, discriminator)`-keyed InstructionSelections.
|
|
632
|
-
// EVM and Fuel ignore it (the address lives in `ChainContract.addresses` and
|
|
633
|
-
// is looked up at dispatch time, not stamped on the event).
|
|
558
|
+
// Build event configs for a contract from JSON event items
|
|
634
559
|
let buildContractEvents = (
|
|
635
560
|
~contractName,
|
|
636
561
|
~events: option<array<_>>,
|
|
637
562
|
~abi,
|
|
638
563
|
~chainId: int,
|
|
639
564
|
~startBlock: option<int>,
|
|
640
|
-
~addresses: array<string>,
|
|
641
|
-
~svmDefinedTypes: JSON.t=JSON.Null,
|
|
642
565
|
) => {
|
|
643
566
|
switch events {
|
|
644
567
|
| None => []
|
|
@@ -669,69 +592,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
669
592
|
`Fuel event ${contractName}.${eventName} is missing "kind" in internal config`,
|
|
670
593
|
)
|
|
671
594
|
}
|
|
672
|
-
| Ecosystem.Svm =>
|
|
673
|
-
let programId = switch addresses {
|
|
674
|
-
| [pid] => pid->SvmTypes.Pubkey.fromStringUnsafe
|
|
675
|
-
| [] =>
|
|
676
|
-
JsError.throwWithMessage(
|
|
677
|
-
`SVM program ${contractName} on chain ${chainId->Int.toString} is missing a program_id`,
|
|
678
|
-
)
|
|
679
|
-
| _ =>
|
|
680
|
-
JsError.throwWithMessage(
|
|
681
|
-
`SVM program ${contractName} on chain ${chainId->Int.toString} has multiple addresses; a program is uniquely identified by a single program_id`,
|
|
682
|
-
)
|
|
683
|
-
}
|
|
684
|
-
let widenedEventItem =
|
|
685
|
-
eventItem->(
|
|
686
|
-
Utils.magic: _ => {
|
|
687
|
-
"svm": option<{
|
|
688
|
-
"discriminator": option<string>,
|
|
689
|
-
"discriminatorByteLen": int,
|
|
690
|
-
"includeTransaction": bool,
|
|
691
|
-
"includeLogs": bool,
|
|
692
|
-
"includeTokenBalances": bool,
|
|
693
|
-
"accountFilters": option<
|
|
694
|
-
array<array<{"position": int, "values": array<string>}>>,
|
|
695
|
-
>,
|
|
696
|
-
"isInner": option<bool>,
|
|
697
|
-
"accounts": option<array<string>>,
|
|
698
|
-
"args": option<JSON.t>,
|
|
699
|
-
}>,
|
|
700
|
-
}
|
|
701
|
-
)
|
|
702
|
-
let svm = switch widenedEventItem["svm"] {
|
|
703
|
-
| Some(s) => s
|
|
704
|
-
| None =>
|
|
705
|
-
JsError.throwWithMessage(
|
|
706
|
-
`SVM instruction ${contractName}.${eventName} is missing the "svm" descriptor in internal config`,
|
|
707
|
-
)
|
|
708
|
-
}
|
|
709
|
-
let accountFilters =
|
|
710
|
-
(svm["accountFilters"]->Option.getOr([]))->Array.map(group =>
|
|
711
|
-
group->Array.map(af => {
|
|
712
|
-
Internal.position: af["position"],
|
|
713
|
-
values: af["values"]->SvmTypes.Pubkey.fromStringsUnsafe,
|
|
714
|
-
})
|
|
715
|
-
)
|
|
716
|
-
(EventConfigBuilder.buildSvmInstructionEventConfig(
|
|
717
|
-
~contractName,
|
|
718
|
-
~instructionName=eventName,
|
|
719
|
-
~programId,
|
|
720
|
-
~discriminator=svm["discriminator"],
|
|
721
|
-
~discriminatorByteLen=svm["discriminatorByteLen"],
|
|
722
|
-
~includeTransaction=svm["includeTransaction"],
|
|
723
|
-
~includeLogs=svm["includeLogs"],
|
|
724
|
-
~includeTokenBalances=svm["includeTokenBalances"],
|
|
725
|
-
~accountFilters,
|
|
726
|
-
~isInner=svm["isInner"],
|
|
727
|
-
~isWildcard=false,
|
|
728
|
-
~handler=None,
|
|
729
|
-
~contractRegister=None,
|
|
730
|
-
~accounts=svm["accounts"]->Option.getOr([]),
|
|
731
|
-
~args=svm["args"]->Option.getOr(JSON.Null),
|
|
732
|
-
~definedTypes=svmDefinedTypes,
|
|
733
|
-
~startBlock?,
|
|
734
|
-
) :> Internal.eventConfig)
|
|
735
595
|
| _ =>
|
|
736
596
|
(EventConfigBuilder.buildEvmEventConfig(
|
|
737
597
|
~contractName,
|
|
@@ -791,11 +651,11 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
791
651
|
->Dict.toArray
|
|
792
652
|
->Array.map(((capitalizedName, contractData)) => {
|
|
793
653
|
let chainContract = chainContracts->Dict.get(capitalizedName)
|
|
794
|
-
let
|
|
654
|
+
let addresses =
|
|
795
655
|
chainContract
|
|
796
656
|
->Option.flatMap(cc => cc["addresses"])
|
|
797
657
|
->Option.getOr([])
|
|
798
|
-
|
|
658
|
+
->Array.map(parseAddress)
|
|
799
659
|
let startBlock = chainContract->Option.flatMap(cc => cc["startBlock"])
|
|
800
660
|
|
|
801
661
|
// Build event configs from JSON (field selections resolved inline)
|
|
@@ -809,10 +669,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
809
669
|
~abi=contractData["abi"],
|
|
810
670
|
~chainId,
|
|
811
671
|
~startBlock,
|
|
812
|
-
~addresses=rawAddresses,
|
|
813
|
-
~svmDefinedTypes=contractData["svmAbi"]
|
|
814
|
-
->Option.map(a => a["definedTypes"])
|
|
815
|
-
->Option.getOr(JSON.Null),
|
|
816
672
|
)
|
|
817
673
|
|
|
818
674
|
{
|
|
@@ -877,11 +733,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
877
733
|
}
|
|
878
734
|
| Ecosystem.Svm =>
|
|
879
735
|
switch publicChainConfig["rpc"] {
|
|
880
|
-
| Some(rpc) =>
|
|
881
|
-
SvmSourceConfig({
|
|
882
|
-
hypersync: publicChainConfig["hypersync"],
|
|
883
|
-
rpc,
|
|
884
|
-
})
|
|
736
|
+
| Some(rpc) => SvmSourceConfig({rpc: rpc})
|
|
885
737
|
| None => JsError.throwWithMessage(`Chain ${chainName} is missing rpc endpoint in config`)
|
|
886
738
|
}
|
|
887
739
|
}
|