envio 3.7.0-svm-alpha.1 → 3.8.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 (45) hide show
  1. package/index.d.ts +331 -142
  2. package/package.json +6 -6
  3. package/src/ChainState.res +9 -1
  4. package/src/ChainState.res.mjs +6 -1
  5. package/src/Config.res +19 -10
  6. package/src/Config.res.mjs +17 -7
  7. package/src/Core.res +4 -0
  8. package/src/Envio.res +54 -76
  9. package/src/EventConfigBuilder.res +159 -30
  10. package/src/EventConfigBuilder.res.mjs +120 -24
  11. package/src/HandlerRegister.res +20 -11
  12. package/src/HandlerRegister.res.mjs +22 -5
  13. package/src/Hasura.res +30 -22
  14. package/src/Hasura.res.mjs +11 -5
  15. package/src/InMemoryStore.res +1 -6
  16. package/src/InMemoryStore.res.mjs +1 -3
  17. package/src/Internal.res +33 -13
  18. package/src/Internal.res.mjs +12 -16
  19. package/src/Main.res +2 -5
  20. package/src/MemoryStorage.res +40 -8
  21. package/src/MemoryStorage.res.mjs +38 -16
  22. package/src/Persistence.res +4 -2
  23. package/src/PgStorage.res +6 -1
  24. package/src/PgStorage.res.mjs +1 -1
  25. package/src/SimulateItems.res +261 -9
  26. package/src/SimulateItems.res.mjs +218 -47
  27. package/src/TestIndexer.res +26 -10
  28. package/src/TestIndexer.res.mjs +34 -12
  29. package/src/bindings/ClickHouse.res +49 -17
  30. package/src/bindings/ClickHouse.res.mjs +35 -14
  31. package/src/db/InternalTable.res +11 -0
  32. package/src/db/InternalTable.res.mjs +7 -0
  33. package/src/sources/EvmHyperSyncSource.res +8 -3
  34. package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
  35. package/src/sources/SimulateSource.res +8 -4
  36. package/src/sources/SimulateSource.res.mjs +7 -3
  37. package/src/sources/Svm.res +34 -7
  38. package/src/sources/Svm.res.mjs +32 -4
  39. package/src/sources/SvmHyperSyncClient.res +10 -12
  40. package/src/sources/SvmHyperSyncClient.res.mjs +3 -2
  41. package/src/sources/SvmHyperSyncSource.res +96 -35
  42. package/src/sources/SvmHyperSyncSource.res.mjs +78 -38
  43. package/src/sources/TransactionStore.res +38 -0
  44. package/src/sources/TransactionStore.res.mjs +5 -0
  45. package/svm.schema.json +27 -78
@@ -273,6 +273,14 @@ function onClusterClause(onCluster) {
273
273
  }
274
274
  }
275
275
 
276
+ function replicatedTableSettingsClause(replicated) {
277
+ if (replicated) {
278
+ return "\nSETTINGS replicated_deduplication_window = 0";
279
+ } else {
280
+ return "";
281
+ }
282
+ }
283
+
276
284
  function databaseEngineName(engineSpec) {
277
285
  return engineSpec.trim().split("(")[0].split(" ")[0].trim();
278
286
  }
@@ -295,12 +303,15 @@ function makeCreateHistoryTableQuery(entityConfig, database, replicatedOpt, onCl
295
303
  let match = options !== undefined ? [
296
304
  options.partitionBy,
297
305
  options.orderBy,
298
- options.ttl
306
+ options.ttl,
307
+ options.skippingIndexes
299
308
  ] : [
309
+ undefined,
300
310
  undefined,
301
311
  undefined,
302
312
  undefined
303
313
  ];
314
+ let skippingIndexes = match[3];
304
315
  let ttl = match[2];
305
316
  let orderBy = match[1];
306
317
  let partitionBy = match[0];
@@ -336,6 +347,11 @@ function makeCreateHistoryTableQuery(entityConfig, database, replicatedOpt, onCl
336
347
  });
337
348
  let partitionByClause = partitionBy !== undefined ? `\nPARTITION BY ` + resolveExpressionColumns(partitionBy) : "";
338
349
  let ttlClause = ttl !== undefined ? `\nTTL ` + resolveExpressionColumns(ttl) : "";
350
+ let skippingIndexDefinitions = skippingIndexes !== undefined ? skippingIndexes.map(index => {
351
+ let granularity = index.granularity;
352
+ let granularityClause = granularity !== undefined ? ` GRANULARITY ` + granularity.toString() : "";
353
+ return `,\n INDEX \`` + index.name + `\` ` + resolveExpressionColumns(index.expr) + ` TYPE ` + index.type + granularityClause;
354
+ }).join("") : "";
339
355
  return `CREATE TABLE IF NOT EXISTS ` + database + `.\`` + EntityHistory.historyTableName(entityConfig.name, entityConfig.index) + `\`` + (
340
356
  onCluster ? ` ON CLUSTER '{cluster}'` : ""
341
357
  ) + ` (
@@ -344,10 +360,12 @@ function makeCreateHistoryTableQuery(entityConfig, database, replicatedOpt, onCl
344
360
  \`` + EntityHistory.changeFieldName + `\` ` + getClickHouseFieldType({
345
361
  type: "Enum",
346
362
  config: EntityHistory.RowAction.config
347
- }, false, false, undefined) + `
363
+ }, false, false, undefined) + skippingIndexDefinitions + `
348
364
  )
349
365
  ENGINE = ` + tableEngine + partitionByClause + `
350
- ORDER BY (` + orderByColumns + `)` + ttlClause;
366
+ ORDER BY (` + orderByColumns + `)` + ttlClause + (
367
+ replicated ? "\nSETTINGS replicated_deduplication_window = 0" : ""
368
+ );
351
369
  }
352
370
 
353
371
  function makeCreateCheckpointsTableQuery(database, replicatedOpt, onClusterOpt, chainIdModeOpt) {
@@ -365,7 +383,9 @@ function makeCreateCheckpointsTableQuery(database, replicatedOpt, onClusterOpt,
365
383
  \`` + "events_processed" + `\` ` + getClickHouseFieldType("UInt64", false, false, undefined) + `
366
384
  )
367
385
  ENGINE = ` + tableEngine + `
368
- ORDER BY (` + "id" + `)`;
386
+ ORDER BY (` + "id" + `)` + (
387
+ replicated ? "\nSETTINGS replicated_deduplication_window = 0" : ""
388
+ );
369
389
  }
370
390
 
371
391
  function makeCreateViewQuery(entityConfig, database, onClusterOpt) {
@@ -428,31 +448,31 @@ async function initialize(client, database, entities, param, chainIdModeOpt) {
428
448
  }
429
449
  }
430
450
  if (hasReplicatedDatabaseEngine) {
431
- await client.exec({
451
+ await client.command({
432
452
  query: `DROP DATABASE IF EXISTS ` + database + ` ON CLUSTER '{cluster}' SYNC`
433
453
  });
434
454
  } else {
435
- await client.exec({
455
+ await client.command({
436
456
  query: `TRUNCATE DATABASE IF EXISTS ` + database + (
437
457
  ddlOnCluster ? ` ON CLUSTER '{cluster}'` : ""
438
458
  )
439
459
  });
440
460
  }
441
- await client.exec({
461
+ await client.command({
442
462
  query: `CREATE DATABASE IF NOT EXISTS ` + database + databaseOnClusterClause + databaseEngineClause
443
463
  });
444
- await Promise.all(entities.map(entityConfig => client.exec({
464
+ await Promise.all(entities.map(entityConfig => client.command({
445
465
  query: makeCreateHistoryTableQuery(entityConfig, database, replicated, ddlOnCluster, chainIdMode)
446
466
  })));
447
- await client.exec({
467
+ await client.command({
448
468
  query: makeCreateCheckpointsTableQuery(database, replicated, ddlOnCluster, chainIdMode)
449
469
  });
450
470
  if (hasReplicatedDatabaseEngine) {
451
- await client.exec({
471
+ await client.command({
452
472
  query: `SYSTEM SYNC DATABASE REPLICA ON CLUSTER '{cluster}' ` + database
453
473
  });
454
474
  }
455
- await Promise.all(entities.map(entityConfig => client.exec({
475
+ await Promise.all(entities.map(entityConfig => client.command({
456
476
  query: makeCreateViewQuery(entityConfig, database, ddlOnCluster)
457
477
  })));
458
478
  return Logging.trace("ClickHouse storage initialization completed successfully");
@@ -466,7 +486,7 @@ async function initialize(client, database, entities, param, chainIdModeOpt) {
466
486
  async function resume(client, database, checkpointId) {
467
487
  try {
468
488
  try {
469
- await client.exec({
489
+ await client.command({
470
490
  query: `USE ` + database
471
491
  });
472
492
  } catch (raw_exn) {
@@ -480,11 +500,11 @@ async function resume(client, database, checkpointId) {
480
500
  let tables = (await tablesResult.json()).data;
481
501
  await Promise.all(tables.map(table => {
482
502
  let tableName = table.name;
483
- return client.exec({
503
+ return client.command({
484
504
  query: `ALTER TABLE ` + database + `.\`` + tableName + `\` DELETE WHERE \`` + EntityHistory.checkpointIdFieldName + `\` > ` + checkpointId.toString()
485
505
  });
486
506
  }));
487
- return await client.exec({
507
+ return await client.command({
488
508
  query: `DELETE FROM ` + database + `.\`` + InternalTable.Checkpoints.table.tableName + `\` WHERE \`` + Table.idFieldName + `\` > ` + checkpointId.toString()
489
509
  });
490
510
  } catch (raw_exn$1) {
@@ -505,6 +525,7 @@ export {
505
525
  setCheckpointsOrThrow,
506
526
  setUpdatesOrThrow,
507
527
  onClusterClause,
528
+ replicatedTableSettingsClause,
508
529
  databaseEngineName,
509
530
  makeCreateHistoryTableQuery,
510
531
  makeCreateCheckpointsTableQuery,
@@ -17,6 +17,7 @@ module Chains = {
17
17
  type field = [
18
18
  | progressFields
19
19
  | #id
20
+ | #ecosystem
20
21
  | #start_block
21
22
  | #end_block
22
23
  | #max_reorg_depth
@@ -29,6 +30,7 @@ module Chains = {
29
30
 
30
31
  let fields: array<field> = [
31
32
  #id,
33
+ #ecosystem,
32
34
  #start_block,
33
35
  #end_block,
34
36
  #max_reorg_depth,
@@ -57,6 +59,7 @@ module Chains = {
57
59
 
58
60
  type t = {
59
61
  @as("id") id: ChainId.t,
62
+ @as("ecosystem") ecosystem: string,
60
63
  @as("start_block") startBlock: int,
61
64
  @as("end_block") endBlock: Null.t<int>,
62
65
  @as("max_reorg_depth") maxReorgDepth: int,
@@ -70,6 +73,10 @@ module Chains = {
70
73
  "envio_chains",
71
74
  ~fields=[
72
75
  mkField((#id: field :> string), ChainId, ~fieldSchema=ChainId.schema, ~isPrimaryKey),
76
+ // Which ecosystem the chain belongs to (evm / fuel / svm). Chain ids are
77
+ // only unique within an ecosystem (HOS-1880: Svm ids are Envio-assigned),
78
+ // so consumers should treat (ecosystem, id) as the chain's identity.
79
+ mkField((#ecosystem: field :> string), String, ~fieldSchema=S.string),
73
80
  // Values populated from config
74
81
  mkField((#start_block: field :> string), Int32, ~fieldSchema=S.int),
75
82
  mkField((#end_block: field :> string), Int32, ~fieldSchema=S.null(S.int), ~isNullable),
@@ -104,6 +111,7 @@ module Chains = {
104
111
  let initialFromConfig = (chainConfig: Config.chain) => {
105
112
  {
106
113
  id: chainConfig.id,
114
+ ecosystem: (chainConfig.ecosystem: Ecosystem.name :> string),
107
115
  startBlock: chainConfig.startBlock,
108
116
  endBlock: chainConfig.endBlock->Null.fromOption,
109
117
  maxReorgDepth: chainConfig.maxReorgDepth,
@@ -131,6 +139,7 @@ module Chains = {
131
139
  let value = initialValues->(Utils.magic: t => dict<unknown>)->Dict.get((field :> string))
132
140
  switch typeof(value) {
133
141
  | #object => "NULL"
142
+ | #string => `'${value->(Utils.magic: option<unknown> => string)}'`
134
143
  | #number => value->(Utils.magic: option<unknown> => int)->Int.toString
135
144
  | #bigint => value->(Utils.magic: option<unknown> => bigint)->BigInt.toString
136
145
  | #boolean => value->(Utils.magic: option<unknown> => bool) ? "true" : "false"
@@ -648,6 +657,7 @@ module Views = {
648
657
  `CREATE VIEW "${pgSchema}"."${metaViewName}" AS
649
658
  SELECT
650
659
  "${(#id: Chains.field :> string)}" AS "chainId",
660
+ "${(#ecosystem: Chains.field :> string)}" AS "ecosystem",
651
661
  "${(#start_block: Chains.field :> string)}" AS "startBlock",
652
662
  "${(#end_block: Chains.field :> string)}" AS "endBlock",
653
663
  "${(#progress_block: Chains.field :> string)}" AS "progressBlock",
@@ -666,6 +676,7 @@ ORDER BY "${(#id: Chains.field :> string)}";`
666
676
  SELECT
667
677
  "${(#source_block: Chains.field :> string)}" AS "block_height",
668
678
  "${(#id: Chains.field :> string)}" AS "chain_id",
679
+ "${(#ecosystem: Chains.field :> string)}" AS "ecosystem",
669
680
  "${(#end_block: Chains.field :> string)}" AS "end_block",
670
681
  "${(#first_event_block: Chains.field :> string)}" AS "first_event_block_number",
671
682
  "${(#_is_hyper_sync: Chains.field :> string)}" AS "is_hyper_sync",
@@ -13,6 +13,7 @@ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_excep
13
13
 
14
14
  let fields = [
15
15
  "id",
16
+ "ecosystem",
16
17
  "start_block",
17
18
  "end_block",
18
19
  "max_reorg_depth",
@@ -27,6 +28,7 @@ let fields = [
27
28
 
28
29
  let table = Table.mkTable("envio_chains", undefined, [
29
30
  Table.mkField("id", "ChainId", ChainId.schema, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined),
31
+ Table.mkField("ecosystem", "String", S$RescriptSchema.string, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
30
32
  Table.mkField("start_block", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
31
33
  Table.mkField("end_block", "Int32", S$RescriptSchema.$$null(S$RescriptSchema.int), undefined, undefined, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
32
34
  Table.mkField("max_reorg_depth", "Int32", S$RescriptSchema.int, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined),
@@ -42,6 +44,7 @@ let table = Table.mkTable("envio_chains", undefined, [
42
44
  function initialFromConfig(chainConfig) {
43
45
  return {
44
46
  id: chainConfig.id,
47
+ ecosystem: chainConfig.ecosystem,
45
48
  start_block: chainConfig.startBlock,
46
49
  end_block: Stdlib_Null.fromOption(chainConfig.endBlock),
47
50
  max_reorg_depth: chainConfig.maxReorgDepth,
@@ -73,6 +76,8 @@ function makeInitialValuesQuery(pgSchema, chainConfigs) {
73
76
  } else {
74
77
  return "false";
75
78
  }
79
+ } else if (match === "string") {
80
+ return `'` + value + `'`;
76
81
  } else if (match === "object") {
77
82
  return "NULL";
78
83
  } else {
@@ -440,6 +445,7 @@ function makeMetaViewQuery(pgSchema) {
440
445
  return `CREATE VIEW "` + pgSchema + `"."` + metaViewName + `" AS
441
446
  SELECT
442
447
  "` + "id" + `" AS "chainId",
448
+ "` + "ecosystem" + `" AS "ecosystem",
443
449
  "` + "start_block" + `" AS "startBlock",
444
450
  "` + "end_block" + `" AS "endBlock",
445
451
  "` + "progress_block" + `" AS "progressBlock",
@@ -458,6 +464,7 @@ function makeChainMetadataViewQuery(pgSchema) {
458
464
  SELECT
459
465
  "` + "source_block" + `" AS "block_height",
460
466
  "` + "id" + `" AS "chain_id",
467
+ "` + "ecosystem" + `" AS "ecosystem",
461
468
  "` + "end_block" + `" AS "end_block",
462
469
  "` + "first_event_block" + `" AS "first_event_block_number",
463
470
  "` + "_is_hyper_sync" + `" AS "is_hyper_sync",
@@ -1,7 +1,8 @@
1
1
  open Source
2
2
 
3
- // Surfaced by HyperSyncClient.getHeight (Rust) when HyperSync rejects the API
4
- // token. The corrupted-token test feeds the real server error through this
3
+ // Surfaced by the HyperSync client (Rust) when HyperSync rejects the API
4
+ // token. The corrupted-token test feeds the real server error (from the query
5
+ // endpoint; the edge no longer 401s malformed tokens on /height) through this
5
6
  // check so it can't silently drift away from what getHeightOrThrow guards on.
6
7
  let isUnauthorizedError = (message: string) => message->String.includes("401 Unauthorized")
7
8
 
@@ -194,7 +195,11 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
194
195
  }
195
196
  }
196
197
 
197
- let getBlockHashes = HyperSync.makeGetBlockHashes(~query=client.getBlockHashes)
198
+ // Called through the client rather than passed as a value: the client is a
199
+ // napi class, so a detached method reference loses the instance it belongs to.
200
+ let getBlockHashes = HyperSync.makeGetBlockHashes(
201
+ ~query=(~blockNumbers) => client.getBlockHashes(~blockNumbers),
202
+ )
198
203
 
199
204
  {
200
205
  name,
@@ -137,7 +137,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
137
137
  requestStats: requestStats
138
138
  };
139
139
  };
140
- let getBlockHashes = HyperSync.makeGetBlockHashes(client.getBlockHashes);
140
+ let getBlockHashes = HyperSync.makeGetBlockHashes(blockNumbers => client.getBlockHashes(blockNumbers));
141
141
  return {
142
142
  name: "HyperSync",
143
143
  sourceFor: "Sync",
@@ -4,6 +4,8 @@ let make = (
4
4
  ~chainId: ChainId.t,
5
5
  ~addressStore: AddressStore.t,
6
6
  ~ecosystem: Ecosystem.name=Evm,
7
+ ~transactionStore: option<TransactionStore.t>=None,
8
+ ~blockStore: option<BlockStore.t>=None,
7
9
  ): Source.t => {
8
10
  let reportedHeight = max(endBlock, 1)
9
11
 
@@ -106,10 +108,12 @@ let make = (
106
108
  Promise.resolve({
107
109
  Source.knownHeight: reportedHeight,
108
110
  parsedQueueItems,
109
- // Simulate keeps the transaction and block inline on the payload; no
110
- // transaction page and an empty block page (nothing to reorg-check).
111
- transactionStore: None,
112
- blockStore: BlockStore.fromJs([], ~ecosystem, ~shouldChecksum=false),
111
+ // EVM/Fuel keep transaction and block inline. SVM simulate returns a
112
+ // store page so materialize + activity attach run the same path as HyperSync.
113
+ transactionStore,
114
+ blockStore: blockStore->Option.getOr(
115
+ BlockStore.fromJs([], ~ecosystem, ~shouldChecksum=false),
116
+ ),
113
117
  fromBlockQueried: fromBlock,
114
118
  latestFetchedBlockNumber: toBlockQueried,
115
119
  stats: {
@@ -2,9 +2,13 @@
2
2
 
3
3
  import * as BlockStore from "./BlockStore.res.mjs";
4
4
  import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
5
7
 
6
- function make(items, endBlock, chainId, addressStore, ecosystemOpt) {
8
+ function make(items, endBlock, chainId, addressStore, ecosystemOpt, transactionStoreOpt, blockStoreOpt) {
7
9
  let ecosystem = ecosystemOpt !== undefined ? ecosystemOpt : "evm";
10
+ let transactionStore = transactionStoreOpt !== undefined ? Primitive_option.valFromOption(transactionStoreOpt) : undefined;
11
+ let blockStore = blockStoreOpt !== undefined ? Primitive_option.valFromOption(blockStoreOpt) : undefined;
8
12
  let reportedHeight = Primitive_int.max(endBlock, 1);
9
13
  return {
10
14
  name: "SimulateSource",
@@ -75,8 +79,8 @@ function make(items, endBlock, chainId, addressStore, ecosystemOpt) {
75
79
  return Promise.resolve({
76
80
  knownHeight: reportedHeight,
77
81
  parsedQueueItems: parsedQueueItems,
78
- transactionStore: undefined,
79
- blockStore: BlockStore.fromJs([], ecosystem, false),
82
+ transactionStore: transactionStore,
83
+ blockStore: Stdlib_Option.getOr(blockStore, BlockStore.fromJs([], ecosystem, false)),
80
84
  fromBlockQueried: fromBlock,
81
85
  latestFetchedBlockNumber: toBlockQueried,
82
86
  stats: {
@@ -14,13 +14,38 @@ let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
14
14
  // Rust store (`SvmBlockField`) — keep this order in sync.
15
15
  let blockFields = ["slot", "time", "hash", "height", "parentSlot", "parentHash"]
16
16
 
17
- // `slot`/`time`/`hash` are always included; every other block field is opt-in
18
- // via `field_selection.block_fields`. All are materialised from the store.
17
+ // `slot` is always included (the item's key); every other block field is
18
+ // opt-in via handler `fields.block`. All are materialised from the store.
19
19
  //
20
20
  // One instruction's selected block fields → store selection bitmask. Computed per
21
21
  // event at config build and cached on the event config.
22
22
  let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields)
23
23
 
24
+ let attachAccountActivities = (payload: Internal.eventPayload, tx: Internal.eventTransaction) => {
25
+ let instruction = payload->(Utils.magic: Internal.eventPayload => dict<unknown>)
26
+ let transaction = tx->(Utils.magic: Internal.eventTransaction => dict<unknown>)
27
+ switch (instruction->Dict.get("accounts"), transaction->Dict.get("accountActivities")) {
28
+ | (Some(accounts), Some(activities)) if !(accounts->Array.isArray) =>
29
+ let byAddress = Dict.make()
30
+ (activities->(Utils.magic: unknown => array<Envio.svmAccountActivity>))->Array.forEach(
31
+ activity => byAddress->Dict.set(activity.address->SvmTypes.Pubkey.toString, activity),
32
+ )
33
+ (accounts->(Utils.magic: unknown => dict<Envio.svmInstructionAccount>))
34
+ ->Dict.valuesToArray
35
+ ->Array.forEach(account =>
36
+ switch byAddress->Dict.get(account.address->SvmTypes.Pubkey.toString) {
37
+ | Some(activity) =>
38
+ (account->(Utils.magic: Envio.svmInstructionAccount => dict<unknown>))->Dict.set(
39
+ "activity",
40
+ activity->(Utils.magic: Envio.svmAccountActivity => unknown),
41
+ )
42
+ | None => ()
43
+ }
44
+ )
45
+ | _ => ()
46
+ }
47
+ }
48
+
24
49
  let make = (~logger: Pino.t): Ecosystem.t => {
25
50
  name: Svm,
26
51
  blockNumberName: "height",
@@ -36,16 +61,18 @@ let make = (~logger: Pino.t): Ecosystem.t => {
36
61
  logger,
37
62
  toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
38
63
  toEventLogger: eventItem => {
39
- let instruction =
40
- eventItem.payload->(Utils.magic: Internal.eventPayload => Envio.svmInstruction)
64
+ let eventConfig =
65
+ eventItem.onEventRegistration.eventConfig->(
66
+ Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig
67
+ )
41
68
  Logging.createChildFrom(
42
69
  ~logger,
43
70
  ~params={
44
- "program": eventItem.onEventRegistration.eventConfig.contractName,
45
- "instruction": eventItem.onEventRegistration.eventConfig.name,
71
+ "program": eventConfig.contractName,
72
+ "instruction": eventConfig.name,
46
73
  "chainId": eventItem.chainId,
47
74
  "slot": eventItem.blockNumber,
48
- "programId": instruction.programId,
75
+ "programId": eventConfig.programId,
49
76
  },
50
77
  )
51
78
  },
@@ -9,6 +9,7 @@ import * as Internal from "../Internal.res.mjs";
9
9
  import * as BlockStore from "./BlockStore.res.mjs";
10
10
  import * as Performance from "../bindings/Performance.res.mjs";
11
11
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
12
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
12
13
  import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
13
14
  import * as TransactionStore from "./TransactionStore.res.mjs";
14
15
 
@@ -25,6 +26,32 @@ let blockFields = [
25
26
 
26
27
  let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields);
27
28
 
29
+ function attachAccountActivities(payload, tx) {
30
+ let match = payload["accounts"];
31
+ let match$1 = tx["accountActivities"];
32
+ if (match === undefined) {
33
+ return;
34
+ }
35
+ if (match$1 === undefined) {
36
+ return;
37
+ }
38
+ let accounts = Primitive_option.valFromOption(match);
39
+ if (Array.isArray(accounts)) {
40
+ return;
41
+ }
42
+ let byAddress = {};
43
+ Primitive_option.valFromOption(match$1).forEach(activity => {
44
+ byAddress[activity.address] = activity;
45
+ });
46
+ Object.values(accounts).forEach(account => {
47
+ let activity = byAddress[account.address];
48
+ if (activity !== undefined) {
49
+ account["activity"] = activity;
50
+ return;
51
+ }
52
+ });
53
+ }
54
+
28
55
  function make(logger) {
29
56
  return {
30
57
  name: "svm",
@@ -37,13 +64,13 @@ function make(logger) {
37
64
  logger: logger,
38
65
  toEvent: eventItem => eventItem.payload,
39
66
  toEventLogger: eventItem => {
40
- let instruction = eventItem.payload;
67
+ let eventConfig = eventItem.onEventRegistration.eventConfig;
41
68
  return Logging.createChildFrom(logger, {
42
- program: eventItem.onEventRegistration.eventConfig.contractName,
43
- instruction: eventItem.onEventRegistration.eventConfig.name,
69
+ program: eventConfig.contractName,
70
+ instruction: eventConfig.name,
44
71
  chainId: eventItem.chainId,
45
72
  slot: eventItem.blockNumber,
46
- programId: instruction.programId
73
+ programId: eventConfig.programId
47
74
  });
48
75
  },
49
76
  toRawEvent: param => Stdlib_JsError.throwWithMessage("Raw events are not supported for SVM")
@@ -96,6 +123,7 @@ export {
96
123
  eventTransactionFieldMask,
97
124
  blockFields,
98
125
  eventBlockFieldMask,
126
+ attachAccountActivities,
99
127
  make,
100
128
  GetFinalizedSlot,
101
129
  makeRPCSource,
@@ -27,14 +27,15 @@ module Registration = {
27
27
  // Earliest slot this registration accepts; `None` is unrestricted.
28
28
  startBlock: option<int>,
29
29
  discriminator?: string,
30
- discriminatorByteLen: int,
31
30
  isInner?: bool,
32
- includeLogs: bool,
33
31
  // DNF: outer array is OR of AND-groups.
34
32
  accountFilters: array<array<accountFilter>>,
35
33
  // camelCase Internal.svmTransactionField / svmBlockField names.
36
34
  transactionFields: array<string>,
37
35
  blockFields: array<string>,
36
+ accountActivityFields: array<string>,
37
+ logFields: array<string>,
38
+ instructionFields: array<string>,
38
39
  // Borsh schema pieces; empty accounts + absent argsJson = no schema.
39
40
  accounts: array<string>,
40
41
  argsJson?: string,
@@ -55,9 +56,7 @@ module Registration = {
55
56
  isWildcard: reg.isWildcard,
56
57
  startBlock: reg.startBlock,
57
58
  discriminator: ?eventConfig.discriminator,
58
- discriminatorByteLen: eventConfig.discriminatorByteLen,
59
59
  isInner: ?eventConfig.isInner,
60
- includeLogs: eventConfig.includeLogs,
61
60
  accountFilters: eventConfig.accountFilters->Array.map(group =>
62
61
  group->Array.map(
63
62
  (filter): accountFilter => {
@@ -68,6 +67,9 @@ module Registration = {
68
67
  ),
69
68
  transactionFields: reg.fieldSelection.transactionFields->Utils.Set.toArray,
70
69
  blockFields: reg.fieldSelection.blockFields->Utils.Set.toArray,
70
+ accountActivityFields: reg.fieldSelection.accountActivityFields->Utils.Set.toArray,
71
+ logFields: reg.fieldSelection.logFields->Utils.Set.toArray,
72
+ instructionFields: reg.fieldSelection.instructionFields->Utils.Set.toArray,
71
73
  accounts: eventConfig.accounts,
72
74
  argsJson: ?switch eventConfig.args {
73
75
  | JSON.Null => None
@@ -205,8 +207,8 @@ module EventItems = {
205
207
  }
206
208
 
207
209
  type log = {
208
- kind: string,
209
- message: string,
210
+ kind?: string,
211
+ message?: string,
210
212
  }
211
213
 
212
214
  // One routed instruction; `block` and `transaction` are materialised from
@@ -215,17 +217,13 @@ module EventItems = {
215
217
  onEventRegistrationIndex: int,
216
218
  slot: int,
217
219
  transactionIndex: int,
218
- instructionAddress: array<int>,
220
+ path: array<int>,
219
221
  programId: string,
220
222
  accounts: array<string>,
221
223
  data: string,
222
- d1?: string,
223
- d2?: string,
224
- d4?: string,
225
- d8?: string,
226
224
  isInner: bool,
227
225
  decoded?: ResponseTypes.decodedInstruction,
228
- // Present only when the routed registration opted in via `includeLogs`.
226
+ // Present only when the routed registration selected `fields.log`.
229
227
  logs?: array<log>,
230
228
  }
231
229
 
@@ -20,15 +20,16 @@ function fromOnEventRegistrations(onEventRegistrations) {
20
20
  isWildcard: reg.isWildcard,
21
21
  startBlock: reg.startBlock,
22
22
  discriminator: eventConfig.discriminator,
23
- discriminatorByteLen: eventConfig.discriminatorByteLen,
24
23
  isInner: eventConfig.isInner,
25
- includeLogs: eventConfig.includeLogs,
26
24
  accountFilters: eventConfig.accountFilters.map(group => group.map(filter => ({
27
25
  position: filter.position,
28
26
  values: filter.values
29
27
  }))),
30
28
  transactionFields: Array.from(reg.fieldSelection.transactionFields),
31
29
  blockFields: Array.from(reg.fieldSelection.blockFields),
30
+ accountActivityFields: Array.from(reg.fieldSelection.accountActivityFields),
31
+ logFields: Array.from(reg.fieldSelection.logFields),
32
+ instructionFields: Array.from(reg.fieldSelection.instructionFields),
32
33
  accounts: eventConfig.accounts,
33
34
  argsJson: tmp,
34
35
  definedTypesJson: tmp$1