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.
- package/evm.schema.json +10 -0
- package/index.d.ts +45 -25
- package/package.json +7 -7
- 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 +43 -9
- package/src/ChainFetching.res.mjs +34 -15
- package/src/ChainMetadata.res +1 -11
- package/src/ChainMetadata.res.mjs +1 -10
- package/src/ChainState.res +605 -151
- package/src/ChainState.res.mjs +427 -104
- package/src/ChainState.resi +72 -4
- package/src/Config.res +61 -33
- package/src/Config.res.mjs +50 -10
- package/src/ContractRegisterContext.res +2 -12
- package/src/ContractRegisterContext.res.mjs +3 -5
- package/src/Core.res +14 -3
- package/src/CrossChainState.res +113 -89
- package/src/CrossChainState.res.mjs +64 -60
- 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 +177 -88
- package/src/EventConfigBuilder.res.mjs +99 -42
- package/src/EventProcessing.res +74 -28
- package/src/EventProcessing.res.mjs +55 -29
- package/src/ExitOnCaughtUp.res +10 -2
- package/src/ExitOnCaughtUp.res.mjs +10 -1
- package/src/FetchState.res +544 -356
- package/src/FetchState.res.mjs +412 -471
- package/src/HandlerLoader.res +8 -104
- package/src/HandlerLoader.res.mjs +2 -81
- package/src/HandlerRegister.res +518 -144
- package/src/HandlerRegister.res.mjs +285 -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 +155 -46
- package/src/Internal.res.mjs +30 -2
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +6 -6
- package/src/LogSelection.res +102 -165
- package/src/LogSelection.res.mjs +101 -116
- 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 +91 -23
- package/src/SimulateItems.res.mjs +55 -32
- package/src/TestIndexer.res +54 -40
- package/src/TestIndexer.res.mjs +37 -30
- 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 -41
- 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 +36 -33
- package/src/sources/EventRouter.res.mjs +13 -14
- package/src/sources/Evm.res +83 -72
- package/src/sources/Evm.res.mjs +53 -69
- package/src/sources/EvmChain.res +18 -20
- package/src/sources/EvmChain.res.mjs +15 -15
- package/src/sources/EvmRpcClient.res +74 -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 +39 -7
- package/src/sources/HyperSync.res.mjs +57 -37
- package/src/sources/HyperSync.resi +12 -2
- package/src/sources/HyperSyncClient.res +22 -111
- package/src/sources/HyperSyncClient.res.mjs +2 -30
- package/src/sources/HyperSyncHeightStream.res +1 -8
- package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
- package/src/sources/HyperSyncSource.res +94 -107
- package/src/sources/HyperSyncSource.res.mjs +82 -86
- package/src/sources/Rpc.res +15 -47
- package/src/sources/Rpc.res.mjs +25 -56
- package/src/sources/RpcSource.res +320 -467
- package/src/sources/RpcSource.res.mjs +268 -385
- package/src/sources/RpcWebSocketHeightStream.res +0 -5
- package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
- package/src/sources/SimulateSource.res +10 -4
- package/src/sources/SimulateSource.res.mjs +16 -6
- 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/sources/Evm.res
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// EVM's concrete item payload. Erased to `Internal.eventPayload` on the item
|
|
2
|
-
// and recovered here via `toPayload`.
|
|
2
|
+
// and recovered here via `toPayload`. HyperSync omits `transaction` (it lives
|
|
3
|
+
// raw in the per-chain store and is written onto the payload at batch prep);
|
|
4
|
+
// RPC/simulate build it inline.
|
|
3
5
|
type payload = {
|
|
4
6
|
contractName: string,
|
|
5
7
|
eventName: string,
|
|
@@ -7,12 +9,62 @@ type payload = {
|
|
|
7
9
|
chainId: int,
|
|
8
10
|
srcAddress: Address.t,
|
|
9
11
|
logIndex: int,
|
|
10
|
-
transaction
|
|
11
|
-
block
|
|
12
|
+
transaction?: Internal.eventTransaction,
|
|
13
|
+
// HyperSync omits `block` (it lives raw in the per-chain store and is written
|
|
14
|
+
// onto the payload at batch prep); RPC/simulate build it inline.
|
|
15
|
+
block?: Internal.eventBlock,
|
|
12
16
|
}
|
|
13
17
|
external fromPayload: payload => Internal.eventPayload = "%identity"
|
|
14
18
|
external toPayload: Internal.eventPayload => payload = "%identity"
|
|
15
19
|
|
|
20
|
+
// Ordered transaction field names, the field codes shared with the Rust store
|
|
21
|
+
// (`EvmTxField`). Derived from the typed field list so the two can't drift;
|
|
22
|
+
// `Internal.allEvmTransactionFields` is pinned to the Rust ordinal order by a test.
|
|
23
|
+
let transactionFields =
|
|
24
|
+
Internal.allEvmTransactionFields->(
|
|
25
|
+
Utils.magic: array<Internal.evmTransactionField> => array<string>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
// One event's selected transaction fields → store selection bitmask. Computed
|
|
29
|
+
// per event at config build and cached on the event config.
|
|
30
|
+
let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
|
|
31
|
+
|
|
32
|
+
// Ordered block field names. The index of each is the field code shared with the
|
|
33
|
+
// Rust store (`EvmBlockField`) — keep this order in sync.
|
|
34
|
+
let blockFields = [
|
|
35
|
+
"number",
|
|
36
|
+
"timestamp",
|
|
37
|
+
"hash",
|
|
38
|
+
"parentHash",
|
|
39
|
+
"nonce",
|
|
40
|
+
"sha3Uncles",
|
|
41
|
+
"logsBloom",
|
|
42
|
+
"transactionsRoot",
|
|
43
|
+
"stateRoot",
|
|
44
|
+
"receiptsRoot",
|
|
45
|
+
"miner",
|
|
46
|
+
"difficulty",
|
|
47
|
+
"totalDifficulty",
|
|
48
|
+
"extraData",
|
|
49
|
+
"size",
|
|
50
|
+
"gasLimit",
|
|
51
|
+
"gasUsed",
|
|
52
|
+
"uncles",
|
|
53
|
+
"baseFeePerGas",
|
|
54
|
+
"blobGasUsed",
|
|
55
|
+
"excessBlobGas",
|
|
56
|
+
"parentBeaconBlockRoot",
|
|
57
|
+
"withdrawalsRoot",
|
|
58
|
+
"l1BlockNumber",
|
|
59
|
+
"sendCount",
|
|
60
|
+
"sendRoot",
|
|
61
|
+
"mixHash",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
// One event's selected block fields → store selection bitmask. Computed per
|
|
65
|
+
// event at config build and cached on the event config.
|
|
66
|
+
let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields)
|
|
67
|
+
|
|
16
68
|
let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
|
|
17
69
|
delete fields.hash
|
|
18
70
|
delete fields.number
|
|
@@ -21,70 +73,6 @@ let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
|
|
|
21
73
|
|
|
22
74
|
let make = (~logger: Pino.t): Ecosystem.t => {
|
|
23
75
|
name: Evm,
|
|
24
|
-
blockFields: [
|
|
25
|
-
"number",
|
|
26
|
-
"timestamp",
|
|
27
|
-
"hash",
|
|
28
|
-
"parentHash",
|
|
29
|
-
"nonce",
|
|
30
|
-
"sha3Uncles",
|
|
31
|
-
"logsBloom",
|
|
32
|
-
"transactionsRoot",
|
|
33
|
-
"stateRoot",
|
|
34
|
-
"receiptsRoot",
|
|
35
|
-
"miner",
|
|
36
|
-
"difficulty",
|
|
37
|
-
"totalDifficulty",
|
|
38
|
-
"extraData",
|
|
39
|
-
"size",
|
|
40
|
-
"gasLimit",
|
|
41
|
-
"gasUsed",
|
|
42
|
-
"uncles",
|
|
43
|
-
"baseFeePerGas",
|
|
44
|
-
"blobGasUsed",
|
|
45
|
-
"excessBlobGas",
|
|
46
|
-
"parentBeaconBlockRoot",
|
|
47
|
-
"withdrawalsRoot",
|
|
48
|
-
"l1BlockNumber",
|
|
49
|
-
"sendCount",
|
|
50
|
-
"sendRoot",
|
|
51
|
-
"mixHash",
|
|
52
|
-
],
|
|
53
|
-
transactionFields: [
|
|
54
|
-
"transactionIndex",
|
|
55
|
-
"hash",
|
|
56
|
-
"from",
|
|
57
|
-
"to",
|
|
58
|
-
"gas",
|
|
59
|
-
"gasPrice",
|
|
60
|
-
"maxPriorityFeePerGas",
|
|
61
|
-
"maxFeePerGas",
|
|
62
|
-
"cumulativeGasUsed",
|
|
63
|
-
"effectiveGasPrice",
|
|
64
|
-
"gasUsed",
|
|
65
|
-
"input",
|
|
66
|
-
"nonce",
|
|
67
|
-
"value",
|
|
68
|
-
"v",
|
|
69
|
-
"r",
|
|
70
|
-
"s",
|
|
71
|
-
"contractAddress",
|
|
72
|
-
"logsBloom",
|
|
73
|
-
"root",
|
|
74
|
-
"status",
|
|
75
|
-
"yParity",
|
|
76
|
-
"chainId",
|
|
77
|
-
"maxFeePerBlobGas",
|
|
78
|
-
"blobVersionedHashes",
|
|
79
|
-
"type",
|
|
80
|
-
"l1Fee",
|
|
81
|
-
"l1GasPrice",
|
|
82
|
-
"l1GasUsed",
|
|
83
|
-
"l1FeeScalar",
|
|
84
|
-
"gasUsedForL1",
|
|
85
|
-
"accessList",
|
|
86
|
-
"authorizationList",
|
|
87
|
-
],
|
|
88
76
|
blockNumberName: "number",
|
|
89
77
|
blockTimestampName: "timestamp",
|
|
90
78
|
blockHashName: "hash",
|
|
@@ -102,17 +90,22 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
102
90
|
// range chunk is validated by `eventBlockRangeSchema` in
|
|
103
91
|
// `LogSelection.res` which rejects `_lte`/`_every` (use `onBlock` for
|
|
104
92
|
// stride- and endBlock-based block handlers).
|
|
93
|
+
// `S.strict` on the inner object rejects unknown `block` fields (e.g. a
|
|
94
|
+
// `numbre` typo or `block: {timestamp: ...}`) instead of silently
|
|
95
|
+
// ignoring them.
|
|
105
96
|
onEventBlockFilterSchema: S.object(s =>
|
|
106
|
-
s.field("block", S.option(S.object(s2 => s2.field("number", S.unknown))))
|
|
97
|
+
s.field("block", S.option(S.object(s2 => s2.field("number", S.unknown))->S.strict))
|
|
107
98
|
),
|
|
108
99
|
logger,
|
|
109
|
-
|
|
100
|
+
// The payload carries `transaction` by batch prep (HyperSync) or inline
|
|
101
|
+
// (RPC/simulate), so the event is the payload as-is.
|
|
102
|
+
toEvent: eventItem => eventItem.payload->(Utils.magic: Internal.eventPayload => Internal.event),
|
|
110
103
|
toEventLogger: eventItem =>
|
|
111
104
|
Logging.createChildFrom(
|
|
112
105
|
~logger,
|
|
113
106
|
~params={
|
|
114
|
-
"contract": eventItem.eventConfig.contractName,
|
|
115
|
-
"event": eventItem.eventConfig.name,
|
|
107
|
+
"contract": eventItem.onEventRegistration.eventConfig.contractName,
|
|
108
|
+
"event": eventItem.onEventRegistration.eventConfig.name,
|
|
116
109
|
"chainId": eventItem.chain->ChainMap.Chain.toChainId,
|
|
117
110
|
"block": eventItem.blockNumber,
|
|
118
111
|
"logIndex": eventItem.logIndex,
|
|
@@ -121,11 +114,29 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
121
114
|
),
|
|
122
115
|
toRawEvent: eventItem => {
|
|
123
116
|
let payload = eventItem.payload->toPayload
|
|
117
|
+
let eventConfig =
|
|
118
|
+
eventItem.onEventRegistration.eventConfig->(
|
|
119
|
+
Utils.magic: Internal.eventConfig => Internal.evmEventConfig
|
|
120
|
+
)
|
|
121
|
+
// Store-backed payloads get `block` written at batch prep and inline
|
|
122
|
+
// sources carry it from the start, with hash/timestamp always selected —
|
|
123
|
+
// so both are present by the time a raw event is built.
|
|
124
|
+
let header = switch payload.block {
|
|
125
|
+
| Some(block) => block->(Utils.magic: Internal.eventBlock => {"hash": string, "timestamp": int})
|
|
126
|
+
| None =>
|
|
127
|
+
JsError.throwWithMessage("Unexpected case: The event block is missing for a raw event")
|
|
128
|
+
}
|
|
124
129
|
eventItem->RawEvent.make(
|
|
125
130
|
~block=payload.block,
|
|
126
131
|
~transaction=payload.transaction,
|
|
127
|
-
|
|
132
|
+
// The decoder emits `{}` for zero-parameter events, which the params
|
|
133
|
+
// schema rejects; pass unit so it serializes to the "null" sentinel.
|
|
134
|
+
~params=eventConfig.paramsMetadata->Array.length == 0
|
|
135
|
+
? ()->(Utils.magic: unit => Internal.eventParams)
|
|
136
|
+
: payload.params,
|
|
128
137
|
~srcAddress=payload.srcAddress,
|
|
138
|
+
~blockHash=header["hash"],
|
|
139
|
+
~blockTimestamp=header["timestamp"],
|
|
129
140
|
~cleanUpRawEventFieldsInPlace,
|
|
130
141
|
)
|
|
131
142
|
},
|
package/src/sources/Evm.res.mjs
CHANGED
|
@@ -1,8 +1,47 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Logging from "../Logging.res.mjs";
|
|
4
|
+
import * as Internal from "../Internal.res.mjs";
|
|
4
5
|
import * as RawEvent from "../RawEvent.res.mjs";
|
|
6
|
+
import * as BlockStore from "./BlockStore.res.mjs";
|
|
7
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
8
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
5
9
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
10
|
+
import * as TransactionStore from "./TransactionStore.res.mjs";
|
|
11
|
+
|
|
12
|
+
let eventTransactionFieldMask = TransactionStore.makeMaskFn(Internal.allEvmTransactionFields);
|
|
13
|
+
|
|
14
|
+
let blockFields = [
|
|
15
|
+
"number",
|
|
16
|
+
"timestamp",
|
|
17
|
+
"hash",
|
|
18
|
+
"parentHash",
|
|
19
|
+
"nonce",
|
|
20
|
+
"sha3Uncles",
|
|
21
|
+
"logsBloom",
|
|
22
|
+
"transactionsRoot",
|
|
23
|
+
"stateRoot",
|
|
24
|
+
"receiptsRoot",
|
|
25
|
+
"miner",
|
|
26
|
+
"difficulty",
|
|
27
|
+
"totalDifficulty",
|
|
28
|
+
"extraData",
|
|
29
|
+
"size",
|
|
30
|
+
"gasLimit",
|
|
31
|
+
"gasUsed",
|
|
32
|
+
"uncles",
|
|
33
|
+
"baseFeePerGas",
|
|
34
|
+
"blobGasUsed",
|
|
35
|
+
"excessBlobGas",
|
|
36
|
+
"parentBeaconBlockRoot",
|
|
37
|
+
"withdrawalsRoot",
|
|
38
|
+
"l1BlockNumber",
|
|
39
|
+
"sendCount",
|
|
40
|
+
"sendRoot",
|
|
41
|
+
"mixHash"
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
let eventBlockFieldMask = BlockStore.makeMaskFn(blockFields);
|
|
6
45
|
|
|
7
46
|
let cleanUpRawEventFieldsInPlace = (fields => {
|
|
8
47
|
delete fields.hash
|
|
@@ -13,82 +52,18 @@ let cleanUpRawEventFieldsInPlace = (fields => {
|
|
|
13
52
|
function make(logger) {
|
|
14
53
|
return {
|
|
15
54
|
name: "evm",
|
|
16
|
-
blockFields: [
|
|
17
|
-
"number",
|
|
18
|
-
"timestamp",
|
|
19
|
-
"hash",
|
|
20
|
-
"parentHash",
|
|
21
|
-
"nonce",
|
|
22
|
-
"sha3Uncles",
|
|
23
|
-
"logsBloom",
|
|
24
|
-
"transactionsRoot",
|
|
25
|
-
"stateRoot",
|
|
26
|
-
"receiptsRoot",
|
|
27
|
-
"miner",
|
|
28
|
-
"difficulty",
|
|
29
|
-
"totalDifficulty",
|
|
30
|
-
"extraData",
|
|
31
|
-
"size",
|
|
32
|
-
"gasLimit",
|
|
33
|
-
"gasUsed",
|
|
34
|
-
"uncles",
|
|
35
|
-
"baseFeePerGas",
|
|
36
|
-
"blobGasUsed",
|
|
37
|
-
"excessBlobGas",
|
|
38
|
-
"parentBeaconBlockRoot",
|
|
39
|
-
"withdrawalsRoot",
|
|
40
|
-
"l1BlockNumber",
|
|
41
|
-
"sendCount",
|
|
42
|
-
"sendRoot",
|
|
43
|
-
"mixHash"
|
|
44
|
-
],
|
|
45
|
-
transactionFields: [
|
|
46
|
-
"transactionIndex",
|
|
47
|
-
"hash",
|
|
48
|
-
"from",
|
|
49
|
-
"to",
|
|
50
|
-
"gas",
|
|
51
|
-
"gasPrice",
|
|
52
|
-
"maxPriorityFeePerGas",
|
|
53
|
-
"maxFeePerGas",
|
|
54
|
-
"cumulativeGasUsed",
|
|
55
|
-
"effectiveGasPrice",
|
|
56
|
-
"gasUsed",
|
|
57
|
-
"input",
|
|
58
|
-
"nonce",
|
|
59
|
-
"value",
|
|
60
|
-
"v",
|
|
61
|
-
"r",
|
|
62
|
-
"s",
|
|
63
|
-
"contractAddress",
|
|
64
|
-
"logsBloom",
|
|
65
|
-
"root",
|
|
66
|
-
"status",
|
|
67
|
-
"yParity",
|
|
68
|
-
"chainId",
|
|
69
|
-
"maxFeePerBlobGas",
|
|
70
|
-
"blobVersionedHashes",
|
|
71
|
-
"type",
|
|
72
|
-
"l1Fee",
|
|
73
|
-
"l1GasPrice",
|
|
74
|
-
"l1GasUsed",
|
|
75
|
-
"l1FeeScalar",
|
|
76
|
-
"gasUsedForL1",
|
|
77
|
-
"accessList",
|
|
78
|
-
"authorizationList"
|
|
79
|
-
],
|
|
80
55
|
blockNumberName: "number",
|
|
81
56
|
blockTimestampName: "timestamp",
|
|
82
57
|
blockHashName: "hash",
|
|
83
58
|
cleanUpRawEventFieldsInPlace: cleanUpRawEventFieldsInPlace,
|
|
84
59
|
onBlockMethodName: "onBlock",
|
|
85
60
|
onBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("number", S$RescriptSchema.unknown))))),
|
|
86
|
-
onEventBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.object(s2 => s2.f("number", S$RescriptSchema.unknown))))),
|
|
61
|
+
onEventBlockFilterSchema: S$RescriptSchema.object(s => s.f("block", S$RescriptSchema.option(S$RescriptSchema.strict(S$RescriptSchema.object(s2 => s2.f("number", S$RescriptSchema.unknown)))))),
|
|
87
62
|
logger: logger,
|
|
88
63
|
toEvent: eventItem => eventItem.payload,
|
|
89
64
|
toEventLogger: eventItem => Logging.createChildFrom(logger, {
|
|
90
|
-
contract: eventItem.eventConfig.contractName,
|
|
91
|
-
event: eventItem.eventConfig.name,
|
|
65
|
+
contract: eventItem.onEventRegistration.eventConfig.contractName,
|
|
66
|
+
event: eventItem.onEventRegistration.eventConfig.name,
|
|
92
67
|
chainId: eventItem.chain,
|
|
93
68
|
block: eventItem.blockNumber,
|
|
94
69
|
logIndex: eventItem.logIndex,
|
|
@@ -96,13 +71,22 @@ function make(logger) {
|
|
|
96
71
|
}),
|
|
97
72
|
toRawEvent: eventItem => {
|
|
98
73
|
let payload = eventItem.payload;
|
|
99
|
-
|
|
74
|
+
let eventConfig = eventItem.onEventRegistration.eventConfig;
|
|
75
|
+
let block = payload.block;
|
|
76
|
+
let header = block !== undefined ? Primitive_option.valFromOption(block) : Stdlib_JsError.throwWithMessage("Unexpected case: The event block is missing for a raw event");
|
|
77
|
+
return RawEvent.make(eventItem, payload.block, payload.transaction, eventConfig.paramsMetadata.length === 0 ? undefined : payload.params, payload.srcAddress, header.hash, header.timestamp, cleanUpRawEventFieldsInPlace);
|
|
100
78
|
}
|
|
101
79
|
};
|
|
102
80
|
}
|
|
103
81
|
|
|
82
|
+
let transactionFields = Internal.allEvmTransactionFields;
|
|
83
|
+
|
|
104
84
|
export {
|
|
85
|
+
transactionFields,
|
|
86
|
+
eventTransactionFieldMask,
|
|
87
|
+
blockFields,
|
|
88
|
+
eventBlockFieldMask,
|
|
105
89
|
cleanUpRawEventFieldsInPlace,
|
|
106
90
|
make,
|
|
107
91
|
}
|
|
108
|
-
/*
|
|
92
|
+
/* eventTransactionFieldMask Not a pure module */
|
package/src/sources/EvmChain.res
CHANGED
|
@@ -3,6 +3,7 @@ type rpc = {
|
|
|
3
3
|
sourceFor: Source.sourceFor,
|
|
4
4
|
syncConfig?: Config.sourceSyncOptions,
|
|
5
5
|
ws?: string,
|
|
6
|
+
headers?: dict<string>,
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
let getSyncConfig = (
|
|
@@ -31,46 +32,42 @@ let getSyncConfig = (
|
|
|
31
32
|
intervalCeiling: Env.Configurable.SyncConfig.intervalCeiling->Option.getOr(
|
|
32
33
|
intervalCeiling->Option.getOr(10_000),
|
|
33
34
|
),
|
|
34
|
-
backoffMillis: backoffMillis->Option.getOr(
|
|
35
|
+
backoffMillis: backoffMillis->Option.getOr(2000),
|
|
35
36
|
queryTimeoutMillis,
|
|
36
37
|
fallbackStallTimeout: fallbackStallTimeout->Option.getOr(queryTimeoutMillis / 2),
|
|
37
38
|
pollingInterval: pollingInterval->Option.getOr(1000),
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
let collectEventParams = (
|
|
42
|
+
let collectEventParams = (onEventRegistrations: array<Internal.evmOnEventRegistration>): array<
|
|
42
43
|
HyperSyncClient.Decoder.eventParamsInput,
|
|
43
44
|
> => {
|
|
44
45
|
let result = []
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
})
|
|
55
|
-
->ignore
|
|
46
|
+
onEventRegistrations->Array.forEach(reg => {
|
|
47
|
+
let event = reg.eventConfig->(Utils.magic: Internal.eventConfig => Internal.evmEventConfig)
|
|
48
|
+
result
|
|
49
|
+
->Array.push({
|
|
50
|
+
HyperSyncClient.Decoder.sighash: event.sighash,
|
|
51
|
+
topicCount: event.topicCount,
|
|
52
|
+
eventName: event.name,
|
|
53
|
+
contractName: event.contractName,
|
|
54
|
+
params: event.paramsMetadata,
|
|
56
55
|
})
|
|
56
|
+
->ignore
|
|
57
57
|
})
|
|
58
58
|
result
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
let makeSources = (
|
|
62
62
|
~chain,
|
|
63
|
-
~
|
|
63
|
+
~onEventRegistrations: array<Internal.evmOnEventRegistration>,
|
|
64
64
|
~hyperSync,
|
|
65
65
|
~rpcs: array<rpc>,
|
|
66
66
|
~lowercaseAddresses,
|
|
67
67
|
) => {
|
|
68
|
-
let eventRouter =
|
|
69
|
-
contracts
|
|
70
|
-
->Array.flatMap(contract => contract.events)
|
|
71
|
-
->EventRouter.fromEvmEventModsOrThrow(~chain)
|
|
68
|
+
let eventRouter = onEventRegistrations->EventRouter.fromEvmEventModsOrThrow(~chain)
|
|
72
69
|
|
|
73
|
-
let allEventParams = collectEventParams(
|
|
70
|
+
let allEventParams = collectEventParams(onEventRegistrations)
|
|
74
71
|
|
|
75
72
|
let sources = switch hyperSync {
|
|
76
73
|
| Some(endpointUrl) => [
|
|
@@ -89,7 +86,7 @@ let makeSources = (
|
|
|
89
86
|
]
|
|
90
87
|
| _ => []
|
|
91
88
|
}
|
|
92
|
-
rpcs->Array.forEach(({?syncConfig, url, sourceFor, ?ws}) => {
|
|
89
|
+
rpcs->Array.forEach(({?syncConfig, url, sourceFor, ?ws, ?headers}) => {
|
|
93
90
|
let source = RpcSource.make({
|
|
94
91
|
chain,
|
|
95
92
|
sourceFor,
|
|
@@ -99,6 +96,7 @@ let makeSources = (
|
|
|
99
96
|
allEventParams,
|
|
100
97
|
lowercaseAddresses,
|
|
101
98
|
?ws,
|
|
99
|
+
?headers,
|
|
102
100
|
})
|
|
103
101
|
let _ = sources->Array.push(source)
|
|
104
102
|
})
|
|
@@ -13,32 +13,31 @@ function getSyncConfig(param) {
|
|
|
13
13
|
backoffMultiplicative: Stdlib_Option.getOr(Env.Configurable.SyncConfig.backoffMultiplicative, Stdlib_Option.getOr(param.backoffMultiplicative, 0.8)),
|
|
14
14
|
accelerationAdditive: Stdlib_Option.getOr(Env.Configurable.SyncConfig.accelerationAdditive, Stdlib_Option.getOr(param.accelerationAdditive, 500)),
|
|
15
15
|
intervalCeiling: Stdlib_Option.getOr(Env.Configurable.SyncConfig.intervalCeiling, Stdlib_Option.getOr(param.intervalCeiling, 10000)),
|
|
16
|
-
backoffMillis: Stdlib_Option.getOr(param.backoffMillis,
|
|
16
|
+
backoffMillis: Stdlib_Option.getOr(param.backoffMillis, 2000),
|
|
17
17
|
queryTimeoutMillis: queryTimeoutMillis,
|
|
18
18
|
fallbackStallTimeout: Stdlib_Option.getOr(param.fallbackStallTimeout, queryTimeoutMillis / 2 | 0),
|
|
19
19
|
pollingInterval: Stdlib_Option.getOr(param.pollingInterval, 1000)
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function collectEventParams(
|
|
23
|
+
function collectEventParams(onEventRegistrations) {
|
|
24
24
|
let result = [];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
25
|
+
onEventRegistrations.forEach(reg => {
|
|
26
|
+
let event = reg.eventConfig;
|
|
27
|
+
result.push({
|
|
28
|
+
sighash: event.sighash,
|
|
29
|
+
topicCount: event.topicCount,
|
|
30
|
+
eventName: event.name,
|
|
31
|
+
contractName: event.contractName,
|
|
32
|
+
params: event.paramsMetadata
|
|
34
33
|
});
|
|
35
34
|
});
|
|
36
35
|
return result;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
function makeSources(chain,
|
|
40
|
-
let eventRouter = EventRouter.fromEvmEventModsOrThrow(
|
|
41
|
-
let allEventParams = collectEventParams(
|
|
38
|
+
function makeSources(chain, onEventRegistrations, hyperSync, rpcs, lowercaseAddresses) {
|
|
39
|
+
let eventRouter = EventRouter.fromEvmEventModsOrThrow(onEventRegistrations, chain);
|
|
40
|
+
let allEventParams = collectEventParams(onEventRegistrations);
|
|
42
41
|
let sources = hyperSync !== undefined ? [HyperSyncSource.make({
|
|
43
42
|
chain: chain,
|
|
44
43
|
endpointUrl: hyperSync,
|
|
@@ -60,7 +59,8 @@ function makeSources(chain, contracts, hyperSync, rpcs, lowercaseAddresses) {
|
|
|
60
59
|
eventRouter: eventRouter,
|
|
61
60
|
allEventParams: allEventParams,
|
|
62
61
|
lowercaseAddresses: lowercaseAddresses,
|
|
63
|
-
ws: param.ws
|
|
62
|
+
ws: param.ws,
|
|
63
|
+
headers: param.headers
|
|
64
64
|
});
|
|
65
65
|
sources.push(source);
|
|
66
66
|
});
|
|
@@ -1,9 +1,56 @@
|
|
|
1
|
-
type cfg = {
|
|
1
|
+
type cfg = {
|
|
2
|
+
url: string,
|
|
3
|
+
httpReqTimeoutMillis?: int,
|
|
4
|
+
headers?: dict<string>,
|
|
5
|
+
initialBlockInterval: int,
|
|
6
|
+
backoffMultiplicative: float,
|
|
7
|
+
accelerationAdditive: int,
|
|
8
|
+
intervalCeiling: int,
|
|
9
|
+
backoffMillis: int,
|
|
10
|
+
queryTimeoutMillis: int,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Decoded `params` keyed by contract name, matching the HyperSync decoder's
|
|
14
|
+
// shape so the caller routes by address then picks its contract's params.
|
|
15
|
+
type rpcEventItem = {
|
|
16
|
+
log: Rpc.GetLogs.log,
|
|
17
|
+
params: Nullable.t<dict<Internal.eventParams>>,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// `addresses` omitted matches any address (a wildcard selection). Each `topics`
|
|
21
|
+
// position is `null` (match any) or a list of accepted topic hashes; the
|
|
22
|
+
// single-match case is a one-element list.
|
|
23
|
+
type logSelectionInput = {
|
|
24
|
+
addresses?: array<Address.t>,
|
|
25
|
+
topics: array<Nullable.t<array<string>>>,
|
|
26
|
+
}
|
|
2
27
|
|
|
3
|
-
type
|
|
28
|
+
type nextPageParams = {
|
|
29
|
+
fromBlock: int,
|
|
30
|
+
toBlockCeiling: int,
|
|
31
|
+
logSelections: array<logSelectionInput>,
|
|
32
|
+
partitionId: string,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type nextPageResponse = {
|
|
36
|
+
items: array<rpcEventItem>,
|
|
37
|
+
toBlock: int,
|
|
38
|
+
requestStats: array<Source.requestStat>,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The caller provides a range; Rust decides the actual `toBlock` and returns it.
|
|
42
|
+
type t = {
|
|
43
|
+
getHeight: unit => promise<int>,
|
|
44
|
+
getNextPage: nextPageParams => promise<nextPageResponse>,
|
|
45
|
+
}
|
|
4
46
|
|
|
5
47
|
@send
|
|
6
|
-
external classNew: (
|
|
48
|
+
external classNew: (
|
|
49
|
+
Core.evmRpcClientCtor,
|
|
50
|
+
cfg,
|
|
51
|
+
array<HyperSyncClient.Decoder.eventParamsInput>,
|
|
52
|
+
~checksumAddresses: bool,
|
|
53
|
+
) => t = "new"
|
|
7
54
|
|
|
8
55
|
// Rust encodes JSON-RPC errors as a JSON payload in the napi error's
|
|
9
56
|
// message: `{"kind":"JsonRpcError","code":-32005,"message":"..."}`.
|
|
@@ -34,9 +81,31 @@ let coerceErrorOrThrow = exn =>
|
|
|
34
81
|
| None => exn->throw
|
|
35
82
|
}
|
|
36
83
|
|
|
37
|
-
let make = (
|
|
38
|
-
|
|
84
|
+
let make = (
|
|
85
|
+
~url,
|
|
86
|
+
~checksumAddresses,
|
|
87
|
+
~syncConfig: Config.sourceSync,
|
|
88
|
+
~httpReqTimeoutMillis=?,
|
|
89
|
+
~headers=?,
|
|
90
|
+
~allEventParams=[],
|
|
91
|
+
) => {
|
|
92
|
+
let client = Core.getAddon().evmRpcClient->classNew(
|
|
93
|
+
{
|
|
94
|
+
url,
|
|
95
|
+
?httpReqTimeoutMillis,
|
|
96
|
+
?headers,
|
|
97
|
+
initialBlockInterval: syncConfig.initialBlockInterval,
|
|
98
|
+
backoffMultiplicative: syncConfig.backoffMultiplicative,
|
|
99
|
+
accelerationAdditive: syncConfig.accelerationAdditive,
|
|
100
|
+
intervalCeiling: syncConfig.intervalCeiling,
|
|
101
|
+
backoffMillis: syncConfig.backoffMillis,
|
|
102
|
+
queryTimeoutMillis: syncConfig.queryTimeoutMillis,
|
|
103
|
+
},
|
|
104
|
+
allEventParams,
|
|
105
|
+
~checksumAddresses,
|
|
106
|
+
)
|
|
39
107
|
{
|
|
40
108
|
getHeight: () => client.getHeight()->Promise.catch(coerceErrorOrThrow),
|
|
109
|
+
getNextPage: params => client.getNextPage(params)->Promise.catch(coerceErrorOrThrow),
|
|
41
110
|
}
|
|
42
111
|
}
|
|
@@ -46,13 +46,22 @@ function coerceErrorOrThrow(exn) {
|
|
|
46
46
|
throw exn;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function make(url, httpReqTimeoutMillis) {
|
|
49
|
+
function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers, allEventParamsOpt) {
|
|
50
|
+
let allEventParams = allEventParamsOpt !== undefined ? allEventParamsOpt : [];
|
|
50
51
|
let client = Core.getAddon().EvmRpcClient.new({
|
|
51
52
|
url: url,
|
|
52
|
-
httpReqTimeoutMillis: httpReqTimeoutMillis
|
|
53
|
-
|
|
53
|
+
httpReqTimeoutMillis: httpReqTimeoutMillis,
|
|
54
|
+
headers: headers,
|
|
55
|
+
initialBlockInterval: syncConfig.initialBlockInterval,
|
|
56
|
+
backoffMultiplicative: syncConfig.backoffMultiplicative,
|
|
57
|
+
accelerationAdditive: syncConfig.accelerationAdditive,
|
|
58
|
+
intervalCeiling: syncConfig.intervalCeiling,
|
|
59
|
+
backoffMillis: syncConfig.backoffMillis,
|
|
60
|
+
queryTimeoutMillis: syncConfig.queryTimeoutMillis
|
|
61
|
+
}, allEventParams, checksumAddresses);
|
|
54
62
|
return {
|
|
55
|
-
getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow)
|
|
63
|
+
getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow),
|
|
64
|
+
getNextPage: params => Stdlib_Promise.$$catch(client.getNextPage(params), coerceErrorOrThrow)
|
|
56
65
|
};
|
|
57
66
|
}
|
|
58
67
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Selection-bitmask helpers shared by the per-chain field stores
|
|
2
|
+
// (`TransactionStore`, `BlockStore`). A field's bit index is its position in the
|
|
3
|
+
// ecosystem's ordered field-name array — the same code the Rust store uses.
|
|
4
|
+
|
|
5
|
+
// Field-name → bit-index map from an ordered field-name array.
|
|
6
|
+
let fieldCodes = (fields: array<string>): dict<int> => {
|
|
7
|
+
let codes = Dict.make()
|
|
8
|
+
fields->Array.forEachWithIndex((name, i) => codes->Dict.set(name, i))
|
|
9
|
+
codes
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let pow2: int => float = %raw(`c => Math.pow(2, c)`)
|
|
13
|
+
|
|
14
|
+
// One event's selected fields as a bitmask float (bit `code` set ⇔ selected).
|
|
15
|
+
// Each field appears once, so summing `pow2(code)` sets distinct bits with no
|
|
16
|
+
// overlap; the result stays exact in f64 (codes span 0..31, so a mask is at most
|
|
17
|
+
// 2^32-1).
|
|
18
|
+
let maskFromFields = (selectedFields: Utils.Set.t<string>, ~codes: dict<int>): float => {
|
|
19
|
+
let mask = ref(0.)
|
|
20
|
+
selectedFields->Utils.Set.forEach(name =>
|
|
21
|
+
switch codes->Utils.Dict.dangerouslyGetNonOption(name) {
|
|
22
|
+
| Some(code) => mask := mask.contents +. pow2(code)
|
|
23
|
+
| None => ()
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
mask.contents
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Build an ecosystem's per-event mask function from its ordered field-name
|
|
30
|
+
// array. The field codes are derived once and closed over.
|
|
31
|
+
let makeMaskFn = (fields: array<string>): (Utils.Set.t<string> => float) => {
|
|
32
|
+
let codes = fieldCodes(fields)
|
|
33
|
+
selectedFields => selectedFields->maskFromFields(~codes)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Bitwise OR of two per-event masks. Masks fit in 32 bits (≤32 fields), so
|
|
37
|
+
// `>>> 0` recovers the unsigned value that a plain `|` renders negative once bit
|
|
38
|
+
// 31 is set.
|
|
39
|
+
let orMask: (float, float) => float = %raw(`(a, b) => (a | b) >>> 0`)
|