envio 3.3.0-alpha.1 → 3.3.0-alpha.10

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 (136) hide show
  1. package/evm.schema.json +10 -0
  2. package/index.d.ts +45 -25
  3. package/package.json +7 -7
  4. package/src/Address.res +5 -2
  5. package/src/Address.res.mjs +3 -1
  6. package/src/Api.res +1 -1
  7. package/src/Api.res.mjs +1 -1
  8. package/src/Batch.res +0 -6
  9. package/src/Batch.res.mjs +1 -12
  10. package/src/BatchProcessing.res +7 -12
  11. package/src/BatchProcessing.res.mjs +8 -7
  12. package/src/Bin.res +3 -0
  13. package/src/Bin.res.mjs +4 -0
  14. package/src/ChainFetching.res +43 -9
  15. package/src/ChainFetching.res.mjs +34 -15
  16. package/src/ChainMetadata.res +1 -11
  17. package/src/ChainMetadata.res.mjs +1 -10
  18. package/src/ChainState.res +605 -151
  19. package/src/ChainState.res.mjs +427 -104
  20. package/src/ChainState.resi +72 -4
  21. package/src/Config.res +61 -33
  22. package/src/Config.res.mjs +50 -10
  23. package/src/ContractRegisterContext.res +2 -12
  24. package/src/ContractRegisterContext.res.mjs +3 -5
  25. package/src/Core.res +14 -3
  26. package/src/CrossChainState.res +113 -89
  27. package/src/CrossChainState.res.mjs +64 -60
  28. package/src/CrossChainState.resi +1 -1
  29. package/src/Ecosystem.res +5 -6
  30. package/src/Ecosystem.res.mjs +3 -3
  31. package/src/Envio.res +17 -11
  32. package/src/EnvioGlobal.res +53 -0
  33. package/src/EnvioGlobal.res.mjs +31 -0
  34. package/src/EventConfigBuilder.res +177 -88
  35. package/src/EventConfigBuilder.res.mjs +99 -42
  36. package/src/EventProcessing.res +74 -28
  37. package/src/EventProcessing.res.mjs +55 -29
  38. package/src/ExitOnCaughtUp.res +10 -2
  39. package/src/ExitOnCaughtUp.res.mjs +10 -1
  40. package/src/FetchState.res +544 -356
  41. package/src/FetchState.res.mjs +412 -471
  42. package/src/HandlerLoader.res +8 -104
  43. package/src/HandlerLoader.res.mjs +2 -81
  44. package/src/HandlerRegister.res +518 -144
  45. package/src/HandlerRegister.res.mjs +285 -133
  46. package/src/HandlerRegister.resi +24 -8
  47. package/src/IndexerState.res +10 -3
  48. package/src/IndexerState.res.mjs +11 -4
  49. package/src/IndexerState.resi +2 -1
  50. package/src/IndexingAddresses.res +108 -0
  51. package/src/IndexingAddresses.res.mjs +101 -0
  52. package/src/IndexingAddresses.resi +34 -0
  53. package/src/Internal.res +155 -46
  54. package/src/Internal.res.mjs +30 -2
  55. package/src/LoadLayer.res +5 -5
  56. package/src/LoadLayer.res.mjs +6 -6
  57. package/src/LogSelection.res +102 -165
  58. package/src/LogSelection.res.mjs +101 -116
  59. package/src/Main.res +71 -236
  60. package/src/Main.res.mjs +60 -145
  61. package/src/Metrics.res +74 -0
  62. package/src/Metrics.res.mjs +75 -0
  63. package/src/PgStorage.res +4 -4
  64. package/src/PgStorage.res.mjs +5 -5
  65. package/src/Prometheus.res +10 -63
  66. package/src/Prometheus.res.mjs +124 -171
  67. package/src/PruneStaleHistory.res +2 -2
  68. package/src/PruneStaleHistory.res.mjs +3 -3
  69. package/src/RawEvent.res +7 -2
  70. package/src/RawEvent.res.mjs +4 -4
  71. package/src/Rollback.res +3 -3
  72. package/src/Rollback.res.mjs +4 -4
  73. package/src/RollbackCommit.res +4 -1
  74. package/src/RollbackCommit.res.mjs +3 -2
  75. package/src/SimulateDeadInputTracker.res +85 -0
  76. package/src/SimulateDeadInputTracker.res.mjs +73 -0
  77. package/src/SimulateDeadInputTracker.resi +12 -0
  78. package/src/SimulateItems.res +91 -23
  79. package/src/SimulateItems.res.mjs +55 -32
  80. package/src/TestIndexer.res +54 -40
  81. package/src/TestIndexer.res.mjs +37 -30
  82. package/src/bindings/Performance.res +7 -0
  83. package/src/bindings/Performance.res.mjs +21 -0
  84. package/src/bindings/Performance.resi +7 -0
  85. package/src/bindings/Viem.res +0 -41
  86. package/src/bindings/Viem.res.mjs +1 -43
  87. package/src/sources/BlockStore.res +46 -0
  88. package/src/sources/BlockStore.res.mjs +24 -0
  89. package/src/sources/EventRouter.res +36 -33
  90. package/src/sources/EventRouter.res.mjs +13 -14
  91. package/src/sources/Evm.res +83 -72
  92. package/src/sources/Evm.res.mjs +53 -69
  93. package/src/sources/EvmChain.res +18 -20
  94. package/src/sources/EvmChain.res.mjs +15 -15
  95. package/src/sources/EvmRpcClient.res +74 -5
  96. package/src/sources/EvmRpcClient.res.mjs +13 -4
  97. package/src/sources/FieldMask.res +39 -0
  98. package/src/sources/FieldMask.res.mjs +42 -0
  99. package/src/sources/Fuel.res +9 -6
  100. package/src/sources/Fuel.res.mjs +5 -10
  101. package/src/sources/HyperFuelSource.res +46 -50
  102. package/src/sources/HyperFuelSource.res.mjs +64 -54
  103. package/src/sources/HyperSync.res +39 -7
  104. package/src/sources/HyperSync.res.mjs +57 -37
  105. package/src/sources/HyperSync.resi +12 -2
  106. package/src/sources/HyperSyncClient.res +22 -111
  107. package/src/sources/HyperSyncClient.res.mjs +2 -30
  108. package/src/sources/HyperSyncHeightStream.res +1 -8
  109. package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
  110. package/src/sources/HyperSyncSource.res +94 -107
  111. package/src/sources/HyperSyncSource.res.mjs +82 -86
  112. package/src/sources/Rpc.res +15 -47
  113. package/src/sources/Rpc.res.mjs +25 -56
  114. package/src/sources/RpcSource.res +320 -467
  115. package/src/sources/RpcSource.res.mjs +268 -385
  116. package/src/sources/RpcWebSocketHeightStream.res +0 -5
  117. package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
  118. package/src/sources/SimulateSource.res +10 -4
  119. package/src/sources/SimulateSource.res.mjs +16 -6
  120. package/src/sources/Source.res +34 -6
  121. package/src/sources/SourceManager.res +77 -19
  122. package/src/sources/SourceManager.res.mjs +63 -21
  123. package/src/sources/SourceManager.resi +10 -0
  124. package/src/sources/Svm.res +31 -16
  125. package/src/sources/Svm.res.mjs +36 -13
  126. package/src/sources/SvmHyperSyncClient.res +5 -32
  127. package/src/sources/SvmHyperSyncSource.res +159 -134
  128. package/src/sources/SvmHyperSyncSource.res.mjs +164 -129
  129. package/src/sources/TransactionStore.res +49 -0
  130. package/src/sources/TransactionStore.res.mjs +30 -0
  131. package/src/tui/Tui.res +13 -16
  132. package/src/tui/Tui.res.mjs +12 -14
  133. package/svm.schema.json +49 -37
  134. package/src/bindings/Hrtime.res +0 -58
  135. package/src/bindings/Hrtime.res.mjs +0 -90
  136. package/src/bindings/Hrtime.resi +0 -30
@@ -8,6 +8,7 @@ let configAddresses: Config.chain => array<Internal.indexingAddress>
8
8
  let make: (
9
9
  ~chainConfig: Config.chain,
10
10
  ~fetchState: FetchState.t,
11
+ ~indexingAddresses: IndexingAddresses.t,
11
12
  ~sourceManager: SourceManager.t,
12
13
  ~reorgDetection: ReorgDetection.t,
13
14
  ~committedProgressBlockNumber: int,
@@ -15,13 +16,16 @@ let make: (
15
16
  ~numEventsProcessed: float=?,
16
17
  ~timestampCaughtUpToHeadOrEndblock: option<Date.t>=?,
17
18
  ~isProgressAtHead: bool=?,
19
+ ~transactionStore: TransactionStore.t=?,
20
+ ~chainDensity: option<float>=?,
21
+ ~blockStore: BlockStore.t=?,
18
22
  ~logger: Pino.t,
19
23
  ) => t
20
24
 
21
25
  let makeFromConfig: (
22
26
  Config.chain,
23
27
  ~config: Config.t,
24
- ~registrations: HandlerRegister.registrations,
28
+ ~registrationsByChainId: HandlerRegister.registrationsByChainId,
25
29
  ~knownHeight: int,
26
30
  ) => t
27
31
 
@@ -32,13 +36,12 @@ let makeFromDbState: (
32
36
  ~isInReorgThreshold: bool,
33
37
  ~isRealtime: bool,
34
38
  ~config: Config.t,
35
- ~registrations: HandlerRegister.registrations,
39
+ ~registrationsByChainId: HandlerRegister.registrationsByChainId,
36
40
  ~reducedPollingInterval: int=?,
37
41
  ) => t
38
42
 
39
43
  // Accessors.
40
44
  let logger: t => Pino.t
41
- let fetchState: t => FetchState.t
42
45
  let sourceManager: t => SourceManager.t
43
46
  let chainConfig: t => Config.chain
44
47
  let reorgDetection: t => ReorgDetection.t
@@ -50,12 +53,64 @@ let pendingBudget: t => float
50
53
  let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
51
54
  let timestampCaughtUpToHeadOrEndblock: t => option<Date.t>
52
55
 
56
+ // Fetch-frontier reads.
57
+ let knownHeight: t => int
58
+ let contractAddresses: (t, ~contractName: string) => array<Address.t>
59
+ let bufferSize: t => int
60
+ let bufferReadyCount: t => int
61
+ let getProgressPercentage: t => float
62
+ let chainDensity: t => option<float>
63
+ let effectiveDensity: t => option<float>
64
+ let hasReadyItem: t => bool
65
+ let isReadyToEnterReorgThreshold: t => bool
66
+
67
+ // Fetch control.
68
+ let targetBlock: (t, ~chainTargetItems: float) => int
69
+ let progressAtBlock: (t, ~blockNumber: int) => float
70
+ let blockAtProgress: (t, ~progress: float) => int
71
+ let getNextQuery: (
72
+ t,
73
+ ~chainTargetItems: float,
74
+ ~chunkItemsMultiplier: float=?,
75
+ ~maxTargetBlock: int=?,
76
+ ) => FetchState.nextQuery
77
+ let dispatch: (
78
+ t,
79
+ ~executeQuery: FetchState.query => promise<unit>,
80
+ ~waitForNewBlock: (~knownHeight: int) => promise<int>,
81
+ ~onNewBlock: (~knownHeight: int) => unit,
82
+ ~action: FetchState.nextQuery,
83
+ ~stateId: int,
84
+ ) => promise<unit>
85
+
86
+ // Views.
87
+ type chainData = {
88
+ chainId: float,
89
+ poweredByHyperSync: bool,
90
+ firstEventBlockNumber: option<int>,
91
+ latestProcessedBlock: option<int>,
92
+ timestampCaughtUpToHeadOrEndblock: option<Date.t>,
93
+ numEventsProcessed: float,
94
+ latestFetchedBlockNumber: int,
95
+ @as("currentBlockHeight")
96
+ knownHeight: int,
97
+ numBatchesFetched: int,
98
+ startBlock: int,
99
+ endBlock: option<int>,
100
+ numAddresses: int,
101
+ }
102
+ let toChainData: t => chainData
103
+ let toChainMetadata: t => InternalTable.Chains.metaFields
104
+ let toChainBeforeBatch: t => Batch.chainBeforeBatch
105
+ let isReadyToEnterReorgThresholdAfterBatch: (t, ~batch: Batch.t) => bool
106
+
53
107
  // Derived (pure).
54
108
  let hasProcessedToEndblock: t => bool
55
109
  let getHighestBlockBelowThreshold: t => int
56
110
  let isActivelyIndexing: t => bool
57
111
  let isReady: t => bool
58
112
  let isFetchingAtHead: t => bool
113
+ let isAtHeadWithoutEndBlock: t => bool
59
114
 
60
115
  // State transitions. The chain state is mutated only through these.
61
116
  let handleQueryResult: (
@@ -65,7 +120,20 @@ let handleQueryResult: (
65
120
  ~newItemsWithDcs: array<Internal.item>,
66
121
  ~latestFetchedBlock: FetchState.blockNumberAndTimestamp,
67
122
  ~knownHeight: int,
123
+ ~transactionStore: option<TransactionStore.t>,
124
+ ~blockStore: option<BlockStore.t>,
68
125
  ) => unit
126
+ let materializeBatchItems: (
127
+ t,
128
+ ~items: array<Internal.item>,
129
+ ~ecosystem: Ecosystem.name,
130
+ ) => promise<unit>
131
+ let materializePageItems: (
132
+ ~items: array<Internal.item>,
133
+ ~transactionStore: option<TransactionStore.t>,
134
+ ~blockStore: option<BlockStore.t>,
135
+ ~ecosystem: Ecosystem.name,
136
+ ) => promise<unit>
69
137
  let registerReorgGuard: (
70
138
  t,
71
139
  ~blockHashes: array<ReorgDetection.blockData>,
@@ -76,7 +144,7 @@ let updateKnownHeight: (t, ~knownHeight: int) => unit
76
144
  let setEndBlockToFirstEvent: (t, ~blockNumber: int) => unit
77
145
  let enterReorgThreshold: t => unit
78
146
  let advanceAfterBatch: (t, ~batch: Batch.t, ~enteringReorgThreshold: bool) => unit
79
- let applyBatchProgress: (t, ~batch: Batch.t) => unit
147
+ let applyBatchProgress: (t, ~batch: Batch.t, ~blockTimestampName: string) => unit
80
148
  let markReady: t => unit
81
149
  let rollback: (
82
150
  t,
package/src/Config.res CHANGED
@@ -23,6 +23,7 @@ type evmRpcConfig = {
23
23
  sourceFor: Source.sourceFor,
24
24
  syncConfig: option<sourceSyncOptions>,
25
25
  ws: option<string>,
26
+ headers: option<dict<string>>,
26
27
  }
27
28
 
28
29
  type sourceConfig =
@@ -156,6 +157,7 @@ let rpcConfigSchema = S.schema(s =>
156
157
  "url": s.matches(S.string),
157
158
  "for": s.matches(rpcSourceForSchema),
158
159
  "ws": s.matches(S.option(S.string)),
160
+ "headers": s.matches(S.option(S.dict(S.string))),
159
161
  "initialBlockInterval": s.matches(S.option(S.int)),
160
162
  "backoffMultiplicative": s.matches(S.option(S.float)),
161
163
  "accelerationAdditive": s.matches(S.option(S.int)),
@@ -195,9 +197,9 @@ let svmEventDescriptorSchema = S.schema(s =>
195
197
  {
196
198
  "discriminator": s.matches(S.option(S.string)),
197
199
  "discriminatorByteLen": s.matches(S.int),
198
- "includeTransaction": s.matches(S.bool),
200
+ "transactionFields": s.matches(S.array(Internal.svmTransactionFieldSchema)),
201
+ "blockFields": s.matches(S.option(S.array(Internal.svmBlockFieldSchema))),
199
202
  "includeLogs": s.matches(S.bool),
200
- "includeTokenBalances": s.matches(S.bool),
201
203
  "accountFilters": s.matches(
202
204
  S.option(
203
205
  S.array(
@@ -653,7 +655,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
653
655
  ~events: option<array<_>>,
654
656
  ~abi,
655
657
  ~chainId: int,
656
- ~startBlock: option<int>,
657
658
  ~addresses: array<string>,
658
659
  ~svmDefinedTypes: JSON.t=JSON.Null,
659
660
  ) => {
@@ -676,10 +677,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
676
677
  ~kind=fuelKind,
677
678
  ~sighash,
678
679
  ~rawAbi=abi->(Utils.magic: EvmTypes.Abi.t => JSON.t),
679
- ~isWildcard=false,
680
- ~handler=None,
681
- ~contractRegister=None,
682
- ~startBlock?,
683
680
  ) :> Internal.eventConfig)
684
681
  | None =>
685
682
  JsError.throwWithMessage(
@@ -704,9 +701,9 @@ let fromPublic = (publicConfigJson: JSON.t) => {
704
701
  "svm": option<{
705
702
  "discriminator": option<string>,
706
703
  "discriminatorByteLen": int,
707
- "includeTransaction": bool,
704
+ "transactionFields": array<Internal.svmTransactionField>,
705
+ "blockFields": option<array<Internal.svmBlockField>>,
708
706
  "includeLogs": bool,
709
- "includeTokenBalances": bool,
710
707
  "accountFilters": option<
711
708
  array<array<{"position": int, "values": array<string>}>>,
712
709
  >,
@@ -740,18 +737,14 @@ let fromPublic = (publicConfigJson: JSON.t) => {
740
737
  ~programId,
741
738
  ~discriminator=svm["discriminator"],
742
739
  ~discriminatorByteLen=svm["discriminatorByteLen"],
743
- ~includeTransaction=svm["includeTransaction"],
740
+ ~transactionFields=svm["transactionFields"],
741
+ ~blockFields=?svm["blockFields"],
744
742
  ~includeLogs=svm["includeLogs"],
745
- ~includeTokenBalances=svm["includeTokenBalances"],
746
743
  ~accountFilters,
747
744
  ~isInner=svm["isInner"],
748
- ~isWildcard=false,
749
- ~handler=None,
750
- ~contractRegister=None,
751
745
  ~accounts=svm["accounts"]->Option.getOr([]),
752
746
  ~args=svm["args"]->Option.getOr(JSON.Null),
753
747
  ~definedTypes=svmDefinedTypes,
754
- ~startBlock?,
755
748
  ) :> Internal.eventConfig)
756
749
  | _ =>
757
750
  (EventConfigBuilder.buildEvmEventConfig(
@@ -759,15 +752,8 @@ let fromPublic = (publicConfigJson: JSON.t) => {
759
752
  ~eventName,
760
753
  ~sighash,
761
754
  ~params,
762
- ~isWildcard=false,
763
- ~handler=None,
764
- ~contractRegister=None,
765
- ~eventFilters=None,
766
- ~probeChainId=chainId,
767
- ~onEventBlockFilterSchema=ecosystem.onEventBlockFilterSchema,
768
755
  ~blockFields=?eventItem["blockFields"],
769
756
  ~transactionFields=?eventItem["transactionFields"],
770
- ~startBlock?,
771
757
  ~globalBlockFieldsSet,
772
758
  ~globalTransactionFieldsSet,
773
759
  ) :> Internal.eventConfig)
@@ -819,17 +805,14 @@ let fromPublic = (publicConfigJson: JSON.t) => {
819
805
  let addresses = rawAddresses->Array.map(parseAddress)
820
806
  let startBlock = chainContract->Option.flatMap(cc => cc["startBlock"])
821
807
 
822
- // Build event configs from JSON (field selections resolved inline)
823
- // chainId is threaded in so the where-callback detection probe
824
- // exercises the callback with this chain's real id — handlers
825
- // that branch on `chain.id` are taken through the same path
826
- // they will follow at runtime.
808
+ // Build event definitions from JSON (field selections resolved
809
+ // inline). Handlers and per-chain `where` filters are layered on
810
+ // later as `onEventRegistration`s in `ChainState.makeInternal`.
827
811
  let events = buildContractEvents(
828
812
  ~contractName=capitalizedName,
829
813
  ~events=contractData["events"],
830
814
  ~abi=contractData["abi"],
831
815
  ~chainId,
832
- ~startBlock,
833
816
  ~addresses=rawAddresses,
834
817
  ~svmDefinedTypes=contractData["svmAbi"]
835
818
  ->Option.map(a => a["definedTypes"])
@@ -910,6 +893,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
910
893
  sourceFor: parseRpcSourceFor(rpcConfig["for"]),
911
894
  syncConfig,
912
895
  ws: rpcConfig["ws"],
896
+ headers: rpcConfig["headers"],
913
897
  }
914
898
  })
915
899
  EvmSourceConfig({hypersync: publicChainConfig["hypersync"], rpcs})
@@ -1018,6 +1002,50 @@ let fromPublic = (publicConfigJson: JSON.t) => {
1018
1002
  }
1019
1003
  }
1020
1004
 
1005
+ // Canonicalize a user-provided address to the configured casing so it matches
1006
+ // addresses parsed from config.yaml during routing. HyperSync/RPC data arrives
1007
+ // already canonical; only user-land input (simulate srcAddress, contractRegister
1008
+ // add) can carry arbitrary casing and needs this before comparison.
1009
+ let normalizeUserAddress = (config: t, address: Address.t): Address.t =>
1010
+ switch config.ecosystem.name {
1011
+ | Ecosystem.Evm =>
1012
+ if config.lowercaseAddresses {
1013
+ address->Address.Evm.fromAddressLowercaseOrThrow
1014
+ } else {
1015
+ address->Address.Evm.fromAddressOrThrow
1016
+ }
1017
+ // TODO: Ideally we should do the same for other ecosystems
1018
+ | Ecosystem.Fuel | Ecosystem.Svm => address
1019
+ }
1020
+
1021
+ // Relaxed counterpart to normalizeUserAddress, used only for simulate srcAddress.
1022
+ // Tests commonly use short placeholder strings like "0xfoo" as a stand-in
1023
+ // identity (e.g. for a wildcard event whose srcAddress doesn't need to resolve
1024
+ // to any real contract), so only the "0x" prefix is enforced here. Under
1025
+ // address_format: checksum, a real address is checksummed even if the input
1026
+ // casing doesn't match (getAddress doesn't require the input to already be
1027
+ // checksummed) — a placeholder that isn't valid hex falls back unchanged.
1028
+ let normalizeSimulateAddress = (config: t, address: Address.t): Address.t =>
1029
+ switch config.ecosystem.name {
1030
+ | Ecosystem.Evm =>
1031
+ let str = address->Address.toString
1032
+ if !(str->String.startsWith("0x")) {
1033
+ JsError.throwWithMessage(
1034
+ `simulate: srcAddress "${str}" is invalid. Expected a string starting with "0x".`,
1035
+ )
1036
+ }
1037
+ if config.lowercaseAddresses {
1038
+ address->Address.Evm.fromAddressLowercaseOrThrow
1039
+ } else {
1040
+ try {
1041
+ address->Address.Evm.fromAddressOrThrow
1042
+ } catch {
1043
+ | _ => address
1044
+ }
1045
+ }
1046
+ | Ecosystem.Fuel | Ecosystem.Svm => address
1047
+ }
1048
+
1021
1049
  // Look up an event config by (contract, event) name. When `chainId` is given,
1022
1050
  // returns that chain's per-chain event config (matters for where-callback
1023
1051
  // probe detection, which runs with the chain's real id). Without `chainId`,
@@ -1259,11 +1287,11 @@ let throwIfIncompatible = (
1259
1287
  }
1260
1288
  }
1261
1289
 
1262
- // The returned value is a pure function of the JSON no handler
1263
- // registrations are applied here. Post-registration configs come from
1264
- // `HandlerLoader.applyRegistrations`. That purity is what lets this
1265
- // memoize without invalidation.
1266
- let loadWithoutRegistrations = () =>
1290
+ // The returned value is a pure function of the JSON: it holds only event
1291
+ // definitions, never handler/contractRegister/where state (that's layered on
1292
+ // separately as `HandlerRegister.registrationsByChainId`). That purity is
1293
+ // what lets this memoize without invalidation.
1294
+ let load = () =>
1267
1295
  switch cached.contents {
1268
1296
  | Some(c) => c
1269
1297
  | None => {
@@ -81,6 +81,7 @@ let rpcConfigSchema = S$RescriptSchema.schema(s => ({
81
81
  url: s.m(S$RescriptSchema.string),
82
82
  for: s.m(rpcSourceForSchema),
83
83
  ws: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
84
+ headers: s.m(S$RescriptSchema.option(S$RescriptSchema.dict(S$RescriptSchema.string))),
84
85
  initialBlockInterval: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
85
86
  backoffMultiplicative: s.m(S$RescriptSchema.option(S$RescriptSchema.float)),
86
87
  accelerationAdditive: s.m(S$RescriptSchema.option(S$RescriptSchema.int)),
@@ -111,9 +112,9 @@ let publicConfigChainSchema = S$RescriptSchema.schema(s => ({
111
112
  let svmEventDescriptorSchema = S$RescriptSchema.schema(s => ({
112
113
  discriminator: s.m(S$RescriptSchema.option(S$RescriptSchema.string)),
113
114
  discriminatorByteLen: s.m(S$RescriptSchema.int),
114
- includeTransaction: s.m(S$RescriptSchema.bool),
115
+ transactionFields: s.m(S$RescriptSchema.array(Internal.svmTransactionFieldSchema)),
116
+ blockFields: s.m(S$RescriptSchema.option(S$RescriptSchema.array(Internal.svmBlockFieldSchema))),
115
117
  includeLogs: s.m(S$RescriptSchema.bool),
116
- includeTokenBalances: s.m(S$RescriptSchema.bool),
117
118
  accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
118
119
  position: s.m(S$RescriptSchema.int),
119
120
  values: s.m(S$RescriptSchema.array(S$RescriptSchema.string))
@@ -490,7 +491,7 @@ function fromPublic(publicConfigJson) {
490
491
  };
491
492
  });
492
493
  }
493
- let buildContractEvents = (contractName, events, abi, chainId, startBlock, addresses, svmDefinedTypesOpt) => {
494
+ let buildContractEvents = (contractName, events, abi, chainId, addresses, svmDefinedTypesOpt) => {
494
495
  let svmDefinedTypes = svmDefinedTypesOpt !== undefined ? svmDefinedTypesOpt : null;
495
496
  if (events !== undefined) {
496
497
  return events.map(eventItem => {
@@ -500,10 +501,10 @@ function fromPublic(publicConfigJson) {
500
501
  let kind = eventItem.kind;
501
502
  switch (ecosystemName) {
502
503
  case "evm" :
503
- return EventConfigBuilder.buildEvmEventConfig(contractName, eventName, sighash, params, false, undefined, undefined, undefined, chainId, ecosystem.onEventBlockFilterSchema, eventItem.blockFields, eventItem.transactionFields, startBlock, Primitive_option.some(globalBlockFieldsSet), Primitive_option.some(globalTransactionFieldsSet));
504
+ return EventConfigBuilder.buildEvmEventConfig(contractName, eventName, sighash, params, eventItem.blockFields, eventItem.transactionFields, Primitive_option.some(globalBlockFieldsSet), Primitive_option.some(globalTransactionFieldsSet));
504
505
  case "fuel" :
505
506
  if (kind !== undefined) {
506
- return EventConfigBuilder.buildFuelEventConfig(contractName, eventName, kind, sighash, abi, false, undefined, undefined, startBlock);
507
+ return EventConfigBuilder.buildFuelEventConfig(contractName, eventName, kind, sighash, abi);
507
508
  } else {
508
509
  return Stdlib_JsError.throwWithMessage(`Fuel event ` + contractName + `.` + eventName + ` is missing "kind" in internal config`);
509
510
  }
@@ -518,7 +519,7 @@ function fromPublic(publicConfigJson) {
518
519
  position: af.position,
519
520
  values: af.values
520
521
  })));
521
- return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.includeTransaction, svm.includeLogs, svm.includeTokenBalances, accountFilters, svm.isInner, false, undefined, undefined, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes, startBlock);
522
+ return EventConfigBuilder.buildSvmInstructionEventConfig(contractName, eventName, programId, svm.discriminator, svm.discriminatorByteLen, svm.includeLogs, svm.transactionFields, svm.blockFields, accountFilters, svm.isInner, Stdlib_Option.getOr(svm.accounts, []), Stdlib_Option.getOr(svm.args, null), svmDefinedTypes);
522
523
  }
523
524
  });
524
525
  } else {
@@ -559,7 +560,7 @@ function fromPublic(publicConfigJson) {
559
560
  let rawAddresses = Stdlib_Option.getOr(Stdlib_Option.flatMap(chainContract, cc => cc.addresses), []);
560
561
  let addresses = rawAddresses.map(parseAddress);
561
562
  let startBlock = Stdlib_Option.flatMap(chainContract, cc => cc.startBlock);
562
- let events = buildContractEvents(capitalizedName, contractData.events, contractData.abi, chainId, startBlock, rawAddresses, Stdlib_Option.getOr(Stdlib_Option.map(contractData.svmAbi, a => a.definedTypes), null));
563
+ let events = buildContractEvents(capitalizedName, contractData.events, contractData.abi, chainId, rawAddresses, Stdlib_Option.getOr(Stdlib_Option.map(contractData.svmAbi, a => a.definedTypes), null));
563
564
  return {
564
565
  name: capitalizedName,
565
566
  abi: contractData.abi,
@@ -607,7 +608,8 @@ function fromPublic(publicConfigJson) {
607
608
  url: rpcConfig.url,
608
609
  sourceFor: parseRpcSourceFor(rpcConfig.for),
609
610
  syncConfig: syncConfig,
610
- ws: rpcConfig.ws
611
+ ws: rpcConfig.ws,
612
+ headers: rpcConfig.headers
611
613
  };
612
614
  });
613
615
  sourceConfig = {
@@ -705,6 +707,42 @@ function fromPublic(publicConfigJson) {
705
707
  };
706
708
  }
707
709
 
710
+ function normalizeUserAddress(config, address) {
711
+ let match = config.ecosystem.name;
712
+ switch (match) {
713
+ case "evm" :
714
+ if (config.lowercaseAddresses) {
715
+ return Address.Evm.fromAddressLowercaseOrThrow(address);
716
+ } else {
717
+ return Address.Evm.fromAddressOrThrow(address);
718
+ }
719
+ case "fuel" :
720
+ case "svm" :
721
+ return address;
722
+ }
723
+ }
724
+
725
+ function normalizeSimulateAddress(config, address) {
726
+ let match = config.ecosystem.name;
727
+ switch (match) {
728
+ case "evm" :
729
+ if (!address.startsWith("0x")) {
730
+ Stdlib_JsError.throwWithMessage(`simulate: srcAddress "` + address + `" is invalid. Expected a string starting with "0x".`);
731
+ }
732
+ if (config.lowercaseAddresses) {
733
+ return Address.Evm.fromAddressLowercaseOrThrow(address);
734
+ }
735
+ try {
736
+ return Address.Evm.fromAddressOrThrow(address);
737
+ } catch (exn) {
738
+ return address;
739
+ }
740
+ case "fuel" :
741
+ case "svm" :
742
+ return address;
743
+ }
744
+ }
745
+
708
746
  function getEventConfig(config, contractName, eventName, chainId) {
709
747
  let chains;
710
748
  if (chainId !== undefined) {
@@ -974,7 +1012,7 @@ function throwIfIncompatible(changedPaths, resetCommand, runCommand, hasClickhou
974
1012
  Stdlib_JsError.throwWithMessage(`The following config changes are incompatible with the existing indexer data:\n\n` + bullets + `\n\nPick one:\n 1. ` + padTo(option1, col) + `# resume indexing where it left off\n 2. ` + padTo(resetCommand, col) + `# delete all indexed data and start over` + option3);
975
1013
  }
976
1014
 
977
- function loadWithoutRegistrations() {
1015
+ function load() {
978
1016
  let c = cached.contents;
979
1017
  if (c !== undefined) {
980
1018
  return c;
@@ -1011,6 +1049,8 @@ export {
1011
1049
  publicConfigStorageSchema,
1012
1050
  publicConfigSchema,
1013
1051
  fromPublic,
1052
+ normalizeUserAddress,
1053
+ normalizeSimulateAddress,
1014
1054
  getEventConfig,
1015
1055
  shouldSaveHistory,
1016
1056
  shouldPruneHistory,
@@ -1021,7 +1061,7 @@ export {
1021
1061
  canonicalJson,
1022
1062
  diffPaths,
1023
1063
  throwIfIncompatible,
1024
- loadWithoutRegistrations,
1064
+ load,
1025
1065
  getPgUserEntities,
1026
1066
  }
1027
1067
  /* schema Not a pure module */
@@ -22,18 +22,8 @@ let makeAddFunction = (~params: contractRegisterParams, ~contractName: string):
22
22
  ~logger=Ecosystem.getItemLogger(params.item, ~ecosystem=params.config.ecosystem),
23
23
  )
24
24
  }
25
- let validatedAddress = if params.config.ecosystem.name === Evm {
26
- // The value is passed from the user-land,
27
- // so we need to validate and checksum/lowercase the address.
28
- if params.config.lowercaseAddresses {
29
- contractAddress->Address.Evm.fromAddressLowercaseOrThrow
30
- } else {
31
- contractAddress->Address.Evm.fromAddressOrThrow
32
- }
33
- } else {
34
- // TODO: Ideally we should do the same for other ecosystems
35
- contractAddress
36
- }
25
+ // The value is passed from user-land, so validate and checksum/lowercase it.
26
+ let validatedAddress = params.config->Config.normalizeUserAddress(contractAddress)
37
27
 
38
28
  params.onRegister(~item=params.item, ~contractAddress=validatedAddress, ~contractName)
39
29
  }
@@ -1,6 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as Address from "./Address.res.mjs";
3
+ import * as Config from "./Config.res.mjs";
4
4
  import * as ChainMap from "./ChainMap.res.mjs";
5
5
  import * as Ecosystem from "./Ecosystem.res.mjs";
6
6
  import * as EntityFilter from "./db/EntityFilter.res.mjs";
@@ -12,9 +12,7 @@ function makeAddFunction(params, contractName) {
12
12
  if (params.isResolved) {
13
13
  ErrorHandling.mkLogAndRaise(Ecosystem.getItemLogger(params.item, params.config.ecosystem), undefined, new Error(`Impossible to access context.chain after the contract register is resolved. Make sure you didn't miss an await in the handler.`));
14
14
  }
15
- let validatedAddress = params.config.ecosystem.name === "evm" ? (
16
- params.config.lowercaseAddresses ? Address.Evm.fromAddressLowercaseOrThrow(contractAddress) : Address.Evm.fromAddressOrThrow(contractAddress)
17
- ) : contractAddress;
15
+ let validatedAddress = Config.normalizeUserAddress(params.config, contractAddress);
18
16
  params.onRegister(params.item, validatedAddress, contractName);
19
17
  };
20
18
  }
@@ -73,4 +71,4 @@ export {
73
71
  getContractRegisterContext,
74
72
  getContractRegisterArgs,
75
73
  }
76
- /* Address Not a pure module */
74
+ /* Config Not a pure module */
package/src/Core.res CHANGED
@@ -6,9 +6,10 @@
6
6
  // tighter `Null.t` captures the exact boundary shape.
7
7
  type evmHypersyncClientCtor
8
8
  type evmRpcClientCtor
9
- type evmDecoderCtor
10
9
  type svmHypersyncClientCtor
11
10
  type hyperfuelClientCtor
11
+ type transactionStoreCtor
12
+ type blockStoreCtor
12
13
 
13
14
  type addon = {
14
15
  getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
@@ -17,12 +18,22 @@ type addon = {
17
18
  evmHypersyncClient: evmHypersyncClientCtor,
18
19
  @as("EvmRpcClient")
19
20
  evmRpcClient: evmRpcClientCtor,
20
- @as("EvmDecoder")
21
- evmDecoder: evmDecoderCtor,
22
21
  @as("SvmHypersyncClient")
23
22
  svmHypersyncClient: svmHypersyncClientCtor,
24
23
  @as("HyperfuelClient")
25
24
  hyperfuelClient: hyperfuelClientCtor,
25
+ @as("TransactionStore")
26
+ transactionStore: transactionStoreCtor,
27
+ @as("BlockStore")
28
+ blockStore: blockStoreCtor,
29
+ // Ordered transaction-field names exposed for the field-code contract test
30
+ // (the ReScript `transactionFields` arrays must match the Rust ordinals).
31
+ evmTransactionFieldNames: unit => array<string>,
32
+ svmTransactionFieldNames: unit => array<string>,
33
+ // Ordered block-field names for the same contract test (`blockFields` arrays
34
+ // must match the Rust ordinals).
35
+ evmBlockFieldNames: unit => array<string>,
36
+ svmBlockFieldNames: unit => array<string>,
26
37
  }
27
38
 
28
39
  @module("node:module") external createRequire: string => {..} = "createRequire"