envio 3.0.2-svm-alpha.0 → 3.0.2

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 +1 -155
  4. package/package.json +7 -6
  5. package/src/ChainFetcher.res +1 -25
  6. package/src/ChainFetcher.res.mjs +1 -19
  7. package/src/Config.res +94 -156
  8. package/src/Config.res.mjs +97 -60
  9. package/src/Core.res +0 -32
  10. package/src/Env.res.mjs +2 -1
  11. package/src/Envio.res +0 -94
  12. package/src/EventConfigBuilder.res +25 -63
  13. package/src/EventConfigBuilder.res.mjs +8 -37
  14. package/src/HandlerLoader.res +1 -12
  15. package/src/HandlerLoader.res.mjs +1 -6
  16. package/src/Internal.res +0 -38
  17. package/src/Main.res +3 -53
  18. package/src/Main.res.mjs +2 -34
  19. package/src/Persistence.res +17 -2
  20. package/src/Persistence.res.mjs +14 -2
  21. package/src/SimulateItems.res +10 -23
  22. package/src/SimulateItems.res.mjs +6 -21
  23. package/src/bindings/ClickHouse.res +6 -2
  24. package/src/bindings/ClickHouse.res.mjs +3 -2
  25. package/src/sources/EventRouter.res +0 -65
  26. package/src/sources/EventRouter.res.mjs +0 -43
  27. package/src/sources/HyperSyncClient.res +157 -30
  28. package/src/sources/HyperSyncClient.res.mjs +6 -20
  29. package/src/sources/HyperSyncSource.res +8 -5
  30. package/src/sources/HyperSyncSource.res.mjs +8 -1
  31. package/src/sources/RpcSource.res.mjs +1 -1
  32. package/src/sources/Svm.res +2 -2
  33. package/src/sources/Svm.res.mjs +2 -3
  34. package/src/tui/Tui.res +2 -9
  35. package/src/tui/Tui.res.mjs +4 -19
  36. package/src/tui/components/TuiData.res +0 -3
  37. package/svm.schema.json +4 -345
  38. package/src/SvmTypes.res +0 -9
  39. package/src/SvmTypes.res.mjs +0 -14
  40. package/src/sources/HyperSyncSolanaClient.res +0 -227
  41. package/src/sources/HyperSyncSolanaClient.res.mjs +0 -25
  42. package/src/sources/HyperSyncSolanaSource.res +0 -515
  43. package/src/sources/HyperSyncSolanaSource.res.mjs +0 -441
package/src/Core.res CHANGED
@@ -4,41 +4,9 @@
4
4
 
5
5
  // NAPI encodes Rust `Option<T>` as `null | T` (never `undefined`), so the
6
6
  // tighter `Null.t` captures the exact boundary shape.
7
- //
8
- // Opaque carriers for the NAPI class constructors. Static factories
9
- // (`newWithAgent`, `fromConfig`, `fromSignatures`) hang off these via `@send`
10
- // in `HyperSyncClient.res` / `HyperSyncSolanaClient.res`.
11
- type hypersyncClientCtor
12
- type hypersyncSolanaClientCtor
13
- type decoderCtor
14
-
15
- /// JS shape of one decoded instruction. Mirrors `DecodedInstructionJson` in
16
- /// `packages/cli/src/hypersync_source_svm/decoder.rs`. The `argsJson` /
17
- /// `accountsJson` fields are stringified to side-step napi-rs's lack of
18
- /// native `serde_json::Value` passthrough; callers `JSON.parse` once.
19
- type svmDecodedInstruction = {
20
- name: string,
21
- argsJson: string,
22
- accountsJson: string,
23
- extraAccounts: array<string>,
24
- }
25
-
26
7
  type addon = {
27
8
  getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
28
9
  runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
29
- @as("HypersyncClient")
30
- hypersyncClient: hypersyncClientCtor,
31
- @as("HypersyncSolanaClient")
32
- hypersyncSolanaClient: hypersyncSolanaClientCtor,
33
- @as("Decoder")
34
- decoder: decoderCtor,
35
- setLogLevel: string => unit,
36
- registerProgramSchema: (~descriptorJson: string) => int,
37
- decodeInstruction: (
38
- ~schemaHandle: int,
39
- ~dataHex: string,
40
- ~accounts: array<string>,
41
- ) => Null.t<svmDecodedInstruction>,
42
10
  }
43
11
 
44
12
  @module("node:module") external createRequire: string => {..} = "createRequire"
package/src/Env.res.mjs CHANGED
@@ -7,6 +7,7 @@ import * as Belt_Option from "@rescript/runtime/lib/es6/Belt_Option.js";
7
7
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
8
  import * as HyperSyncClient from "./sources/HyperSyncClient.res.mjs";
9
9
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
10
+ import * as HypersyncClient from "@envio-dev/hypersync-client";
10
11
 
11
12
  import 'dotenv/config'
12
13
  ;
@@ -61,7 +62,7 @@ let hypersyncClientEnableQueryCaching = EnvSafe.get(envSafe, "ENVIO_HYPERSYNC_CL
61
62
 
62
63
  let hypersyncLogLevel = EnvSafe.get(envSafe, "ENVIO_HYPERSYNC_LOG_LEVEL", HyperSyncClient.logLevelSchema, undefined, "info", undefined, undefined);
63
64
 
64
- HyperSyncClient.setLogLevel(hypersyncLogLevel);
65
+ HypersyncClient.setLogLevel(hypersyncLogLevel);
65
66
 
66
67
  let logStrategy = EnvSafe.get(envSafe, "LOG_STRATEGY", S$RescriptSchema.$$enum([
67
68
  "ecs-file",
package/src/Envio.res CHANGED
@@ -20,100 +20,6 @@ type svmOnSlotArgs<'context> = {
20
20
  context: 'context,
21
21
  }
22
22
 
23
- /** Borsh-decoded instruction view. Present whenever a `ProgramSchema` was
24
- attached to the program (bundled schema, Anchor IDL, or hand-written YAML
25
- `accounts`/`args`). Absent (`None`) when no schema applied or the
26
- discriminator didn't match any registered instruction. */
27
- type svmDecodedInstruction = {
28
- /** Schema-declared instruction name (matches the codegen module suffix). */
29
- name: string,
30
- /** Borsh-decoded args. `JSON.Object({})` for no-arg instructions
31
- (e.g. `VerifyCollection`). POC types this as raw `JSON.t`; cast at the
32
- handler with `(json :> MyArgsType)` until typed codegen lands. */
33
- args: JSON.t,
34
- /** Named accounts in schema order. Keys are exactly the schema-declared
35
- names; values are base58 pubkey strings. */
36
- accounts: dict<string>,
37
- /** Accounts beyond the schema's named list (Anchor `remaining_accounts`,
38
- IDL drift). `[]` when counts match. */
39
- extraAccounts: array<string>,
40
- }
41
-
42
- type svmInstruction = {
43
- programId: SvmTypes.Pubkey.t,
44
- /** Raw instruction bytes as `0x`-prefixed hex. */
45
- data: string,
46
- accounts: array<SvmTypes.Pubkey.t>,
47
- /** Path through the call tree: `[outerIndex]` for top-level instructions,
48
- appended child indices for inner CPI calls. */
49
- instructionAddress: array<int>,
50
- isInner: bool,
51
- /** Discriminator prefixes pre-extracted by HyperSync. Each is `Some` only
52
- when the underlying instruction is at least that long. */
53
- d1?: string,
54
- d2?: string,
55
- d4?: string,
56
- d8?: string,
57
- /** Borsh-decoded view. See [[svmDecodedInstruction]]. */
58
- decoded?: svmDecodedInstruction,
59
- }
60
-
61
- type svmTransaction = {
62
- signatures: array<string>,
63
- feePayer?: SvmTypes.Pubkey.t,
64
- success?: bool,
65
- err?: string,
66
- fee?: bigint,
67
- computeUnitsConsumed?: bigint,
68
- accountKeys: array<SvmTypes.Pubkey.t>,
69
- recentBlockhash?: string,
70
- version?: string,
71
- }
72
-
73
- type svmLog = {
74
- kind: string,
75
- message: string,
76
- }
77
-
78
- /** Inner block record on `svmInstructionEvent`. Field names follow EVM/Fuel
79
- (`height`, `time`, `hash`) so the shared `Ecosystem.t` getters in
80
- `Svm.res` work uniformly across ecosystems — `height` carries the slot. */
81
- type svmInstructionEventBlock = {
82
- /** Slot number. Named `height` so the shared ecosystem getter reads it. */
83
- height: int,
84
- /** Unix block time (seconds). `0` when HyperSync didn't return a block
85
- for this instruction's slot. */
86
- time: int,
87
- /** Block hash. Currently always empty — populated by the future
88
- reorg-guard `queryBlockHash(slot)` route. */
89
- hash: string,
90
- }
91
-
92
- /** The per-instruction payload handlers receive on `.event`. Mirrors the
93
- EVM `type event` shape inside generated per-event modules. */
94
- type svmInstructionEvent = {
95
- contractName: string,
96
- eventName: string,
97
- instruction: svmInstruction,
98
- /** Parent transaction. `None` when the per-instruction
99
- `include_transaction` flag is `false`. */
100
- transaction: option<svmTransaction>,
101
- /** Program log entries scoped to this instruction. `None` when the
102
- per-instruction `include_logs` flag is `false`. */
103
- logs: option<array<svmLog>>,
104
- /** Convenience alias for `block.height`. */
105
- slot: int,
106
- /** Convenience alias for `block.time`. */
107
- blockTime: option<int>,
108
- block: svmInstructionEventBlock,
109
- }
110
-
111
- /** Arguments passed to handlers registered via `indexer.onInstruction`. */
112
- type svmOnInstructionArgs<'context> = {
113
- event: svmInstructionEvent,
114
- context: 'context,
115
- }
116
-
117
23
  // Internal-only type for the `indexer.onBlock` (and SVM `onSlot`) `where`
118
24
  // callback argument. The canonical TypeScript shape lives in
119
25
  // `packages/envio/index.d.ts`; the ReScript declaration here is free to
@@ -142,25 +142,28 @@ let rec abiTypeToDefaultValue = (abiType: string): unknown => {
142
142
 
143
143
  // ============== Named-tuple (struct) schema helpers ==============
144
144
 
145
- // Build a simulate schema that honours component names: whenever an event param
146
- // (or nested field) has components, we decode it as an object with named fields
147
- // rather than a positional tuple. Walks through array wrappers so `struct[]`
148
- // still produces `array<{...}>`.
149
- let rec componentsToSimulateSchema = (abiType: string, components: array<eventParamComponent>): S.t<
150
- unknown,
151
- > => {
145
+ // Build an object schema that honours component names: whenever an event param
146
+ // (or nested field) has components, decode/serialize it as an object with
147
+ // named fields rather than a positional tuple. Walks through array wrappers so
148
+ // `struct[]` still produces `array<{...}>`. `~leafSchema` picks the per-ABI
149
+ // schema for non-tuple leaves (raw-event vs simulate variants differ in how
150
+ // they accept primitives — string-encoded numbers vs native bigints).
151
+ let rec componentsToObjectSchema = (
152
+ ~leafSchema: string => S.t<unknown>,
153
+ abiType: string,
154
+ components: array<eventParamComponent>,
155
+ ): S.t<unknown> => {
152
156
  if abiType->String.endsWith("]") {
153
157
  let bracketIdx = abiType->String.lastIndexOf("[")
154
158
  let baseType = abiType->String.slice(~start=0, ~end=bracketIdx)
155
- S.array(componentsToSimulateSchema(baseType, components))->S.toUnknown
159
+ S.array(componentsToObjectSchema(~leafSchema, baseType, components))->S.toUnknown
156
160
  } else {
157
- // Must be a tuple at this level: build a record keyed by component names.
158
161
  S.object(s => {
159
162
  let dict = Dict.make()
160
163
  components->Array.forEach(c => {
161
164
  let childSchema = switch c.components {
162
- | Some(sub) => componentsToSimulateSchema(c.abiType, sub)
163
- | None => abiTypeToSimulateSchema(c.abiType)
165
+ | Some(sub) => componentsToObjectSchema(~leafSchema, c.abiType, sub)
166
+ | None => leafSchema(c.abiType)
164
167
  }
165
168
  dict->Dict.set(c.name, s.field(c.name, childSchema))
166
169
  })
@@ -233,7 +236,16 @@ let buildParamsSchema = (params: array<eventParam>): S.t<Internal.eventParams> =
233
236
  S.object(s => {
234
237
  let dict = Dict.make()
235
238
  params->Array.forEach(p => {
236
- dict->Dict.set(p.name, s.field(p.name, abiTypeToSchema(p.abiType)))
239
+ // Indexed structs arrive as keccak256 topic hashes (single hex
240
+ // strings), so they keep the positional/leaf path; only non-indexed
241
+ // tuple params get the named-object shape that the HyperSync decoder
242
+ // (componentsToRemapper) produces.
243
+ let paramSchema = switch p.components {
244
+ | Some(components) if !p.indexed =>
245
+ componentsToObjectSchema(~leafSchema=abiTypeToSchema, p.abiType, components)
246
+ | _ => abiTypeToSchema(p.abiType)
247
+ }
248
+ dict->Dict.set(p.name, s.field(p.name, paramSchema))
237
249
  })
238
250
  dict
239
251
  })->(Utils.magic: S.t<dict<unknown>> => S.t<Internal.eventParams>)
@@ -255,7 +267,7 @@ let buildSimulateParamsSchema = (params: array<eventParam>): S.t<Internal.eventP
255
267
  params->Array.forEach(p => {
256
268
  let (paramSchema, paramDefault) = switch p.components {
257
269
  | Some(components) => (
258
- componentsToSimulateSchema(p.abiType, components),
270
+ componentsToObjectSchema(~leafSchema=abiTypeToSimulateSchema, p.abiType, components),
259
271
  componentsToDefaultValue(p.abiType, components),
260
272
  )
261
273
  | None => (abiTypeToSimulateSchema(p.abiType), abiTypeToDefaultValue(p.abiType))
@@ -484,56 +496,6 @@ let buildEvmEventConfig = (
484
496
 
485
497
  // ============== Build Fuel event config ==============
486
498
 
487
- let buildSvmInstructionEventConfig = (
488
- ~contractName: string,
489
- ~instructionName: string,
490
- ~programId: SvmTypes.Pubkey.t,
491
- ~discriminator: option<string>,
492
- ~discriminatorByteLen: int,
493
- ~includeTransaction: bool,
494
- ~includeLogs: bool,
495
- ~accountFilters: array<Internal.svmAccountFilter>,
496
- ~isInner: option<bool>,
497
- ~isWildcard: bool,
498
- ~handler: option<Internal.handler>,
499
- ~contractRegister: option<Internal.contractRegister>,
500
- ~accounts: array<string>=[],
501
- ~args: JSON.t=JSON.Null,
502
- ~definedTypes: JSON.t=JSON.Null,
503
- ~startBlock: option<int>=?,
504
- ): Internal.svmInstructionEventConfig => {
505
- let paramsSchema =
506
- S.json(~validate=false)
507
- ->Utils.Schema.coerceToJsonPgType
508
- ->(Utils.magic: S.t<JSON.t> => S.t<Internal.eventParams>)
509
- {
510
- id: switch discriminator {
511
- | Some(d) => d
512
- | None => "none"
513
- },
514
- name: instructionName,
515
- contractName,
516
- isWildcard,
517
- handler,
518
- contractRegister,
519
- paramsRawEventSchema: paramsSchema,
520
- simulateParamsSchema: paramsSchema,
521
- filterByAddresses: false,
522
- dependsOnAddresses: !isWildcard,
523
- startBlock,
524
- programId,
525
- discriminator,
526
- discriminatorByteLen,
527
- includeTransaction,
528
- includeLogs,
529
- accountFilters,
530
- isInner,
531
- accounts,
532
- args,
533
- definedTypes,
534
- }
535
- }
536
-
537
499
  let buildFuelEventConfig = (
538
500
  ~contractName: string,
539
501
  ~eventName: string,
@@ -144,13 +144,13 @@ function abiTypeToDefaultValue(abiType) {
144
144
  }
145
145
  }
146
146
 
147
- function componentsToSimulateSchema(abiType, components) {
147
+ function componentsToObjectSchema(leafSchema, abiType, components) {
148
148
  if (!abiType.endsWith("]")) {
149
149
  return S$RescriptSchema.object(s => {
150
150
  let dict = {};
151
151
  components.forEach(c => {
152
152
  let sub = c.components;
153
- let childSchema = sub !== undefined ? componentsToSimulateSchema(c.abiType, sub) : abiTypeToSimulateSchema(c.abiType);
153
+ let childSchema = sub !== undefined ? componentsToObjectSchema(leafSchema, c.abiType, sub) : leafSchema(c.abiType);
154
154
  dict[c.name] = s.f(c.name, childSchema);
155
155
  });
156
156
  return dict;
@@ -158,7 +158,7 @@ function componentsToSimulateSchema(abiType, components) {
158
158
  }
159
159
  let bracketIdx = abiType.lastIndexOf("[");
160
160
  let baseType = abiType.slice(0, bracketIdx);
161
- return S$RescriptSchema.array(componentsToSimulateSchema(baseType, components));
161
+ return S$RescriptSchema.array(componentsToObjectSchema(leafSchema, baseType, components));
162
162
  }
163
163
 
164
164
  function componentsToDefaultValue(abiType, components) {
@@ -197,7 +197,9 @@ function buildParamsSchema(params) {
197
197
  return S$RescriptSchema.object(s => {
198
198
  let dict = {};
199
199
  params.forEach(p => {
200
- dict[p.name] = s.f(p.name, abiTypeToSchema(p.abiType));
200
+ let components = p.components;
201
+ let paramSchema = components !== undefined && !p.indexed ? componentsToObjectSchema(abiTypeToSchema, p.abiType, components) : abiTypeToSchema(p.abiType);
202
+ dict[p.name] = s.f(p.name, paramSchema);
201
203
  });
202
204
  return dict;
203
205
  });
@@ -213,7 +215,7 @@ function buildSimulateParamsSchema(params) {
213
215
  params.forEach(p => {
214
216
  let components = p.components;
215
217
  let match = components !== undefined ? [
216
- componentsToSimulateSchema(p.abiType, components),
218
+ componentsToObjectSchema(abiTypeToSimulateSchema, p.abiType, components),
217
219
  componentsToDefaultValue(p.abiType, components)
218
220
  ] : [
219
221
  abiTypeToSimulateSchema(p.abiType),
@@ -350,36 +352,6 @@ function buildEvmEventConfig(contractName, eventName, sighash, params, isWildcar
350
352
  };
351
353
  }
352
354
 
353
- function buildSvmInstructionEventConfig(contractName, instructionName, programId, discriminator, discriminatorByteLen, includeTransaction, includeLogs, accountFilters, isInner, isWildcard, handler, contractRegister, accountsOpt, argsOpt, definedTypesOpt, startBlock) {
354
- let accounts = accountsOpt !== undefined ? accountsOpt : [];
355
- let args = argsOpt !== undefined ? argsOpt : null;
356
- let definedTypes = definedTypesOpt !== undefined ? definedTypesOpt : null;
357
- let paramsSchema = Utils.Schema.coerceToJsonPgType(S$RescriptSchema.json(false));
358
- return {
359
- id: discriminator !== undefined ? discriminator : "none",
360
- name: instructionName,
361
- contractName: contractName,
362
- isWildcard: isWildcard,
363
- filterByAddresses: false,
364
- dependsOnAddresses: !isWildcard,
365
- handler: handler,
366
- contractRegister: contractRegister,
367
- paramsRawEventSchema: paramsSchema,
368
- simulateParamsSchema: paramsSchema,
369
- startBlock: startBlock,
370
- programId: programId,
371
- discriminator: discriminator,
372
- discriminatorByteLen: discriminatorByteLen,
373
- includeTransaction: includeTransaction,
374
- includeLogs: includeLogs,
375
- accountFilters: accountFilters,
376
- isInner: isInner,
377
- accounts: accounts,
378
- args: args,
379
- definedTypes: definedTypes
380
- };
381
- }
382
-
383
355
  function buildFuelEventConfig(contractName, eventName, kind, sighash, rawAbi, isWildcard, handler, contractRegister, startBlock) {
384
356
  let fuelKind;
385
357
  switch (kind) {
@@ -447,7 +419,7 @@ export {
447
419
  abiTypeToSchema,
448
420
  abiTypeToSimulateSchema,
449
421
  abiTypeToDefaultValue,
450
- componentsToSimulateSchema,
422
+ componentsToObjectSchema,
451
423
  componentsToDefaultValue,
452
424
  componentsToRemapper,
453
425
  buildParamsSchema,
@@ -458,7 +430,6 @@ export {
458
430
  alwaysIncludedBlockFields,
459
431
  resolveFieldSelection,
460
432
  buildEvmEventConfig,
461
- buildSvmInstructionEventConfig,
462
433
  buildFuelEventConfig,
463
434
  }
464
435
  /* eventParamComponentSchema Not a pure module */
@@ -142,18 +142,7 @@ let applyRegistrations = (~config: Config.t): Config.t => {
142
142
  dependsOnAddresses: Internal.dependsOnAddresses(~isWildcard, ~filterByAddresses),
143
143
  } :> Internal.eventConfig)
144
144
  | Svm =>
145
- let svmEv =
146
- ev->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
147
- ({
148
- ...svmEv,
149
- isWildcard,
150
- handler,
151
- contractRegister,
152
- dependsOnAddresses: Internal.dependsOnAddresses(
153
- ~isWildcard,
154
- ~filterByAddresses=false,
155
- ),
156
- } :> Internal.eventConfig)
145
+ JsError.throwWithMessage(`SVM does not support indexer.onEvent or indexer.contractRegister. Use indexer.onSlot for per-slot handlers.`)
157
146
  }
158
147
  },
159
148
  )
@@ -109,12 +109,7 @@ function applyRegistrations(config) {
109
109
  kind: ev.kind
110
110
  };
111
111
  case "svm" :
112
- let newrecord = {...ev};
113
- newrecord.contractRegister = contractRegister;
114
- newrecord.handler = handler;
115
- newrecord.dependsOnAddresses = Internal.dependsOnAddresses(isWildcard, false);
116
- newrecord.isWildcard = isWildcard;
117
- return newrecord;
112
+ return Stdlib_JsError.throwWithMessage(`SVM does not support indexer.onEvent or indexer.contractRegister. Use indexer.onSlot for per-slot handlers.`);
118
113
  }
119
114
  });
120
115
  return {
package/src/Internal.res CHANGED
@@ -422,44 +422,6 @@ type evmContractConfig = {
422
422
  events: array<evmEventConfig>,
423
423
  }
424
424
 
425
- type svmAccountFilter = {
426
- position: int,
427
- values: array<SvmTypes.Pubkey.t>,
428
- }
429
-
430
- type svmInstructionEventConfig = {
431
- ...eventConfig,
432
- /** Base58 Solana program id this instruction belongs to. */
433
- programId: SvmTypes.Pubkey.t,
434
- /** Hex-encoded discriminator. `None` matches every instruction in the program. */
435
- discriminator: option<string>,
436
- /** Length of the discriminator in bytes (0 / 1 / 2 / 4 / 8). Drives the
437
- `dN` selector at query time and the dispatch-key precomputation in the
438
- router. */
439
- discriminatorByteLen: int,
440
- includeTransaction: bool,
441
- includeLogs: bool,
442
- accountFilters: array<svmAccountFilter>,
443
- /** `None` matches both outer and inner (CPI-invoked) instructions. */
444
- isInner: option<bool>,
445
- /** Positional account names from the Borsh schema, in declared order.
446
- `[]` means no schema is attached for this instruction. */
447
- accounts: array<string>,
448
- /** Borsh args layout as `Vec<ArgDef>` JSON (see `human_config::svm::ArgDef`
449
- on the Rust side). `JSON.Null` means no schema is attached. */
450
- args: JSON.t,
451
- /** Program-level nominal-type registry (`BTreeMap<String, ArgType>` JSON).
452
- Duplicated on every event of the same program — the runtime dedups by
453
- `programId` when registering. `JSON.Null` when empty. */
454
- definedTypes: JSON.t,
455
- }
456
-
457
- type svmProgramConfig = {
458
- name: string,
459
- programId: SvmTypes.Pubkey.t,
460
- instructions: array<svmInstructionEventConfig>,
461
- }
462
-
463
425
  type indexingAddress = {
464
426
  address: Address.t,
465
427
  contractName: string,
package/src/Main.res CHANGED
@@ -298,57 +298,6 @@ let getGlobalIndexer = (): 'indexer => {
298
298
  )
299
299
  }
300
300
 
301
- // SVM identity: `{program, instruction}` from TS or
302
- // `{instruction: GADT{contract, _0}}` from ReScript. Same two-format dance
303
- // as the EVM `parseIdentityConfig`, but reading the SVM-native field names.
304
- let parseSvmIdentityConfig = (identityConfig: 'a) => {
305
- let raw =
306
- identityConfig->(
307
- Utils.magic: 'a => {
308
- "program": unknown,
309
- "instruction": unknown,
310
- "where": option<JSON.t>,
311
- }
312
- )
313
- let (programName, instructionName) = if typeof(raw["program"]) === #string {
314
- (
315
- raw["program"]->(Utils.magic: unknown => string),
316
- raw["instruction"]->(Utils.magic: unknown => string),
317
- )
318
- } else {
319
- let inst =
320
- raw["instruction"]->(Utils.magic: unknown => {"contract": string, "_0": string})
321
- (inst["contract"], inst["_0"])
322
- }
323
- let where = raw["where"]
324
- let eventOptions: option<Internal.eventOptions<_>> = switch where {
325
- | None => None
326
- | Some(_) =>
327
- Some({
328
- where: ?(where->(Utils.magic: option<JSON.t> => option<_>)),
329
- })
330
- }
331
- (programName, instructionName, eventOptions)
332
- }
333
-
334
- // onInstruction: delegates to HandlerRegister.setHandler. The SVM analog of
335
- // onEvent; the registration store keys on `(contractName, eventName)` which
336
- // for SVM is `(programName, instructionName)`.
337
- let onInstructionFn = (identityConfig: 'a, handler: 'b) => {
338
- HandlerRegister.throwIfFinishedRegistration(~methodName="onInstruction")
339
- let (programName, instructionName, eventOptions) = parseSvmIdentityConfig(identityConfig)
340
- HandlerRegister.setHandler(
341
- ~contractName=programName,
342
- ~eventName=instructionName,
343
- handler->(
344
- Utils.magic: 'b => Internal.genericHandler<
345
- Internal.genericHandlerArgs<Internal.event, Internal.handlerContext>,
346
- >
347
- ),
348
- ~eventOptions,
349
- )
350
- }
351
-
352
301
  // contractRegister: delegates to HandlerRegister.setContractRegister
353
302
  let contractRegisterFn = (identityConfig: 'a, handler: 'b) => {
354
303
  HandlerRegister.throwIfFinishedRegistration(~methodName="contractRegister")
@@ -507,7 +456,7 @@ let getGlobalIndexer = (): 'indexer => {
507
456
  "contractRegister",
508
457
  "onBlock",
509
458
  ]
510
- | Svm => ["name", "description", "chainIds", "chains", "onInstruction", "onSlot"]
459
+ | Svm => ["name", "description", "chainIds", "chains", "onSlot"]
511
460
  }
512
461
  keysMemo := Some(keys)
513
462
  keys
@@ -528,7 +477,6 @@ let getGlobalIndexer = (): 'indexer => {
528
477
  chains->(Utils.magic: {..} => unknown)
529
478
  }
530
479
  | "onEvent" => onEventFn->Utils.magic
531
- | "onInstruction" => onInstructionFn->Utils.magic
532
480
  | "contractRegister" => contractRegisterFn->Utils.magic
533
481
  | "onBlock" | "onSlot" => onBlockFn->Utils.magic
534
482
  | _ =>
@@ -674,6 +622,7 @@ let migrate = async (~reset) => {
674
622
  ~chainConfigs=config.chainMap->ChainMap.values,
675
623
  ~envioInfo=getEnvioInfo(),
676
624
  ~resetCommand="envio local db-migrate setup",
625
+ ~runCommand=None,
677
626
  )
678
627
  await persistence.storage.close()
679
628
  }
@@ -720,6 +669,7 @@ let start = async (
720
669
  ~chainConfigs=configWithoutRegistrations.chainMap->ChainMap.values,
721
670
  ~envioInfo=getEnvioInfo(),
722
671
  ~resetCommand=isDevelopmentMode ? "envio dev -r" : "envio start -r",
672
+ ~runCommand=Some(isDevelopmentMode ? "envio dev" : "envio start"),
723
673
  )
724
674
 
725
675
  // `Config.loadWithoutRegistrations` never sees registration state; handler,
package/src/Main.res.mjs CHANGED
@@ -249,35 +249,6 @@ function getGlobalIndexer() {
249
249
  let match = parseIdentityConfig(identityConfig);
250
250
  HandlerRegister.setHandler(match[0], match[1], handler, match[2], undefined);
251
251
  };
252
- let parseSvmIdentityConfig = identityConfig => {
253
- let match;
254
- if (typeof identityConfig.program === "string") {
255
- match = [
256
- identityConfig.program,
257
- identityConfig.instruction
258
- ];
259
- } else {
260
- let inst = identityConfig.instruction;
261
- match = [
262
- inst.contract,
263
- inst._0
264
- ];
265
- }
266
- let where = identityConfig.where;
267
- let eventOptions = where !== undefined ? ({
268
- where: where
269
- }) : undefined;
270
- return [
271
- match[0],
272
- match[1],
273
- eventOptions
274
- ];
275
- };
276
- let onInstructionFn = (identityConfig, handler) => {
277
- HandlerRegister.throwIfFinishedRegistration("onInstruction");
278
- let match = parseSvmIdentityConfig(identityConfig);
279
- HandlerRegister.setHandler(match[0], match[1], handler, match[2], undefined);
280
- };
281
252
  let contractRegisterFn = (identityConfig, handler) => {
282
253
  HandlerRegister.throwIfFinishedRegistration("contractRegister");
283
254
  let match = parseIdentityConfig(identityConfig);
@@ -369,7 +340,6 @@ function getGlobalIndexer() {
369
340
  "description",
370
341
  "chainIds",
371
342
  "chains",
372
- "onInstruction",
373
343
  "onSlot"
374
344
  ];
375
345
  break;
@@ -402,8 +372,6 @@ function getGlobalIndexer() {
402
372
  return Config.loadWithoutRegistrations().name;
403
373
  case "onEvent" :
404
374
  return onEventFn;
405
- case "onInstruction" :
406
- return onInstructionFn;
407
375
  case "onBlock" :
408
376
  case "onSlot" :
409
377
  return onBlockFn;
@@ -504,7 +472,7 @@ function getEnvioInfo() {
504
472
  async function migrate(reset) {
505
473
  let config = Config.loadWithoutRegistrations();
506
474
  let persistence = PgStorage.makePersistenceFromConfig(config, undefined);
507
- await Persistence.init(persistence, ChainMap.values(config.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), "envio local db-migrate setup", reset);
475
+ await Persistence.init(persistence, ChainMap.values(config.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), "envio local db-migrate setup", undefined, reset);
508
476
  return await persistence.storage.close();
509
477
  }
510
478
 
@@ -529,7 +497,7 @@ async function start(persistence, resetOpt, isTestOpt, exitAfterFirstEventBlockO
529
497
  let isDevelopmentMode = !isTest && configWithoutRegistrations.isDev;
530
498
  let persistence$1 = persistence !== undefined ? persistence : PgStorage.makePersistenceFromConfig(configWithoutRegistrations, undefined);
531
499
  globalPersistenceRef.contents = persistence$1;
532
- await Persistence.init(persistence$1, ChainMap.values(configWithoutRegistrations.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), isDevelopmentMode ? "envio dev -r" : "envio start -r", reset);
500
+ await Persistence.init(persistence$1, ChainMap.values(configWithoutRegistrations.chainMap), Config.stripSensitiveData(Config.getPublicConfigJson()), isDevelopmentMode ? "envio dev -r" : "envio start -r", isDevelopmentMode ? "envio dev" : "envio start", reset);
533
501
  let match = await HandlerLoader.registerAllHandlers(configWithoutRegistrations);
534
502
  let registrations = match[1];
535
503
  let config = match[0];
@@ -168,7 +168,7 @@ let make = (
168
168
  }
169
169
 
170
170
  let init = {
171
- async (persistence, ~chainConfigs, ~envioInfo, ~resetCommand, ~reset=false) => {
171
+ async (persistence, ~chainConfigs, ~envioInfo, ~resetCommand, ~runCommand, ~reset=false) => {
172
172
  try {
173
173
  let shouldRun = switch persistence.storageStatus {
174
174
  | Unknown => true
@@ -212,7 +212,22 @@ let init = {
212
212
  | None => ["envio info is missing — storage initialized by an older envio"]
213
213
  | Some(stored) => Config.diffPaths(~stored, ~current=envioInfo)
214
214
  }
215
- Config.throwIfIncompatible(changedPaths, ~resetCommand)
215
+ // `storage.clickhouse` is serialized as a plain bool by the
216
+ // public config (see Rust `StorageConfig`), so probe for
217
+ // `Boolean(true)`, not an object.
218
+ let hasClickhouse = switch envioInfo {
219
+ | Object(d) =>
220
+ switch d->Dict.get("storage") {
221
+ | Some(Object(s)) =>
222
+ switch s->Dict.get("clickhouse") {
223
+ | Some(Boolean(true)) => true
224
+ | _ => false
225
+ }
226
+ | _ => false
227
+ }
228
+ | _ => false
229
+ }
230
+ Config.throwIfIncompatible(changedPaths, ~resetCommand, ~runCommand, ~hasClickhouse)
216
231
  persistence.storageStatus = Ready(initialState)
217
232
  let progress = Dict.make()
218
233
  initialState.chains->Array.forEach(c => {