envio 3.9.0 → 3.10.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.
Files changed (110) hide show
  1. package/evm.schema.json +26 -0
  2. package/fuel.schema.json +26 -0
  3. package/index.d.ts +35 -3
  4. package/package.json +6 -7
  5. package/src/Batch.res +9 -0
  6. package/src/ChainState.res +35 -39
  7. package/src/ChainState.res.mjs +14 -11
  8. package/src/ChainState.resi +15 -7
  9. package/src/Config.res +52 -44
  10. package/src/Config.res.mjs +51 -17
  11. package/src/Core.res +4 -0
  12. package/src/CrossChainState.res +15 -23
  13. package/src/CrossChainState.res.mjs +10 -18
  14. package/src/CrossChainState.resi +4 -1
  15. package/src/Ecosystem.res +7 -2
  16. package/src/Envio.res +6 -4
  17. package/src/EventConfigBuilder.res +169 -26
  18. package/src/EventConfigBuilder.res.mjs +124 -15
  19. package/src/HandlerRegister.res +21 -4
  20. package/src/HandlerRegister.res.mjs +24 -5
  21. package/src/InMemoryStore.res +9 -7
  22. package/src/InMemoryStore.res.mjs +3 -3
  23. package/src/IndexerState.res +43 -6
  24. package/src/IndexerState.res.mjs +36 -7
  25. package/src/IndexerState.resi +4 -4
  26. package/src/Internal.res +16 -17
  27. package/src/Metrics.res +72 -6
  28. package/src/Metrics.res.mjs +30 -2
  29. package/src/Persistence.res +36 -30
  30. package/src/Persistence.res.mjs +1 -20
  31. package/src/PgStorage.res +104 -56
  32. package/src/PgStorage.res.mjs +57 -35
  33. package/src/PruneStaleHistory.res +85 -52
  34. package/src/PruneStaleHistory.res.mjs +63 -37
  35. package/src/Rollback.res +68 -45
  36. package/src/Rollback.res.mjs +38 -19
  37. package/src/RollbackCommit.res +7 -9
  38. package/src/RollbackCommit.res.mjs +5 -6
  39. package/src/SafeCheckpointTracking.res +11 -11
  40. package/src/SafeCheckpointTracking.res.mjs +7 -6
  41. package/src/SimulateItems.res +33 -18
  42. package/src/SimulateItems.res.mjs +26 -22
  43. package/src/Sink.res +38 -26
  44. package/src/Sink.res.mjs +21 -16
  45. package/src/TestIndexer.res +5 -5
  46. package/src/TestIndexer.res.mjs +1 -1
  47. package/src/Utils.res +170 -0
  48. package/src/Utils.res.mjs +153 -0
  49. package/src/Writing.res +2 -2
  50. package/src/Writing.res.mjs +1 -1
  51. package/src/bindings/ClickHouse.res +366 -698
  52. package/src/bindings/ClickHouse.res.mjs +371 -421
  53. package/src/bindings/ClickHouseSink.res +337 -0
  54. package/src/bindings/ClickHouseSink.res.mjs +246 -0
  55. package/src/bindings/EventSource.res +8 -2
  56. package/src/bindings/NodeJs.res +6 -0
  57. package/src/bindings/Vitest.res +23 -0
  58. package/src/bindings/Vitest.res.mjs +3 -0
  59. package/src/bindings/WebSocket.res +9 -10
  60. package/src/db/CheckpointBounds.res +53 -0
  61. package/src/db/CheckpointBounds.res.mjs +44 -0
  62. package/src/db/EntityFilter.res +36 -17
  63. package/src/db/EntityFilter.res.mjs +25 -14
  64. package/src/db/EntityHistory.res +47 -22
  65. package/src/db/EntityHistory.res.mjs +19 -12
  66. package/src/db/InternalTable.res +84 -35
  67. package/src/db/InternalTable.res.mjs +56 -23
  68. package/src/db/RollbackFloors.res +55 -0
  69. package/src/db/RollbackFloors.res.mjs +92 -0
  70. package/src/db/Table.res +22 -4
  71. package/src/db/Table.res.mjs +30 -9
  72. package/src/sources/Evm.res +2 -0
  73. package/src/sources/Evm.res.mjs +2 -0
  74. package/src/sources/EvmHyperSyncSource.res +3 -3
  75. package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
  76. package/src/sources/EvmRpcWs.res +124 -0
  77. package/src/sources/EvmRpcWs.res.mjs +117 -0
  78. package/src/sources/Fuel.res +2 -0
  79. package/src/sources/Fuel.res.mjs +2 -0
  80. package/src/sources/FuelHyperSyncSource.res +1 -1
  81. package/src/sources/FuelHyperSyncSource.res.mjs +2 -1
  82. package/src/sources/HeightFeed.res +568 -0
  83. package/src/sources/HeightFeed.res.mjs +452 -0
  84. package/src/sources/HeightStream.res +257 -0
  85. package/src/sources/HeightStream.res.mjs +194 -0
  86. package/src/sources/HyperSync.res +5 -0
  87. package/src/sources/HyperSync.res.mjs +3 -0
  88. package/src/sources/HyperSync.resi +4 -0
  89. package/src/sources/HyperSyncSSE.res +49 -0
  90. package/src/sources/HyperSyncSSE.res.mjs +79 -0
  91. package/src/sources/RpcSource.res +1 -1
  92. package/src/sources/RpcSource.res.mjs +2 -2
  93. package/src/sources/Source.res +54 -1
  94. package/src/sources/Source.res.mjs +25 -0
  95. package/src/sources/SourceManager.res +220 -227
  96. package/src/sources/SourceManager.res.mjs +139 -164
  97. package/src/sources/SourceManager.resi +8 -0
  98. package/src/sources/Svm.res +2 -0
  99. package/src/sources/Svm.res.mjs +2 -0
  100. package/src/sources/SvmHyperSyncClient.res +70 -127
  101. package/src/sources/SvmHyperSyncClient.res.mjs +51 -25
  102. package/src/sources/SvmHyperSyncSource.res +18 -15
  103. package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
  104. package/svm.schema.json +1 -75
  105. package/src/MemoryStorage.res +0 -729
  106. package/src/MemoryStorage.res.mjs +0 -586
  107. package/src/sources/HyperSyncHeightStream.res +0 -129
  108. package/src/sources/HyperSyncHeightStream.res.mjs +0 -110
  109. package/src/sources/RpcWebSocketHeightStream.res +0 -175
  110. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -180
@@ -14,19 +14,14 @@ module Registration = {
14
14
  values: array<string>,
15
15
  }
16
16
 
17
- // The full per-(instruction, chain) registration passed to the Rust client
18
- // at construction: routing identity, the fetch state queries are built
19
- // from, and the Borsh schema pieces the client builds decoders from.
17
+ // One `onInstruction`/`contractRegister` binding of an instruction on a
18
+ // chain: the handler-specific routing state and the fetch state queries are
19
+ // built from. Chain-scoped `index` is echoed back on routed items.
20
20
  type input = {
21
- // Chain-scoped sequential registration index, echoed back on routed items.
22
21
  index: int,
23
- instructionName: string,
24
- contractName: string,
25
- programId: string,
26
22
  isWildcard: bool,
27
23
  // Earliest slot this registration accepts; `None` is unrestricted.
28
24
  startBlock: option<int>,
29
- discriminator?: string,
30
25
  isInner?: bool,
31
26
  // DNF: outer array is OR of AND-groups.
32
27
  accountFilters: array<array<accountFilter>>,
@@ -36,28 +31,73 @@ module Registration = {
36
31
  accountActivityFields: array<string>,
37
32
  logFields: array<string>,
38
33
  instructionFields: array<string>,
39
- // Borsh schema pieces; empty accounts + absent argsJson = no schema.
40
- accounts: array<string>,
34
+ }
35
+
36
+ // One config instruction: its identity (data prefix of any length; absent
37
+ // matches every instruction of the program) and Borsh layout, shared by
38
+ // every registration bound to it.
39
+ type instruction = {
40
+ name: string,
41
+ discriminator?: string,
41
42
  argsJson?: string,
43
+ registrations: array<input>,
44
+ }
45
+
46
+ type program = {
47
+ // The config's program name.
48
+ name: string,
49
+ programId: string,
42
50
  definedTypesJson?: string,
51
+ instructions: array<instruction>,
43
52
  }
44
53
 
54
+ // Groups a chain's registrations under the config instruction each was
55
+ // built from, so the Rust client holds one layout per instruction rather
56
+ // than a copy per registration.
45
57
  let fromOnEventRegistrations = (
46
58
  onEventRegistrations: array<Internal.svmOnEventRegistration>,
47
- ): array<input> =>
48
- onEventRegistrations->Array.map(reg => {
59
+ ): array<program> => {
60
+ let programs: array<program> = []
61
+ onEventRegistrations->Array.forEach(reg => {
49
62
  let eventConfig =
50
63
  reg.eventConfig->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
51
- {
64
+ let program = switch programs->Array.find(p => p.name === eventConfig.contractName) {
65
+ | Some(program) => program
66
+ | None =>
67
+ let program = {
68
+ name: eventConfig.contractName,
69
+ programId: eventConfig.programId->SvmTypes.Pubkey.toString,
70
+ definedTypesJson: ?switch eventConfig.definedTypes {
71
+ | JSON.Null => None
72
+ | definedTypes => Some(definedTypes->JSON.stringify)
73
+ },
74
+ instructions: [],
75
+ }
76
+ programs->Array.push(program)->ignore
77
+ program
78
+ }
79
+ let instruction = switch program.instructions->Array.find(i => i.name === eventConfig.name) {
80
+ | Some(instruction) => instruction
81
+ | None =>
82
+ let instruction = {
83
+ name: eventConfig.name,
84
+ discriminator: ?eventConfig.discriminator,
85
+ argsJson: ?switch eventConfig.args {
86
+ | JSON.Null => None
87
+ | args => Some(args->JSON.stringify)
88
+ },
89
+ registrations: [],
90
+ }
91
+ program.instructions->Array.push(instruction)->ignore
92
+ instruction
93
+ }
94
+ instruction.registrations
95
+ ->Array.push({
52
96
  index: reg.index,
53
- instructionName: eventConfig.name,
54
- contractName: eventConfig.contractName,
55
- programId: eventConfig.programId->SvmTypes.Pubkey.toString,
56
97
  isWildcard: reg.isWildcard,
57
98
  startBlock: reg.startBlock,
58
- discriminator: ?eventConfig.discriminator,
59
- isInner: ?eventConfig.isInner,
60
- accountFilters: eventConfig.accountFilters->Array.map(group =>
99
+ isInner: ?reg.isInner,
100
+ accountFilters: reg.accountFilters->Array.map(group =>
61
101
  group->Array.map(
62
102
  (filter): accountFilter => {
63
103
  position: filter.position,
@@ -70,71 +110,10 @@ module Registration = {
70
110
  accountActivityFields: reg.fieldSelection.accountActivityFields->Utils.Set.toArray,
71
111
  logFields: reg.fieldSelection.logFields->Utils.Set.toArray,
72
112
  instructionFields: reg.fieldSelection.instructionFields->Utils.Set.toArray,
73
- accounts: eventConfig.accounts,
74
- argsJson: ?switch eventConfig.args {
75
- | JSON.Null => None
76
- | args => Some(args->JSON.stringify)
77
- },
78
- definedTypesJson: ?switch eventConfig.definedTypes {
79
- | JSON.Null => None
80
- | definedTypes => Some(definedTypes->JSON.stringify)
81
- },
82
- }
113
+ })
114
+ ->ignore
83
115
  })
84
- }
85
-
86
- module QueryTypes = {
87
- type blockField =
88
- | @as("slot") Slot
89
- | @as("blockhash") Blockhash
90
- | @as("parent_slot") ParentSlot
91
- | @as("parent_blockhash") ParentBlockhash
92
- | @as("block_time") BlockTime
93
- | @as("block_height") BlockHeight
94
-
95
- type transactionField =
96
- | @as("slot") Slot
97
- | @as("transaction_index") TransactionIndex
98
- | @as("signatures") Signatures
99
- | @as("fee_payer") FeePayer
100
- | @as("success") Success
101
- | @as("err") Err
102
- | @as("fee") Fee
103
- | @as("compute_units_consumed") ComputeUnitsConsumed
104
- | @as("account_keys") AccountKeys
105
- | @as("recent_blockhash") RecentBlockhash
106
- | @as("version") Version
107
- | @as("loaded_addresses_writable") LoadedAddressesWritable
108
- | @as("loaded_addresses_readonly") LoadedAddressesReadonly
109
-
110
- type fieldSelection = {block?: array<blockField>, transaction?: array<transactionField>}
111
-
112
- /** Filter for selecting instruction calls. All non-empty fields are AND-ed:
113
- an instruction must match at least one value in every non-empty field.
114
-
115
- Discriminator filters (d1..d8) take hex-encoded byte prefixes ("0x" optional).
116
- Account filters (a0..a9) take base58 pubkey strings. */
117
- type instructionSelection = {
118
- executingAccount?: array<string>,
119
- d1?: array<string>,
120
- d2?: array<string>,
121
- d4?: array<string>,
122
- d8?: array<string>,
123
- isInner?: bool,
124
- /** Success of the parent transaction; absent matches both. */
125
- txSuccess?: bool,
126
- }
127
-
128
- // The `get` query surface, used only for block-data range queries; event
129
- // fetching goes through `getEventItems`, which builds its query in Rust.
130
- type query = {
131
- fromSlot: int,
132
- toSlot?: int,
133
- instructionCalls?: array<instructionSelection>,
134
- includeAllBlocks?: bool,
135
- fields?: fieldSelection,
136
- maxNumBlocks?: int,
137
- maxNumInstructions?: int,
116
+ programs
138
117
  }
139
118
  }
140
119
 
@@ -146,37 +125,6 @@ module ResponseTypes = {
146
125
  blockhash: string,
147
126
  blockTime: Null.t<int>,
148
127
  }
149
-
150
- type instructionCall = {
151
- slot: int,
152
- transactionIndex: int,
153
- instructionAddress: array<int>,
154
- /** The invoked program's account. */
155
- executingAccount: string,
156
- accountArguments: array<string>,
157
- data: string,
158
- d1: Null.t<string>,
159
- d2: Null.t<string>,
160
- d4: Null.t<string>,
161
- d8: Null.t<string>,
162
- isInner: bool,
163
- /** Success of the parent transaction, not of this invocation. */
164
- txSuccess: bool,
165
- /** Per-invocation failure reason (e.g. "custom program error: 0x1"). */
166
- error: Null.t<string>,
167
- computeUnitsConsumed: Null.t<bigint>,
168
- }
169
-
170
- type queryResponseData = {
171
- blocks: array<block>,
172
- instructionCalls: array<instructionCall>,
173
- }
174
-
175
- type queryResponse = {
176
- nextSlot: int,
177
- responseBytes: int,
178
- data: queryResponseData,
179
- }
180
128
  }
181
129
 
182
130
  module EventItems = {
@@ -213,12 +161,12 @@ module EventItems = {
213
161
  path: array<int>,
214
162
  programId: string,
215
163
  accounts: array<string>,
216
- data: string,
164
+ data: Uint8Array.t,
217
165
  isInner: bool,
218
- // Borsh-decoded args as a JSON object literal, `{}` when the routed
219
- // registration reads no args. An instruction the schema rejects is dropped
220
- // in Rust, so a selected `args` is always decoded.
221
- argsJson: string,
166
+ // Borsh-decoded args as a JS value tree (wide integers as bigint).
167
+ // Non-null exactly when the routed registration selected `args`: an
168
+ // instruction its layout rejects is dropped in Rust.
169
+ args: Null.t<unknown>,
222
170
  // Non-null only when the routed registration selected `fields.log`.
223
171
  logs: Null.t<array<log>>,
224
172
  }
@@ -234,16 +182,11 @@ module EventItems = {
234
182
  }
235
183
  }
236
184
 
237
- type query = QueryTypes.query
238
- type queryResponse = ResponseTypes.queryResponse
239
-
240
185
  type t = {
241
186
  getHeight: unit => promise<int>,
242
187
  // Block-hash query construction, pagination, and cursor-backed skipped-slot
243
188
  // coverage live in Rust.
244
189
  getBlockHashes: (~blockNumbers: array<int>) => promise<(BlockStore.t, array<RequestStat.t>)>,
245
- // Block-data range queries only; the store pages it returns are empty.
246
- get: (~query: query) => promise<(queryResponse, TransactionStore.t, BlockStore.t)>,
247
190
  // Returns the routed items plus pages of raw transactions and blocks (kept
248
191
  // in Rust), keyed by (slot, transactionIndex) / slot, materialised at batch
249
192
  // prep.
@@ -258,7 +201,7 @@ external classFromConfig: (
258
201
  Core.svmHyperSyncClientCtor,
259
202
  cfg,
260
203
  string,
261
- array<Registration.input>,
204
+ array<Registration.program>,
262
205
  AddressStore.t,
263
206
  ) => t = "fromConfig"
264
207
 
@@ -268,7 +211,7 @@ let make = (
268
211
  ~httpReqTimeoutMillis=?,
269
212
  ~retryBaseMs=?,
270
213
  ~retryCeilingMs=?,
271
- ~eventRegistrations=[],
214
+ ~programs=[],
272
215
  ~addressStore,
273
216
  ) => {
274
217
  let envioVersion = Utils.EnvioPackage.value.version
@@ -281,7 +224,7 @@ let make = (
281
224
  ?retryCeilingMs,
282
225
  },
283
226
  `hyperindex/${envioVersion}`,
284
- eventRegistrations,
227
+ programs,
285
228
  addressStore,
286
229
  )
287
230
  }
@@ -4,24 +4,55 @@ import * as Core from "../Core.res.mjs";
4
4
  import * as Utils from "../Utils.res.mjs";
5
5
 
6
6
  function fromOnEventRegistrations(onEventRegistrations) {
7
- return onEventRegistrations.map(reg => {
7
+ let programs = [];
8
+ onEventRegistrations.forEach(reg => {
8
9
  let eventConfig = reg.eventConfig;
9
- let args = eventConfig.args;
10
- let tmp;
11
- tmp = args === null ? undefined : JSON.stringify(args);
12
- let definedTypes = eventConfig.definedTypes;
13
- let tmp$1;
14
- tmp$1 = definedTypes === null ? undefined : JSON.stringify(definedTypes);
15
- return {
10
+ let program = programs.find(p => p.name === eventConfig.contractName);
11
+ let program$1;
12
+ if (program !== undefined) {
13
+ program$1 = program;
14
+ } else {
15
+ let definedTypes = eventConfig.definedTypes;
16
+ let tmp;
17
+ tmp = definedTypes === null ? undefined : JSON.stringify(definedTypes);
18
+ let program_name = eventConfig.contractName;
19
+ let program_programId = eventConfig.programId;
20
+ let program_instructions = [];
21
+ let program$2 = {
22
+ name: program_name,
23
+ programId: program_programId,
24
+ definedTypesJson: tmp,
25
+ instructions: program_instructions
26
+ };
27
+ programs.push(program$2);
28
+ program$1 = program$2;
29
+ }
30
+ let instruction = program$1.instructions.find(i => i.name === eventConfig.name);
31
+ let instruction$1;
32
+ if (instruction !== undefined) {
33
+ instruction$1 = instruction;
34
+ } else {
35
+ let args = eventConfig.args;
36
+ let tmp$1;
37
+ tmp$1 = args === null ? undefined : JSON.stringify(args);
38
+ let instruction_name = eventConfig.name;
39
+ let instruction_discriminator = eventConfig.discriminator;
40
+ let instruction_registrations = [];
41
+ let instruction$2 = {
42
+ name: instruction_name,
43
+ discriminator: instruction_discriminator,
44
+ argsJson: tmp$1,
45
+ registrations: instruction_registrations
46
+ };
47
+ program$1.instructions.push(instruction$2);
48
+ instruction$1 = instruction$2;
49
+ }
50
+ instruction$1.registrations.push({
16
51
  index: reg.index,
17
- instructionName: eventConfig.name,
18
- contractName: eventConfig.contractName,
19
- programId: eventConfig.programId,
20
52
  isWildcard: reg.isWildcard,
21
53
  startBlock: reg.startBlock,
22
- discriminator: eventConfig.discriminator,
23
- isInner: eventConfig.isInner,
24
- accountFilters: eventConfig.accountFilters.map(group => group.map(filter => ({
54
+ isInner: reg.isInner,
55
+ accountFilters: reg.accountFilters.map(group => group.map(filter => ({
25
56
  position: filter.position,
26
57
  values: filter.values
27
58
  }))),
@@ -29,26 +60,22 @@ function fromOnEventRegistrations(onEventRegistrations) {
29
60
  blockFields: Array.from(reg.fieldSelection.blockFields),
30
61
  accountActivityFields: Array.from(reg.fieldSelection.accountActivityFields),
31
62
  logFields: Array.from(reg.fieldSelection.logFields),
32
- instructionFields: Array.from(reg.fieldSelection.instructionFields),
33
- accounts: eventConfig.accounts,
34
- argsJson: tmp,
35
- definedTypesJson: tmp$1
36
- };
63
+ instructionFields: Array.from(reg.fieldSelection.instructionFields)
64
+ });
37
65
  });
66
+ return programs;
38
67
  }
39
68
 
40
69
  let Registration = {
41
70
  fromOnEventRegistrations: fromOnEventRegistrations
42
71
  };
43
72
 
44
- let QueryTypes = {};
45
-
46
73
  let ResponseTypes = {};
47
74
 
48
75
  let EventItems = {};
49
76
 
50
- function make(url, apiToken, httpReqTimeoutMillis, retryBaseMs, retryCeilingMs, eventRegistrationsOpt, addressStore) {
51
- let eventRegistrations = eventRegistrationsOpt !== undefined ? eventRegistrationsOpt : [];
77
+ function make(url, apiToken, httpReqTimeoutMillis, retryBaseMs, retryCeilingMs, programsOpt, addressStore) {
78
+ let programs = programsOpt !== undefined ? programsOpt : [];
52
79
  let envioVersion = Utils.EnvioPackage.value.version;
53
80
  return Core.getAddon().SvmHyperSyncClient.fromConfig({
54
81
  url: url,
@@ -56,12 +83,11 @@ function make(url, apiToken, httpReqTimeoutMillis, retryBaseMs, retryCeilingMs,
56
83
  httpReqTimeoutMillis: httpReqTimeoutMillis,
57
84
  retryBaseMs: retryBaseMs,
58
85
  retryCeilingMs: retryCeilingMs
59
- }, `hyperindex/` + envioVersion, eventRegistrations, addressStore);
86
+ }, `hyperindex/` + envioVersion, programs, addressStore);
60
87
  }
61
88
 
62
89
  export {
63
90
  Registration,
64
- QueryTypes,
65
91
  ResponseTypes,
66
92
  EventItems,
67
93
  make,
@@ -10,10 +10,9 @@ type options = {
10
10
  addressStore: AddressStore.t,
11
11
  }
12
12
 
13
- let namedAccounts = (
14
- ~idlNames: array<string>,
15
- ~accountArguments: array<string>,
16
- ): dict<Envio.svmInstructionAccount> => {
13
+ let namedAccounts = (~idlNames: array<string>, ~accountArguments: array<string>): dict<
14
+ Envio.svmInstructionAccount,
15
+ > => {
17
16
  let out = Dict.make()
18
17
  idlNames->Array.forEachWithIndex((name, i) =>
19
18
  switch accountArguments->Array.get(i) {
@@ -32,7 +31,10 @@ let namedAccounts = (
32
31
  out
33
32
  }
34
33
 
35
- let selectedLog = (log: SvmHyperSyncClient.EventItems.log, ~logFields: Utils.Set.t<string>): Envio.svmLog => {
34
+ let selectedLog = (
35
+ log: SvmHyperSyncClient.EventItems.log,
36
+ ~logFields: Utils.Set.t<string>,
37
+ ): Envio.svmLog => {
36
38
  let out = Dict.make()
37
39
  if logFields->Utils.Set.has("kind") {
38
40
  switch log.kind->Null.toOption {
@@ -63,9 +65,11 @@ let toSvmInstruction = (
63
65
  ~fieldSelection: Internal.fieldSelection,
64
66
  ): Envio.svmInstruction => {
65
67
  let hasSelection = name => fieldSelection.instructionFields->Utils.Set.has(name)
68
+ // A program-wide registration has no configured discriminator, so the whole
69
+ // data stands in, hex-encoded like a configured one would be.
66
70
  let discriminator = switch eventConfig.discriminator {
67
71
  | Some(d) => d
68
- | None => item.data
72
+ | None => "0x" ++ item.data->NodeJs.Buffer.fromUint8Array->NodeJs.Buffer.toHex
69
73
  }
70
74
  let out = Dict.make()
71
75
  out->setField("programName", programName)
@@ -83,8 +87,9 @@ let toSvmInstruction = (
83
87
  if hasSelection("isInner") {
84
88
  out->setField("isInner", item.isInner)
85
89
  }
86
- if hasSelection("args") {
87
- out->setField("args", item.argsJson->JSON.parseOrThrow)
90
+ switch item.args->Null.toOption {
91
+ | Some(args) => out->setField("args", args)
92
+ | None => ()
88
93
  }
89
94
  if hasSelection("accounts") {
90
95
  out->setField(
@@ -125,9 +130,7 @@ let make = (
125
130
  ~url=endpointUrl,
126
131
  ~apiToken?,
127
132
  ~httpReqTimeoutMillis=clientTimeoutMillis,
128
- ~eventRegistrations=SvmHyperSyncClient.Registration.fromOnEventRegistrations(
129
- onEventRegistrations,
130
- ),
133
+ ~programs=SvmHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations),
131
134
  ~addressStore,
132
135
  )
133
136
 
@@ -198,7 +201,7 @@ let make = (
198
201
  ~fieldSelection=onEventRegistration.fieldSelection,
199
202
  )
200
203
  Internal.Event({
201
- onEventRegistration,
204
+ onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
202
205
  chainId,
203
206
  blockNumber: item.slot,
204
207
  // A slot orders by `(transactionIndex, path)` — the
@@ -236,15 +239,15 @@ let make = (
236
239
 
237
240
  // Called through the client rather than passed as a value: the client is a
238
241
  // napi class, so a detached method reference loses the instance it belongs to.
239
- let getBlockHashes = HyperSync.makeGetBlockHashes(
240
- ~query=(~blockNumbers) => client.getBlockHashes(~blockNumbers),
242
+ let getBlockHashes = HyperSync.makeGetBlockHashes(~query=(~blockNumbers) =>
243
+ client.getBlockHashes(~blockNumbers)
241
244
  )
242
245
 
243
246
  {
244
247
  name,
245
248
  sourceFor: Sync,
246
249
  chainId,
247
- pollingInterval: 1000,
250
+ pollingInterval: HyperSync.pollingInterval,
248
251
  poweredByHyperSync: true,
249
252
  getBlockHashes,
250
253
  getHeightOrThrow: async () => {
@@ -48,7 +48,7 @@ function setField(out, name, value) {
48
48
 
49
49
  function toSvmInstruction(item, programName, instructionName, eventConfig, fieldSelection) {
50
50
  let d = eventConfig.discriminator;
51
- let discriminator = d !== undefined ? d : item.data;
51
+ let discriminator = d !== undefined ? d : "0x" + Buffer.from(item.data).toString("hex");
52
52
  let out = {};
53
53
  setField(out, "programName", programName);
54
54
  setField(out, "instructionName", instructionName);
@@ -65,8 +65,9 @@ function toSvmInstruction(item, programName, instructionName, eventConfig, field
65
65
  if (fieldSelection.instructionFields.has("isInner")) {
66
66
  setField(out, "isInner", item.isInner);
67
67
  }
68
- if (fieldSelection.instructionFields.has("args")) {
69
- setField(out, "args", JSON.parse(item.argsJson));
68
+ let args = item.args;
69
+ if (args !== null) {
70
+ setField(out, "args", args);
70
71
  }
71
72
  if (fieldSelection.instructionFields.has("accounts")) {
72
73
  setField(out, "accounts", namedAccounts(eventConfig.accounts, item.accounts));
@@ -163,7 +164,7 @@ function make(param) {
163
164
  sourceFor: "Sync",
164
165
  chainId: chainId,
165
166
  poweredByHyperSync: true,
166
- pollingInterval: 1000,
167
+ pollingInterval: HyperSync.pollingInterval,
167
168
  getBlockHashes: getBlockHashes,
168
169
  getHeightOrThrow: async () => {
169
170
  let timer = Performance.now();
package/svm.schema.json CHANGED
@@ -381,30 +381,12 @@
381
381
  "type": "string"
382
382
  },
383
383
  "discriminator": {
384
- "description": "Hex-encoded instruction-data prefix used as the discriminator (\"0x\" optional). Must be 1, 2, 4, or 8 bytes after decoding. An 8-byte value matches the standard Anchor discriminator.",
384
+ "description": "Hex-encoded instruction-data prefix used as the discriminator (\"0x\" optional), of any whole number of bytes; an 8-byte value matches the standard Anchor discriminator. Omit it to match every instruction of the program. Every instruction whose prefix an on-chain call carries receives it, so a program-wide entry fires alongside a keyed one, and two entries may share a prefix (say, the layouts before and after a program upgrade): each decodes with its own `args`, and one whose layout rejects the data is skipped for that call.",
385
385
  "type": [
386
386
  "string",
387
387
  "null"
388
388
  ]
389
389
  },
390
- "is_inner": {
391
- "description": "Filter on inner-vs-outer instructions. None / absent matches both.",
392
- "type": [
393
- "boolean",
394
- "null"
395
- ]
396
- },
397
- "account_filters": {
398
- "description": "Optional positional account filters. Two shapes are accepted: a flat list of `{position, values}` entries (AND across positions, OR within `values`); or `{any_of: [[...]] }`, a list of AND-groups that are OR-ed together. Positions must be in 0..=5; positions 6..=9 are reserved for a future extension.",
399
- "anyOf": [
400
- {
401
- "$ref": "#/$defs/AccountFilters"
402
- },
403
- {
404
- "type": "null"
405
- }
406
- ]
407
- },
408
390
  "accounts": {
409
391
  "description": "Optional positional account names. The Nth entry names account slot N on the dispatched instruction; surfaces as `instruction.accounts.<name>` when `fields.instruction` includes `accounts`.",
410
392
  "type": [
@@ -431,62 +413,6 @@
431
413
  "name"
432
414
  ]
433
415
  },
434
- "AccountFilters": {
435
- "anyOf": [
436
- {
437
- "type": "array",
438
- "items": {
439
- "$ref": "#/$defs/AccountFilter"
440
- }
441
- },
442
- {
443
- "$ref": "#/$defs/AnyOfAccountFilters"
444
- }
445
- ]
446
- },
447
- "AccountFilter": {
448
- "type": "object",
449
- "properties": {
450
- "position": {
451
- "description": "Account position within the instruction (0..=5).",
452
- "type": "integer",
453
- "format": "uint8",
454
- "minimum": 0,
455
- "maximum": 255
456
- },
457
- "values": {
458
- "description": "Allowed base58 pubkeys for this account position.",
459
- "type": "array",
460
- "items": {
461
- "type": "string"
462
- }
463
- }
464
- },
465
- "additionalProperties": false,
466
- "required": [
467
- "position",
468
- "values"
469
- ]
470
- },
471
- "AnyOfAccountFilters": {
472
- "type": "object",
473
- "properties": {
474
- "any_of": {
475
- "description": "A non-empty list of AND-groups. Each group is itself a non-empty list of `{position, values}` entries that must all match the same instruction. An instruction matches `any_of` when any one group matches.",
476
- "type": "array",
477
- "items": {
478
- "type": "array",
479
- "items": {
480
- "$ref": "#/$defs/AccountFilter"
481
- }
482
- }
483
- }
484
- },
485
- "additionalProperties": false,
486
- "required": [
487
- "any_of"
488
- ]
489
- },
490
416
  "ArgDef": {
491
417
  "description": "One named argument of an instruction. Mirrors\n`hypersync_client_solana::decode::NamedField`.",
492
418
  "type": "object",