envio 3.3.0-alpha.1 → 3.3.0-alpha.11
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.
- package/evm.schema.json +10 -0
- package/index.d.ts +45 -25
- package/licenses/CLA.md +35 -0
- package/licenses/EULA.md +67 -0
- package/licenses/LICENSE.md +45 -0
- package/licenses/README.md +35 -0
- package/package.json +10 -9
- package/src/Address.res +5 -2
- package/src/Address.res.mjs +3 -1
- package/src/Api.res +1 -1
- package/src/Api.res.mjs +1 -1
- package/src/Batch.res +0 -6
- package/src/Batch.res.mjs +1 -12
- package/src/BatchProcessing.res +7 -12
- package/src/BatchProcessing.res.mjs +8 -7
- package/src/Bin.res +3 -0
- package/src/Bin.res.mjs +4 -0
- package/src/ChainFetching.res +53 -16
- package/src/ChainFetching.res.mjs +37 -19
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +630 -161
- package/src/ChainState.res.mjs +438 -101
- package/src/ChainState.resi +77 -4
- package/src/Config.res +70 -38
- package/src/Config.res.mjs +52 -12
- package/src/ContractRegisterContext.res +2 -12
- package/src/ContractRegisterContext.res.mjs +3 -5
- package/src/Core.res +34 -3
- package/src/Core.res.mjs +12 -0
- package/src/CrossChainState.res +130 -89
- package/src/CrossChainState.res.mjs +66 -61
- package/src/CrossChainState.resi +1 -1
- package/src/Ecosystem.res +5 -6
- package/src/Ecosystem.res.mjs +3 -3
- package/src/Envio.res +17 -11
- package/src/EnvioGlobal.res +53 -0
- package/src/EnvioGlobal.res.mjs +31 -0
- package/src/EventConfigBuilder.res +191 -127
- package/src/EventConfigBuilder.res.mjs +116 -66
- package/src/EventProcessing.res +77 -28
- package/src/EventProcessing.res.mjs +56 -29
- package/src/ExitOnCaughtUp.res +10 -2
- package/src/ExitOnCaughtUp.res.mjs +10 -1
- package/src/FetchState.res +684 -415
- package/src/FetchState.res.mjs +505 -486
- package/src/HandlerLoader.res +8 -104
- package/src/HandlerLoader.res.mjs +2 -81
- package/src/HandlerRegister.res +520 -144
- package/src/HandlerRegister.res.mjs +286 -133
- package/src/HandlerRegister.resi +24 -8
- package/src/IndexerState.res +10 -3
- package/src/IndexerState.res.mjs +11 -4
- package/src/IndexerState.resi +2 -1
- package/src/IndexingAddresses.res +108 -0
- package/src/IndexingAddresses.res.mjs +101 -0
- package/src/IndexingAddresses.resi +34 -0
- package/src/Internal.res +166 -48
- package/src/Internal.res.mjs +30 -2
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +6 -6
- package/src/LogSelection.res +92 -217
- package/src/LogSelection.res.mjs +95 -184
- package/src/Main.res +71 -236
- package/src/Main.res.mjs +60 -145
- package/src/Metrics.res +74 -0
- package/src/Metrics.res.mjs +75 -0
- package/src/PgStorage.res +4 -4
- package/src/PgStorage.res.mjs +5 -5
- package/src/Prometheus.res +10 -63
- package/src/Prometheus.res.mjs +124 -171
- package/src/PruneStaleHistory.res +2 -2
- package/src/PruneStaleHistory.res.mjs +3 -3
- package/src/RawEvent.res +7 -2
- package/src/RawEvent.res.mjs +4 -4
- package/src/Rollback.res +3 -3
- package/src/Rollback.res.mjs +4 -4
- package/src/RollbackCommit.res +4 -1
- package/src/RollbackCommit.res.mjs +3 -2
- package/src/SimulateDeadInputTracker.res +85 -0
- package/src/SimulateDeadInputTracker.res.mjs +73 -0
- package/src/SimulateDeadInputTracker.resi +12 -0
- package/src/SimulateItems.res +128 -28
- package/src/SimulateItems.res.mjs +82 -40
- package/src/TestIndexer.res +55 -41
- package/src/TestIndexer.res.mjs +39 -32
- package/src/TopicFilter.res +1 -25
- package/src/TopicFilter.res.mjs +0 -74
- package/src/bindings/Performance.res +7 -0
- package/src/bindings/Performance.res.mjs +21 -0
- package/src/bindings/Performance.resi +7 -0
- package/src/bindings/Viem.res +0 -46
- package/src/bindings/Viem.res.mjs +1 -43
- package/src/sources/BlockStore.res +46 -0
- package/src/sources/BlockStore.res.mjs +24 -0
- package/src/sources/EventRouter.res +31 -49
- package/src/sources/EventRouter.res.mjs +12 -25
- package/src/sources/Evm.res +83 -72
- package/src/sources/Evm.res.mjs +53 -69
- package/src/sources/EvmChain.res +7 -34
- package/src/sources/EvmChain.res.mjs +6 -27
- package/src/sources/EvmRpcClient.res +71 -5
- package/src/sources/EvmRpcClient.res.mjs +13 -4
- package/src/sources/FieldMask.res +39 -0
- package/src/sources/FieldMask.res.mjs +42 -0
- package/src/sources/Fuel.res +9 -6
- package/src/sources/Fuel.res.mjs +5 -10
- package/src/sources/HyperFuelSource.res +46 -50
- package/src/sources/HyperFuelSource.res.mjs +64 -54
- package/src/sources/HyperSync.res +45 -42
- package/src/sources/HyperSync.res.mjs +68 -60
- package/src/sources/HyperSync.resi +14 -4
- package/src/sources/HyperSyncClient.res +107 -119
- package/src/sources/HyperSyncClient.res.mjs +36 -31
- package/src/sources/HyperSyncHeightStream.res +1 -8
- package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
- package/src/sources/HyperSyncSource.res +60 -254
- package/src/sources/HyperSyncSource.res.mjs +57 -180
- package/src/sources/Rpc.res +15 -79
- package/src/sources/Rpc.res.mjs +25 -101
- package/src/sources/RpcSource.res +221 -514
- package/src/sources/RpcSource.res.mjs +216 -411
- package/src/sources/RpcWebSocketHeightStream.res +0 -5
- package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
- package/src/sources/SimulateSource.res +44 -20
- package/src/sources/SimulateSource.res.mjs +41 -14
- package/src/sources/Source.res +34 -6
- package/src/sources/SourceManager.res +77 -19
- package/src/sources/SourceManager.res.mjs +63 -21
- package/src/sources/SourceManager.resi +10 -0
- package/src/sources/Svm.res +31 -16
- package/src/sources/Svm.res.mjs +36 -13
- package/src/sources/SvmHyperSyncClient.res +5 -32
- package/src/sources/SvmHyperSyncSource.res +159 -134
- package/src/sources/SvmHyperSyncSource.res.mjs +164 -129
- package/src/sources/TransactionStore.res +49 -0
- package/src/sources/TransactionStore.res.mjs +30 -0
- package/src/tui/Tui.res +13 -16
- package/src/tui/Tui.res.mjs +12 -14
- package/svm.schema.json +49 -37
- package/src/bindings/Hrtime.res +0 -58
- package/src/bindings/Hrtime.res.mjs +0 -90
- package/src/bindings/Hrtime.resi +0 -30
package/src/ChainState.resi
CHANGED
|
@@ -8,6 +8,8 @@ let configAddresses: Config.chain => array<Internal.indexingAddress>
|
|
|
8
8
|
let make: (
|
|
9
9
|
~chainConfig: Config.chain,
|
|
10
10
|
~fetchState: FetchState.t,
|
|
11
|
+
~onEventRegistrations: array<Internal.onEventRegistration>=?,
|
|
12
|
+
~indexingAddresses: IndexingAddresses.t,
|
|
11
13
|
~sourceManager: SourceManager.t,
|
|
12
14
|
~reorgDetection: ReorgDetection.t,
|
|
13
15
|
~committedProgressBlockNumber: int,
|
|
@@ -15,13 +17,16 @@ let make: (
|
|
|
15
17
|
~numEventsProcessed: float=?,
|
|
16
18
|
~timestampCaughtUpToHeadOrEndblock: option<Date.t>=?,
|
|
17
19
|
~isProgressAtHead: bool=?,
|
|
20
|
+
~transactionStore: TransactionStore.t=?,
|
|
21
|
+
~chainDensity: option<float>=?,
|
|
22
|
+
~blockStore: BlockStore.t=?,
|
|
18
23
|
~logger: Pino.t,
|
|
19
24
|
) => t
|
|
20
25
|
|
|
21
26
|
let makeFromConfig: (
|
|
22
27
|
Config.chain,
|
|
23
28
|
~config: Config.t,
|
|
24
|
-
~
|
|
29
|
+
~registrationsByChainId: HandlerRegister.registrationsByChainId,
|
|
25
30
|
~knownHeight: int,
|
|
26
31
|
) => t
|
|
27
32
|
|
|
@@ -32,13 +37,12 @@ let makeFromDbState: (
|
|
|
32
37
|
~isInReorgThreshold: bool,
|
|
33
38
|
~isRealtime: bool,
|
|
34
39
|
~config: Config.t,
|
|
35
|
-
~
|
|
40
|
+
~registrationsByChainId: HandlerRegister.registrationsByChainId,
|
|
36
41
|
~reducedPollingInterval: int=?,
|
|
37
42
|
) => t
|
|
38
43
|
|
|
39
44
|
// Accessors.
|
|
40
45
|
let logger: t => Pino.t
|
|
41
|
-
let fetchState: t => FetchState.t
|
|
42
46
|
let sourceManager: t => SourceManager.t
|
|
43
47
|
let chainConfig: t => Config.chain
|
|
44
48
|
let reorgDetection: t => ReorgDetection.t
|
|
@@ -50,12 +54,68 @@ let pendingBudget: t => float
|
|
|
50
54
|
let startFetchingQueries: (t, ~queries: array<FetchState.query>) => unit
|
|
51
55
|
let timestampCaughtUpToHeadOrEndblock: t => option<Date.t>
|
|
52
56
|
|
|
57
|
+
// Fetch-frontier reads.
|
|
58
|
+
let knownHeight: t => int
|
|
59
|
+
let contractAddresses: (t, ~contractName: string) => array<Address.t>
|
|
60
|
+
let bufferSize: t => int
|
|
61
|
+
let bufferReadyCount: t => int
|
|
62
|
+
let getProgressPercentage: t => float
|
|
63
|
+
let chainDensity: t => option<float>
|
|
64
|
+
let effectiveDensity: t => option<float>
|
|
65
|
+
let hasReadyItem: t => bool
|
|
66
|
+
let isReadyToEnterReorgThreshold: t => bool
|
|
67
|
+
|
|
68
|
+
// Fetch control.
|
|
69
|
+
let targetBlock: (t, ~chainTargetItems: float) => int
|
|
70
|
+
let progressAtBlock: (t, ~blockNumber: int) => float
|
|
71
|
+
let blockAtProgress: (t, ~progress: float) => int
|
|
72
|
+
let getNextQuery: (
|
|
73
|
+
t,
|
|
74
|
+
~chainTargetItems: float,
|
|
75
|
+
~chunkItemsMultiplier: float=?,
|
|
76
|
+
~maxTargetBlock: int=?,
|
|
77
|
+
) => FetchState.nextQuery
|
|
78
|
+
let dispatch: (
|
|
79
|
+
t,
|
|
80
|
+
~executeQuery: FetchState.query => promise<unit>,
|
|
81
|
+
~waitForNewBlock: (~knownHeight: int) => promise<int>,
|
|
82
|
+
~onNewBlock: (~knownHeight: int) => unit,
|
|
83
|
+
~action: FetchState.nextQuery,
|
|
84
|
+
~stateId: int,
|
|
85
|
+
) => promise<unit>
|
|
86
|
+
|
|
87
|
+
// Views.
|
|
88
|
+
type chainData = {
|
|
89
|
+
chainId: float,
|
|
90
|
+
poweredByHyperSync: bool,
|
|
91
|
+
firstEventBlockNumber: option<int>,
|
|
92
|
+
latestProcessedBlock: option<int>,
|
|
93
|
+
timestampCaughtUpToHeadOrEndblock: option<Date.t>,
|
|
94
|
+
numEventsProcessed: float,
|
|
95
|
+
latestFetchedBlockNumber: int,
|
|
96
|
+
@as("currentBlockHeight")
|
|
97
|
+
knownHeight: int,
|
|
98
|
+
numBatchesFetched: int,
|
|
99
|
+
startBlock: int,
|
|
100
|
+
endBlock: option<int>,
|
|
101
|
+
numAddresses: int,
|
|
102
|
+
}
|
|
103
|
+
let toChainData: t => chainData
|
|
104
|
+
let toChainMetadata: t => InternalTable.Chains.metaFields
|
|
105
|
+
let toChainBeforeBatch: t => Batch.chainBeforeBatch
|
|
106
|
+
let isReadyToEnterReorgThresholdAfterBatch: (t, ~batch: Batch.t) => bool
|
|
107
|
+
|
|
53
108
|
// Derived (pure).
|
|
54
109
|
let hasProcessedToEndblock: t => bool
|
|
55
110
|
let getHighestBlockBelowThreshold: t => int
|
|
56
111
|
let isActivelyIndexing: t => bool
|
|
57
112
|
let isReady: t => bool
|
|
58
113
|
let isFetchingAtHead: t => bool
|
|
114
|
+
let isAtHeadWithoutEndBlock: t => bool
|
|
115
|
+
|
|
116
|
+
// Drop over-fetched events an address-param filter rejects, before contract
|
|
117
|
+
// registration runs on them. Uses the chain's current indexing addresses.
|
|
118
|
+
let filterByClientAddress: (t, array<Internal.item>) => array<Internal.item>
|
|
59
119
|
|
|
60
120
|
// State transitions. The chain state is mutated only through these.
|
|
61
121
|
let handleQueryResult: (
|
|
@@ -65,7 +125,20 @@ let handleQueryResult: (
|
|
|
65
125
|
~newItemsWithDcs: array<Internal.item>,
|
|
66
126
|
~latestFetchedBlock: FetchState.blockNumberAndTimestamp,
|
|
67
127
|
~knownHeight: int,
|
|
128
|
+
~transactionStore: option<TransactionStore.t>,
|
|
129
|
+
~blockStore: option<BlockStore.t>,
|
|
68
130
|
) => unit
|
|
131
|
+
let materializeBatchItems: (
|
|
132
|
+
t,
|
|
133
|
+
~items: array<Internal.item>,
|
|
134
|
+
~ecosystem: Ecosystem.name,
|
|
135
|
+
) => promise<unit>
|
|
136
|
+
let materializePageItems: (
|
|
137
|
+
~items: array<Internal.item>,
|
|
138
|
+
~transactionStore: option<TransactionStore.t>,
|
|
139
|
+
~blockStore: option<BlockStore.t>,
|
|
140
|
+
~ecosystem: Ecosystem.name,
|
|
141
|
+
) => promise<unit>
|
|
69
142
|
let registerReorgGuard: (
|
|
70
143
|
t,
|
|
71
144
|
~blockHashes: array<ReorgDetection.blockData>,
|
|
@@ -76,7 +149,7 @@ let updateKnownHeight: (t, ~knownHeight: int) => unit
|
|
|
76
149
|
let setEndBlockToFirstEvent: (t, ~blockNumber: int) => unit
|
|
77
150
|
let enterReorgThreshold: t => unit
|
|
78
151
|
let advanceAfterBatch: (t, ~batch: Batch.t, ~enteringReorgThreshold: bool) => unit
|
|
79
|
-
let applyBatchProgress: (t, ~batch: Batch.t) => unit
|
|
152
|
+
let applyBatchProgress: (t, ~batch: Batch.t, ~blockTimestampName: string) => unit
|
|
80
153
|
let markReady: t => unit
|
|
81
154
|
let rollback: (
|
|
82
155
|
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,17 +197,21 @@ let svmEventDescriptorSchema = S.schema(s =>
|
|
|
195
197
|
{
|
|
196
198
|
"discriminator": s.matches(S.option(S.string)),
|
|
197
199
|
"discriminatorByteLen": s.matches(S.int),
|
|
198
|
-
"
|
|
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
|
-
|
|
203
|
+
// An array of AND-groups OR-ed together: the CLI normalizes both the flat
|
|
204
|
+
// and `any_of` YAML shapes to `Vec<Vec<SvmAccountFilterJson>>`.
|
|
201
205
|
"accountFilters": s.matches(
|
|
202
206
|
S.option(
|
|
203
207
|
S.array(
|
|
204
|
-
S.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
S.array(
|
|
209
|
+
S.schema(s =>
|
|
210
|
+
{
|
|
211
|
+
"position": s.matches(S.int),
|
|
212
|
+
"values": s.matches(S.array(S.string)),
|
|
213
|
+
}
|
|
214
|
+
),
|
|
209
215
|
),
|
|
210
216
|
),
|
|
211
217
|
),
|
|
@@ -653,7 +659,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
653
659
|
~events: option<array<_>>,
|
|
654
660
|
~abi,
|
|
655
661
|
~chainId: int,
|
|
656
|
-
~startBlock: option<int>,
|
|
657
662
|
~addresses: array<string>,
|
|
658
663
|
~svmDefinedTypes: JSON.t=JSON.Null,
|
|
659
664
|
) => {
|
|
@@ -676,10 +681,6 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
676
681
|
~kind=fuelKind,
|
|
677
682
|
~sighash,
|
|
678
683
|
~rawAbi=abi->(Utils.magic: EvmTypes.Abi.t => JSON.t),
|
|
679
|
-
~isWildcard=false,
|
|
680
|
-
~handler=None,
|
|
681
|
-
~contractRegister=None,
|
|
682
|
-
~startBlock?,
|
|
683
684
|
) :> Internal.eventConfig)
|
|
684
685
|
| None =>
|
|
685
686
|
JsError.throwWithMessage(
|
|
@@ -704,9 +705,9 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
704
705
|
"svm": option<{
|
|
705
706
|
"discriminator": option<string>,
|
|
706
707
|
"discriminatorByteLen": int,
|
|
707
|
-
"
|
|
708
|
+
"transactionFields": array<Internal.svmTransactionField>,
|
|
709
|
+
"blockFields": option<array<Internal.svmBlockField>>,
|
|
708
710
|
"includeLogs": bool,
|
|
709
|
-
"includeTokenBalances": bool,
|
|
710
711
|
"accountFilters": option<
|
|
711
712
|
array<array<{"position": int, "values": array<string>}>>,
|
|
712
713
|
>,
|
|
@@ -740,18 +741,14 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
740
741
|
~programId,
|
|
741
742
|
~discriminator=svm["discriminator"],
|
|
742
743
|
~discriminatorByteLen=svm["discriminatorByteLen"],
|
|
743
|
-
~
|
|
744
|
+
~transactionFields=svm["transactionFields"],
|
|
745
|
+
~blockFields=?svm["blockFields"],
|
|
744
746
|
~includeLogs=svm["includeLogs"],
|
|
745
|
-
~includeTokenBalances=svm["includeTokenBalances"],
|
|
746
747
|
~accountFilters,
|
|
747
748
|
~isInner=svm["isInner"],
|
|
748
|
-
~isWildcard=false,
|
|
749
|
-
~handler=None,
|
|
750
|
-
~contractRegister=None,
|
|
751
749
|
~accounts=svm["accounts"]->Option.getOr([]),
|
|
752
750
|
~args=svm["args"]->Option.getOr(JSON.Null),
|
|
753
751
|
~definedTypes=svmDefinedTypes,
|
|
754
|
-
~startBlock?,
|
|
755
752
|
) :> Internal.eventConfig)
|
|
756
753
|
| _ =>
|
|
757
754
|
(EventConfigBuilder.buildEvmEventConfig(
|
|
@@ -759,15 +756,8 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
759
756
|
~eventName,
|
|
760
757
|
~sighash,
|
|
761
758
|
~params,
|
|
762
|
-
~isWildcard=false,
|
|
763
|
-
~handler=None,
|
|
764
|
-
~contractRegister=None,
|
|
765
|
-
~eventFilters=None,
|
|
766
|
-
~probeChainId=chainId,
|
|
767
|
-
~onEventBlockFilterSchema=ecosystem.onEventBlockFilterSchema,
|
|
768
759
|
~blockFields=?eventItem["blockFields"],
|
|
769
760
|
~transactionFields=?eventItem["transactionFields"],
|
|
770
|
-
~startBlock?,
|
|
771
761
|
~globalBlockFieldsSet,
|
|
772
762
|
~globalTransactionFieldsSet,
|
|
773
763
|
) :> Internal.eventConfig)
|
|
@@ -819,17 +809,14 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
819
809
|
let addresses = rawAddresses->Array.map(parseAddress)
|
|
820
810
|
let startBlock = chainContract->Option.flatMap(cc => cc["startBlock"])
|
|
821
811
|
|
|
822
|
-
// Build event
|
|
823
|
-
//
|
|
824
|
-
//
|
|
825
|
-
// that branch on `chain.id` are taken through the same path
|
|
826
|
-
// they will follow at runtime.
|
|
812
|
+
// Build event definitions from JSON (field selections resolved
|
|
813
|
+
// inline). Handlers and per-chain `where` filters are layered on
|
|
814
|
+
// later as `onEventRegistration`s in `ChainState.makeInternal`.
|
|
827
815
|
let events = buildContractEvents(
|
|
828
816
|
~contractName=capitalizedName,
|
|
829
817
|
~events=contractData["events"],
|
|
830
818
|
~abi=contractData["abi"],
|
|
831
819
|
~chainId,
|
|
832
|
-
~startBlock,
|
|
833
820
|
~addresses=rawAddresses,
|
|
834
821
|
~svmDefinedTypes=contractData["svmAbi"]
|
|
835
822
|
->Option.map(a => a["definedTypes"])
|
|
@@ -910,6 +897,7 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
910
897
|
sourceFor: parseRpcSourceFor(rpcConfig["for"]),
|
|
911
898
|
syncConfig,
|
|
912
899
|
ws: rpcConfig["ws"],
|
|
900
|
+
headers: rpcConfig["headers"],
|
|
913
901
|
}
|
|
914
902
|
})
|
|
915
903
|
EvmSourceConfig({hypersync: publicChainConfig["hypersync"], rpcs})
|
|
@@ -1018,6 +1006,50 @@ let fromPublic = (publicConfigJson: JSON.t) => {
|
|
|
1018
1006
|
}
|
|
1019
1007
|
}
|
|
1020
1008
|
|
|
1009
|
+
// Canonicalize a user-provided address to the configured casing so it matches
|
|
1010
|
+
// addresses parsed from config.yaml during routing. HyperSync/RPC data arrives
|
|
1011
|
+
// already canonical; only user-land input (simulate srcAddress, contractRegister
|
|
1012
|
+
// add) can carry arbitrary casing and needs this before comparison.
|
|
1013
|
+
let normalizeUserAddress = (config: t, address: Address.t): Address.t =>
|
|
1014
|
+
switch config.ecosystem.name {
|
|
1015
|
+
| Ecosystem.Evm =>
|
|
1016
|
+
if config.lowercaseAddresses {
|
|
1017
|
+
address->Address.Evm.fromAddressLowercaseOrThrow
|
|
1018
|
+
} else {
|
|
1019
|
+
address->Address.Evm.fromAddressOrThrow
|
|
1020
|
+
}
|
|
1021
|
+
// TODO: Ideally we should do the same for other ecosystems
|
|
1022
|
+
| Ecosystem.Fuel | Ecosystem.Svm => address
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// Relaxed counterpart to normalizeUserAddress, used only for simulate srcAddress.
|
|
1026
|
+
// Tests commonly use short placeholder strings like "0xfoo" as a stand-in
|
|
1027
|
+
// identity (e.g. for a wildcard event whose srcAddress doesn't need to resolve
|
|
1028
|
+
// to any real contract), so only the "0x" prefix is enforced here. Under
|
|
1029
|
+
// address_format: checksum, a real address is checksummed even if the input
|
|
1030
|
+
// casing doesn't match (getAddress doesn't require the input to already be
|
|
1031
|
+
// checksummed) — a placeholder that isn't valid hex falls back unchanged.
|
|
1032
|
+
let normalizeSimulateAddress = (config: t, address: Address.t): Address.t =>
|
|
1033
|
+
switch config.ecosystem.name {
|
|
1034
|
+
| Ecosystem.Evm =>
|
|
1035
|
+
let str = address->Address.toString
|
|
1036
|
+
if !(str->String.startsWith("0x")) {
|
|
1037
|
+
JsError.throwWithMessage(
|
|
1038
|
+
`simulate: srcAddress "${str}" is invalid. Expected a string starting with "0x".`,
|
|
1039
|
+
)
|
|
1040
|
+
}
|
|
1041
|
+
if config.lowercaseAddresses {
|
|
1042
|
+
address->Address.Evm.fromAddressLowercaseOrThrow
|
|
1043
|
+
} else {
|
|
1044
|
+
try {
|
|
1045
|
+
address->Address.Evm.fromAddressOrThrow
|
|
1046
|
+
} catch {
|
|
1047
|
+
| _ => address
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
| Ecosystem.Fuel | Ecosystem.Svm => address
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1021
1053
|
// Look up an event config by (contract, event) name. When `chainId` is given,
|
|
1022
1054
|
// returns that chain's per-chain event config (matters for where-callback
|
|
1023
1055
|
// probe detection, which runs with the chain's real id). Without `chainId`,
|
|
@@ -1259,11 +1291,11 @@ let throwIfIncompatible = (
|
|
|
1259
1291
|
}
|
|
1260
1292
|
}
|
|
1261
1293
|
|
|
1262
|
-
// The returned value is a pure function of the JSON
|
|
1263
|
-
//
|
|
1264
|
-
// `
|
|
1265
|
-
// memoize without invalidation.
|
|
1266
|
-
let
|
|
1294
|
+
// The returned value is a pure function of the JSON: it holds only event
|
|
1295
|
+
// definitions, never handler/contractRegister/where state (that's layered on
|
|
1296
|
+
// separately as `HandlerRegister.registrationsByChainId`). That purity is
|
|
1297
|
+
// what lets this memoize without invalidation.
|
|
1298
|
+
let load = () =>
|
|
1267
1299
|
switch cached.contents {
|
|
1268
1300
|
| Some(c) => c
|
|
1269
1301
|
| None => {
|
package/src/Config.res.mjs
CHANGED
|
@@ -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,13 +112,13 @@ 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
|
-
|
|
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
|
-
|
|
117
|
-
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.schema(s => ({
|
|
118
|
+
accountFilters: s.m(S$RescriptSchema.option(S$RescriptSchema.array(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))
|
|
120
|
-
}))))),
|
|
121
|
+
})))))),
|
|
121
122
|
isInner: s.m(S$RescriptSchema.option(S$RescriptSchema.bool)),
|
|
122
123
|
accounts: s.m(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string))),
|
|
123
124
|
args: s.m(S$RescriptSchema.option(S$RescriptSchema.json(false)))
|
|
@@ -490,7 +491,7 @@ function fromPublic(publicConfigJson) {
|
|
|
490
491
|
};
|
|
491
492
|
});
|
|
492
493
|
}
|
|
493
|
-
let buildContractEvents = (contractName, events, abi, chainId,
|
|
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,
|
|
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
|
|
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.
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
|
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
|
|
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
|
-
/*
|
|
74
|
+
/* Config Not a pure module */
|
package/src/Core.res
CHANGED
|
@@ -6,23 +6,42 @@
|
|
|
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
|
|
13
|
+
type parseConfigYamlOptions = {
|
|
14
|
+
schema?: string,
|
|
15
|
+
env?: dict<string>,
|
|
16
|
+
files?: dict<string>,
|
|
17
|
+
isRescript?: bool,
|
|
18
|
+
}
|
|
12
19
|
|
|
13
20
|
type addon = {
|
|
14
21
|
getConfigJson: (~configPath: Null.t<string>, ~directory: Null.t<string>) => string,
|
|
22
|
+
encodeIndexedTopic: (~abiType: string, ~value: unknown) => EvmTypes.Hex.t,
|
|
23
|
+
parseConfigYaml: (string, parseConfigYamlOptions) => string,
|
|
15
24
|
runCli: (~args: array<string>, ~envioPackageDir: Null.t<string>) => promise<Null.t<string>>,
|
|
16
25
|
@as("EvmHypersyncClient")
|
|
17
26
|
evmHypersyncClient: evmHypersyncClientCtor,
|
|
18
27
|
@as("EvmRpcClient")
|
|
19
28
|
evmRpcClient: evmRpcClientCtor,
|
|
20
|
-
@as("EvmDecoder")
|
|
21
|
-
evmDecoder: evmDecoderCtor,
|
|
22
29
|
@as("SvmHypersyncClient")
|
|
23
30
|
svmHypersyncClient: svmHypersyncClientCtor,
|
|
24
31
|
@as("HyperfuelClient")
|
|
25
32
|
hyperfuelClient: hyperfuelClientCtor,
|
|
33
|
+
@as("TransactionStore")
|
|
34
|
+
transactionStore: transactionStoreCtor,
|
|
35
|
+
@as("BlockStore")
|
|
36
|
+
blockStore: blockStoreCtor,
|
|
37
|
+
// Ordered transaction-field names exposed for the field-code contract test
|
|
38
|
+
// (the ReScript `transactionFields` arrays must match the Rust ordinals).
|
|
39
|
+
evmTransactionFieldNames: unit => array<string>,
|
|
40
|
+
svmTransactionFieldNames: unit => array<string>,
|
|
41
|
+
// Ordered block-field names for the same contract test (`blockFields` arrays
|
|
42
|
+
// must match the Rust ordinals).
|
|
43
|
+
evmBlockFieldNames: unit => array<string>,
|
|
44
|
+
svmBlockFieldNames: unit => array<string>,
|
|
26
45
|
}
|
|
27
46
|
|
|
28
47
|
@module("node:module") external createRequire: string => {..} = "createRequire"
|
|
@@ -186,6 +205,18 @@ let getConfigJson = (~configPath=?, ~directory=?) => {
|
|
|
186
205
|
)
|
|
187
206
|
}
|
|
188
207
|
|
|
208
|
+
// Pure config entry point: no cwd, config file, schema file, .env, or process env lookup.
|
|
209
|
+
// A supplied schema is authoritative; omitted or blank schema text means an empty schema.
|
|
210
|
+
let parseConfigYaml = (~schema=?, ~env=?, ~files=?, ~isRescript=false, yaml) => {
|
|
211
|
+
let addon = getAddon()
|
|
212
|
+
addon.parseConfigYaml(yaml, {
|
|
213
|
+
?schema,
|
|
214
|
+
?env,
|
|
215
|
+
?files,
|
|
216
|
+
isRescript,
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
189
220
|
let runCli = args => {
|
|
190
221
|
let addon = getAddon()
|
|
191
222
|
addon.runCli(~args, ~envioPackageDir=Null.make(envioPackageDir))
|
package/src/Core.res.mjs
CHANGED
|
@@ -180,6 +180,17 @@ function getConfigJson(configPath, directory) {
|
|
|
180
180
|
return addon.getConfigJson(Stdlib_Null.fromOption(configPath), Stdlib_Null.fromOption(directory));
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
function parseConfigYaml(schema, env, files, isRescriptOpt, yaml) {
|
|
184
|
+
let isRescript = isRescriptOpt !== undefined ? isRescriptOpt : false;
|
|
185
|
+
let addon = getAddon();
|
|
186
|
+
return addon.parseConfigYaml(yaml, {
|
|
187
|
+
schema: schema,
|
|
188
|
+
env: env,
|
|
189
|
+
files: files,
|
|
190
|
+
isRescript: isRescript
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
183
194
|
function runCli(args) {
|
|
184
195
|
let addon = getAddon();
|
|
185
196
|
return addon.runCli(args, envioPackageDir);
|
|
@@ -196,6 +207,7 @@ export {
|
|
|
196
207
|
addonRef,
|
|
197
208
|
getAddon,
|
|
198
209
|
getConfigJson,
|
|
210
|
+
parseConfigYaml,
|
|
199
211
|
runCli,
|
|
200
212
|
}
|
|
201
213
|
/* envioPackageDir Not a pure module */
|