envio 3.0.1 → 3.0.2-svm-alpha.1

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.
Files changed (43) hide show
  1. package/evm.schema.json +8 -8
  2. package/fuel.schema.json +12 -12
  3. package/index.d.ts +167 -1
  4. package/package.json +6 -7
  5. package/src/ChainFetcher.res +25 -1
  6. package/src/ChainFetcher.res.mjs +19 -1
  7. package/src/Config.res +158 -94
  8. package/src/Config.res.mjs +60 -97
  9. package/src/Core.res +32 -0
  10. package/src/Env.res.mjs +1 -2
  11. package/src/Envio.res +103 -0
  12. package/src/EventConfigBuilder.res +52 -0
  13. package/src/EventConfigBuilder.res.mjs +32 -0
  14. package/src/HandlerLoader.res +12 -1
  15. package/src/HandlerLoader.res.mjs +6 -1
  16. package/src/Hasura.res +43 -0
  17. package/src/Hasura.res.mjs +38 -0
  18. package/src/Internal.res +39 -0
  19. package/src/Main.res +53 -3
  20. package/src/Main.res.mjs +34 -2
  21. package/src/Persistence.res +2 -17
  22. package/src/Persistence.res.mjs +2 -14
  23. package/src/SimulateItems.res +23 -10
  24. package/src/SimulateItems.res.mjs +21 -6
  25. package/src/SvmTypes.res +9 -0
  26. package/src/SvmTypes.res.mjs +14 -0
  27. package/src/sources/EventRouter.res +65 -0
  28. package/src/sources/EventRouter.res.mjs +43 -0
  29. package/src/sources/HyperSyncClient.res +30 -157
  30. package/src/sources/HyperSyncClient.res.mjs +20 -6
  31. package/src/sources/HyperSyncSolanaClient.res +249 -0
  32. package/src/sources/HyperSyncSolanaClient.res.mjs +25 -0
  33. package/src/sources/HyperSyncSolanaSource.res +566 -0
  34. package/src/sources/HyperSyncSolanaSource.res.mjs +488 -0
  35. package/src/sources/HyperSyncSource.res +5 -8
  36. package/src/sources/HyperSyncSource.res.mjs +1 -8
  37. package/src/sources/RpcSource.res.mjs +1 -1
  38. package/src/sources/Svm.res +2 -2
  39. package/src/sources/Svm.res.mjs +3 -2
  40. package/src/tui/Tui.res +9 -2
  41. package/src/tui/Tui.res.mjs +19 -4
  42. package/src/tui/components/TuiData.res +3 -0
  43. package/svm.schema.json +352 -4
package/evm.schema.json CHANGED
@@ -4,6 +4,10 @@
4
4
  "description": "Schema for a YAML config for an envio indexer",
5
5
  "type": "object",
6
6
  "properties": {
7
+ "name": {
8
+ "description": "Name of the project",
9
+ "type": "string"
10
+ },
7
11
  "description": {
8
12
  "description": "Description of the project",
9
13
  "type": [
@@ -11,10 +15,6 @@
11
15
  "null"
12
16
  ]
13
17
  },
14
- "name": {
15
- "description": "Name of the project",
16
- "type": "string"
17
- },
18
18
  "schema": {
19
19
  "description": "Custom path to schema.graphql file",
20
20
  "type": [
@@ -67,7 +67,7 @@
67
67
  "null"
68
68
  ],
69
69
  "items": {
70
- "$ref": "#/$defs/GlobalContract_for_ContractConfig"
70
+ "$ref": "#/$defs/GlobalContract"
71
71
  }
72
72
  },
73
73
  "chains": {
@@ -189,7 +189,7 @@
189
189
  "evm"
190
190
  ]
191
191
  },
192
- "GlobalContract_for_ContractConfig": {
192
+ "GlobalContract": {
193
193
  "type": "object",
194
194
  "properties": {
195
195
  "name": {
@@ -418,7 +418,7 @@
418
418
  "null"
419
419
  ],
420
420
  "items": {
421
- "$ref": "#/$defs/ChainContract_for_ContractConfig"
421
+ "$ref": "#/$defs/ChainContract"
422
422
  }
423
423
  }
424
424
  },
@@ -578,7 +578,7 @@
578
578
  "url"
579
579
  ]
580
580
  },
581
- "ChainContract_for_ContractConfig": {
581
+ "ChainContract": {
582
582
  "type": "object",
583
583
  "properties": {
584
584
  "name": {
package/fuel.schema.json CHANGED
@@ -4,6 +4,10 @@
4
4
  "description": "Schema for a YAML config for an envio indexer",
5
5
  "type": "object",
6
6
  "properties": {
7
+ "name": {
8
+ "description": "Name of the project",
9
+ "type": "string"
10
+ },
7
11
  "description": {
8
12
  "description": "Description of the project",
9
13
  "type": [
@@ -11,10 +15,6 @@
11
15
  "null"
12
16
  ]
13
17
  },
14
- "name": {
15
- "description": "Name of the project",
16
- "type": "string"
17
- },
18
18
  "schema": {
19
19
  "description": "Custom path to schema.graphql file",
20
20
  "type": [
@@ -60,7 +60,7 @@
60
60
  "null"
61
61
  ],
62
62
  "items": {
63
- "$ref": "#/$defs/GlobalContract_for_ContractConfig"
63
+ "$ref": "#/$defs/GlobalContract"
64
64
  }
65
65
  },
66
66
  "chains": {
@@ -147,7 +147,7 @@
147
147
  "fuel"
148
148
  ]
149
149
  },
150
- "GlobalContract_for_ContractConfig": {
150
+ "GlobalContract": {
151
151
  "type": "object",
152
152
  "properties": {
153
153
  "name": {
@@ -183,6 +183,10 @@
183
183
  "EventConfig": {
184
184
  "type": "object",
185
185
  "properties": {
186
+ "name": {
187
+ "description": "Name of the event in the HyperIndex generated code",
188
+ "type": "string"
189
+ },
186
190
  "type": {
187
191
  "description": "Explicitly set the event type you want to index. It's derived from the event name and fallbacks to LogData.",
188
192
  "anyOf": [
@@ -194,10 +198,6 @@
194
198
  }
195
199
  ]
196
200
  },
197
- "name": {
198
- "description": "Name of the event in the HyperIndex generated code",
199
- "type": "string"
200
- },
201
201
  "logId": {
202
202
  "description": "An identifier of a logged type from ABI. Used for indexing LogData receipts. The option can be omitted when the event name matches the logged struct/enum name.",
203
203
  "type": [
@@ -281,7 +281,7 @@
281
281
  "null"
282
282
  ],
283
283
  "items": {
284
- "$ref": "#/$defs/ChainContract_for_ContractConfig"
284
+ "$ref": "#/$defs/ChainContract"
285
285
  }
286
286
  }
287
287
  },
@@ -304,7 +304,7 @@
304
304
  "url"
305
305
  ]
306
306
  },
307
- "ChainContract_for_ContractConfig": {
307
+ "ChainContract": {
308
308
  "type": "object",
309
309
  "properties": {
310
310
  "name": {
package/index.d.ts CHANGED
@@ -961,6 +961,137 @@ export type SvmOnSlotOptions<Config extends IndexerConfigTypes = GlobalConfig> =
961
961
  readonly where?: (args: SvmOnSlotWhereArgs<Config>) => SvmOnSlotWhereResult;
962
962
  };
963
963
 
964
+ // ============== SVM onInstruction types ==============
965
+
966
+ /** Borsh-decoded view of an instruction. Present whenever a `ProgramSchema`
967
+ * was attached to the program (bundled, Anchor IDL, or hand-written
968
+ * `accounts`/`args` in YAML). Absent when no schema applies or the
969
+ * discriminator didn't match any registered instruction. */
970
+ export type SvmDecodedInstruction = {
971
+ /** Schema-declared instruction name. */
972
+ readonly name: string;
973
+ /** Borsh-decoded args object. POC types this as `unknown`; narrow with a
974
+ * locally-declared type until the typed-args codegen lands. */
975
+ readonly args: unknown;
976
+ /** Named accounts in schema order. Keys are exactly the schema-declared
977
+ * names; values are base58 pubkeys. */
978
+ readonly accounts: Readonly<Record<string, string>>;
979
+ /** Accounts beyond the schema's named list (Anchor `remaining_accounts`,
980
+ * IDL drift). Empty when counts match the schema. */
981
+ readonly extraAccounts: readonly string[];
982
+ };
983
+
984
+ /** A single Solana instruction matched by the indexer.
985
+ *
986
+ * `data` and discriminator prefixes are `0x`-prefixed hex strings; accounts
987
+ * are base58 strings. When a Borsh schema is configured (bundled, Anchor
988
+ * IDL, or hand-written YAML), `decoded` carries the named-accounts +
989
+ * decoded-args view. */
990
+ export type SvmInstruction = {
991
+ readonly programId: string;
992
+ readonly data: string;
993
+ readonly accounts: readonly string[];
994
+ readonly instructionAddress: readonly number[];
995
+ readonly isInner: boolean;
996
+ readonly d1?: string;
997
+ readonly d2?: string;
998
+ readonly d4?: string;
999
+ readonly d8?: string;
1000
+ readonly decoded?: SvmDecodedInstruction;
1001
+ };
1002
+
1003
+ export type SvmTokenBalance = {
1004
+ readonly account?: string;
1005
+ readonly mint?: string;
1006
+ readonly owner?: string;
1007
+ /** u64 decimal string. Cast with BigInt(...) for arithmetic. */
1008
+ readonly preAmount?: string;
1009
+ readonly postAmount?: string;
1010
+ };
1011
+
1012
+ /** Parent transaction surfaced when an instruction's
1013
+ * `include_transaction` flag is `true`. */
1014
+ export type SvmTransaction = {
1015
+ readonly signatures: readonly string[];
1016
+ readonly feePayer?: string;
1017
+ readonly success?: boolean;
1018
+ readonly err?: string;
1019
+ /** Lamports. */
1020
+ readonly fee?: bigint;
1021
+ readonly computeUnitsConsumed?: bigint;
1022
+ readonly accountKeys: readonly string[];
1023
+ readonly recentBlockhash?: string;
1024
+ readonly version?: string;
1025
+ /** SPL Token / Token-2022 balance snapshots for this transaction.
1026
+ * Present when `include_token_balances` is `true`. */
1027
+ readonly tokenBalances?: readonly SvmTokenBalance[];
1028
+ };
1029
+
1030
+ export type SvmLog = {
1031
+ readonly kind: string;
1032
+ readonly message: string;
1033
+ };
1034
+
1035
+ /** A single Solana instruction event delivered to a handler. Parameterised
1036
+ * over `Decoded` so the per-(program, instruction) overload of
1037
+ * `onInstruction` can narrow `event.instruction.decoded` to the
1038
+ * codegen-generated `{ args, accounts }` shape. */
1039
+ export type SvmInstructionEvent<
1040
+ Decoded extends SvmDecodedInstruction = SvmDecodedInstruction,
1041
+ > = {
1042
+ readonly contractName: string;
1043
+ readonly eventName: string;
1044
+ readonly instruction: Omit<SvmInstruction, "decoded"> & {
1045
+ readonly decoded?: Decoded;
1046
+ };
1047
+ /** Present when the instruction's `include_transaction` is `true`. */
1048
+ readonly transaction?: SvmTransaction;
1049
+ /** Present when the instruction's `include_logs` is `true`; only logs
1050
+ * scoped to this exact instruction (matching `instruction_address`). */
1051
+ readonly logs?: readonly SvmLog[];
1052
+ readonly slot: number;
1053
+ readonly blockTime?: number;
1054
+ };
1055
+
1056
+ /** Arguments passed to handlers registered via `indexer.onInstruction`. */
1057
+ export type SvmOnInstructionHandlerArgs<
1058
+ Config extends IndexerConfigTypes = GlobalConfig,
1059
+ Event extends SvmInstructionEvent = SvmInstructionEvent,
1060
+ > = {
1061
+ readonly event: Event;
1062
+ readonly context: SvmOnSlotContext<Config>;
1063
+ };
1064
+
1065
+ /** Shape extracted from `Global.config.svm.programs[P][I]`. The codegen
1066
+ * emits `{ args: ...; accounts: ... }` per (program, instruction); this
1067
+ * helper turns that into a `SvmDecodedInstruction`-compatible record. */
1068
+ type SvmDecodedFromProgramTable<TInstr> = TInstr extends {
1069
+ args: infer A;
1070
+ accounts: infer Acc extends Readonly<Record<string, string>>;
1071
+ }
1072
+ ? {
1073
+ readonly name: string;
1074
+ readonly args: A;
1075
+ readonly accounts: Acc;
1076
+ readonly extraAccounts: readonly string[];
1077
+ }
1078
+ : SvmDecodedInstruction;
1079
+
1080
+ /** Options for an SVM `indexer.onInstruction` registration. */
1081
+ export type SvmOnInstructionOptions<P extends string = string, I extends string = string> = {
1082
+ /** Program name as declared under `chains[].programs[].name` in
1083
+ * `config.yaml`. */
1084
+ readonly program: P;
1085
+ /** Instruction name as declared under
1086
+ * `chains[].programs[].instructions[].name` in `config.yaml`. */
1087
+ readonly instruction: I;
1088
+ };
1089
+
1090
+ /** Handler function for an SVM `indexer.onInstruction` registration. */
1091
+ export type SvmOnInstructionHandler<
1092
+ Config extends IndexerConfigTypes = GlobalConfig,
1093
+ > = (args: SvmOnInstructionHandlerArgs<Config>) => Promise<void>;
1094
+
964
1095
  // ============== Indexer Types ==============
965
1096
 
966
1097
  // Helper: Check if an ecosystem is configured. Single-ecosystem indexers only
@@ -1138,7 +1269,7 @@ type FuelEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
1138
1269
  : never
1139
1270
  : never;
1140
1271
 
1141
- // SVM ecosystem type — chains plus onSlot handler method. SVM has no onEvent yet.
1272
+ // SVM ecosystem type — chains plus instruction + slot handler methods.
1142
1273
  type SvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
1143
1274
  "svm" extends keyof Config
1144
1275
  ? Config["svm"] extends { chains: infer Chains }
@@ -1162,7 +1293,41 @@ type SvmEcosystem<Config extends IndexerConfigTypes = GlobalConfig> =
1162
1293
  options: SvmOnSlotOptions<Config>,
1163
1294
  handler: SvmOnSlotHandler<Config>,
1164
1295
  ) => void;
1296
+ } & (Config["svm"] extends {
1297
+ programs: infer Programs extends Record<string, Record<string, any>>;
1165
1298
  }
1299
+ ? {
1300
+ /**
1301
+ * Register an instruction handler. Dispatch matches on
1302
+ * `(programId, discriminator)` from the YAML config.
1303
+ * `event.instruction.decoded.args` and
1304
+ * `event.instruction.decoded.accounts` are typed from the
1305
+ * program's Borsh schema (Anchor IDL, bundled, or
1306
+ * hand-written `accounts`/`args` in YAML). `decoded` stays
1307
+ * optional at runtime because schema-matching can fail on
1308
+ * IDL drift or unknown discriminators.
1309
+ */
1310
+ readonly onInstruction: <
1311
+ P extends keyof Programs & string,
1312
+ I extends keyof Programs[P] & string,
1313
+ >(
1314
+ options: SvmOnInstructionOptions<P, I>,
1315
+ handler: (
1316
+ args: SvmOnInstructionHandlerArgs<
1317
+ Config,
1318
+ SvmInstructionEvent<SvmDecodedFromProgramTable<Programs[P][I]>>
1319
+ >,
1320
+ ) => Promise<void>,
1321
+ ) => void;
1322
+ }
1323
+ : {
1324
+ /** Untyped fallback for indexers with no `programs` in
1325
+ * config. `decoded` stays the generic shape. */
1326
+ readonly onInstruction: (
1327
+ options: SvmOnInstructionOptions,
1328
+ handler: SvmOnInstructionHandler<Config>,
1329
+ ) => void;
1330
+ })
1166
1331
  : never
1167
1332
  : never
1168
1333
  : never;
@@ -1178,6 +1343,7 @@ type CodegenRequiredHint =
1178
1343
  "Run 'envio codegen' to generate handler types from config.yaml. Without codegen, the indexer has no contracts, chains, or events to register handlers for.";
1179
1344
  type CodegenRequiredFallback = {
1180
1345
  readonly onEvent: (...hint: CodegenRequiredHint[]) => void;
1346
+ readonly onInstruction: (...hint: CodegenRequiredHint[]) => void;
1181
1347
  readonly onBlock: (...hint: CodegenRequiredHint[]) => void;
1182
1348
  readonly onSlot: (...hint: CodegenRequiredHint[]) => void;
1183
1349
  readonly contractRegister: (...hint: CodegenRequiredHint[]) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envio",
3
- "version": "3.0.1",
3
+ "version": "3.0.2-svm-alpha.1",
4
4
  "type": "module",
5
5
  "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
6
6
  "bin": "./bin.mjs",
@@ -43,7 +43,6 @@
43
43
  "@clickhouse/client": "1.17.0",
44
44
  "@elastic/ecs-pino-format": "1.4.0",
45
45
  "@envio-dev/hyperfuel-client": "1.2.2",
46
- "@envio-dev/hypersync-client": "1.3.0",
47
46
  "@fuel-ts/crypto": "0.96.1",
48
47
  "@fuel-ts/errors": "0.96.1",
49
48
  "@fuel-ts/hasher": "0.96.1",
@@ -71,10 +70,10 @@
71
70
  "tsx": "4.21.0"
72
71
  },
73
72
  "optionalDependencies": {
74
- "envio-linux-x64": "3.0.1",
75
- "envio-linux-x64-musl": "3.0.1",
76
- "envio-linux-arm64": "3.0.1",
77
- "envio-darwin-x64": "3.0.1",
78
- "envio-darwin-arm64": "3.0.1"
73
+ "envio-linux-x64": "3.0.2-svm-alpha.1",
74
+ "envio-linux-x64-musl": "3.0.2-svm-alpha.1",
75
+ "envio-linux-arm64": "3.0.2-svm-alpha.1",
76
+ "envio-darwin-x64": "3.0.2-svm-alpha.1",
77
+ "envio-darwin-arm64": "3.0.2-svm-alpha.1"
79
78
  }
80
79
  }
@@ -233,7 +233,31 @@ let make = (
233
233
  ~lowercaseAddresses,
234
234
  )
235
235
  | Config.FuelSourceConfig({hypersync}) => [HyperFuelSource.make({chain, endpointUrl: hypersync})]
236
- | Config.SvmSourceConfig({rpc}) => [Svm.makeRPCSource(~chain, ~rpc)]
236
+ | Config.SvmSourceConfig({hypersync, rpc}) =>
237
+ switch hypersync {
238
+ | None => [Svm.makeRPCSource(~chain, ~rpc)]
239
+ | Some(hypersyncUrl) =>
240
+ // HyperSync drives instruction sync; RPC remains the height oracle
241
+ // (Svm.makeRPCSource's `getFinalizedSlot` route) and the fallback.
242
+ let svmEventConfigs =
243
+ chainConfig.contracts
244
+ ->Array.flatMap(contract => contract.events)
245
+ ->(
246
+ Utils.magic: array<Internal.eventConfig> => array<Internal.svmInstructionEventConfig>
247
+ )
248
+ let apiToken = Env.envioApiToken
249
+ [
250
+ HyperSyncSolanaSource.make({
251
+ chain,
252
+ endpointUrl: hypersyncUrl,
253
+ apiToken,
254
+ eventConfigs: svmEventConfigs,
255
+ clientMaxRetries: Env.hyperSyncClientMaxRetries,
256
+ clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
257
+ }),
258
+ Svm.makeRPCSource(~chain, ~rpc, ~sourceFor=Fallback),
259
+ ]
260
+ }
237
261
  // For tests: use ready-to-use sources directly
238
262
  | Config.CustomSources(sources) => sources
239
263
  }
@@ -21,6 +21,7 @@ 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";
24
25
  import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
25
26
 
26
27
  function configAddresses(chainConfig) {
@@ -133,7 +134,24 @@ function make(chainConfig, indexingAddresses, startBlock, endBlock, firstEventBl
133
134
  })];
134
135
  break;
135
136
  case "SvmSourceConfig" :
136
- sources$1 = [Svm.makeRPCSource(chain, sources.rpc)];
137
+ let rpc = sources.rpc;
138
+ let hypersync = sources.hypersync;
139
+ if (hypersync !== undefined) {
140
+ let svmEventConfigs = chainConfig.contracts.flatMap(contract => contract.events);
141
+ sources$1 = [
142
+ HyperSyncSolanaSource.make({
143
+ chain: chain,
144
+ endpointUrl: hypersync,
145
+ apiToken: Env.envioApiToken,
146
+ eventConfigs: svmEventConfigs,
147
+ clientMaxRetries: Env.hyperSyncClientMaxRetries,
148
+ clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis
149
+ }),
150
+ Svm.makeRPCSource(chain, rpc, "Fallback")
151
+ ];
152
+ } else {
153
+ sources$1 = [Svm.makeRPCSource(chain, rpc, undefined)];
154
+ }
137
155
  break;
138
156
  case "CustomSources" :
139
157
  sources$1 = sources._0;