envio 3.3.0-alpha.6 → 3.3.0-alpha.8
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/index.d.ts +23 -5
- package/package.json +6 -6
- 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/ChainFetching.res +34 -34
- package/src/ChainFetching.res.mjs +29 -29
- package/src/ChainState.res +328 -165
- package/src/ChainState.res.mjs +205 -100
- package/src/ChainState.resi +16 -7
- package/src/Config.res +55 -27
- package/src/Config.res.mjs +46 -7
- package/src/ContractRegisterContext.res +2 -12
- package/src/ContractRegisterContext.res.mjs +3 -5
- package/src/Core.res +7 -0
- package/src/CrossChainState.res +54 -79
- package/src/CrossChainState.res.mjs +45 -60
- package/src/CrossChainState.resi +1 -1
- package/src/Ecosystem.res +3 -3
- package/src/Ecosystem.res.mjs +3 -3
- package/src/Envio.res +14 -9
- package/src/EventConfigBuilder.res +105 -56
- package/src/EventConfigBuilder.res.mjs +69 -33
- package/src/EventProcessing.res +19 -15
- package/src/EventProcessing.res.mjs +13 -12
- package/src/FetchState.res +383 -182
- package/src/FetchState.res.mjs +260 -264
- package/src/HandlerLoader.res +7 -112
- package/src/HandlerLoader.res.mjs +1 -87
- package/src/HandlerRegister.res +209 -6
- package/src/HandlerRegister.res.mjs +90 -5
- package/src/HandlerRegister.resi +13 -2
- package/src/IndexerState.res +6 -3
- package/src/IndexerState.res.mjs +3 -3
- package/src/IndexerState.resi +1 -1
- package/src/IndexingAddresses.res +10 -7
- package/src/IndexingAddresses.res.mjs +8 -7
- package/src/IndexingAddresses.resi +3 -1
- package/src/Internal.res +82 -36
- package/src/Internal.res.mjs +11 -1
- package/src/Main.res +22 -23
- package/src/Main.res.mjs +15 -17
- package/src/Metrics.res +43 -2
- package/src/Metrics.res.mjs +39 -3
- package/src/Prometheus.res +0 -35
- package/src/Prometheus.res.mjs +33 -68
- package/src/RawEvent.res +7 -2
- package/src/RawEvent.res.mjs +4 -4
- package/src/SimulateItems.res +26 -8
- package/src/SimulateItems.res.mjs +11 -14
- package/src/TestIndexer.res +1 -1
- package/src/TestIndexer.res.mjs +1 -1
- package/src/sources/BlockStore.res +46 -0
- package/src/sources/BlockStore.res.mjs +24 -0
- package/src/sources/EventRouter.res +19 -12
- package/src/sources/EventRouter.res.mjs +7 -5
- package/src/sources/Evm.res +54 -4
- package/src/sources/Evm.res.mjs +43 -4
- package/src/sources/EvmChain.res +14 -18
- package/src/sources/EvmChain.res.mjs +12 -13
- package/src/sources/FieldMask.res +39 -0
- package/src/sources/FieldMask.res.mjs +42 -0
- package/src/sources/Fuel.res +5 -2
- package/src/sources/Fuel.res.mjs +4 -3
- package/src/sources/HyperFuelSource.res +32 -40
- package/src/sources/HyperFuelSource.res.mjs +53 -45
- package/src/sources/HyperSync.res +35 -9
- package/src/sources/HyperSync.res.mjs +52 -35
- package/src/sources/HyperSync.resi +11 -3
- package/src/sources/HyperSyncClient.res +15 -4
- package/src/sources/HyperSyncHeightStream.res +1 -8
- package/src/sources/HyperSyncHeightStream.res.mjs +0 -2
- package/src/sources/HyperSyncSource.res +39 -58
- package/src/sources/HyperSyncSource.res.mjs +44 -44
- package/src/sources/RpcSource.res +114 -92
- package/src/sources/RpcSource.res.mjs +77 -46
- package/src/sources/RpcWebSocketHeightStream.res +0 -5
- package/src/sources/RpcWebSocketHeightStream.res.mjs +0 -2
- package/src/sources/SimulateSource.res +6 -3
- package/src/sources/SimulateSource.res.mjs +13 -5
- package/src/sources/Source.res +25 -5
- package/src/sources/SourceManager.res +66 -3
- package/src/sources/SourceManager.res.mjs +55 -7
- package/src/sources/SourceManager.resi +10 -0
- package/src/sources/Svm.res +15 -10
- package/src/sources/Svm.res.mjs +24 -7
- package/src/sources/SvmHyperSyncClient.res +5 -6
- package/src/sources/SvmHyperSyncSource.res +84 -41
- package/src/sources/SvmHyperSyncSource.res.mjs +90 -43
- package/src/sources/TransactionStore.res +6 -107
- package/src/sources/TransactionStore.res.mjs +5 -86
- package/svm.schema.json +19 -0
|
@@ -96,16 +96,16 @@ let getEvmEventId = (~sighash, ~topicCount) => {
|
|
|
96
96
|
sighash ++ "_" ++ topicCount->Int.toString
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
let fromEvmEventModsOrThrow = (events: array<Internal.
|
|
100
|
-
Internal.
|
|
99
|
+
let fromEvmEventModsOrThrow = (events: array<Internal.evmOnEventRegistration>, ~chain): t<
|
|
100
|
+
Internal.evmOnEventRegistration,
|
|
101
101
|
> => {
|
|
102
102
|
let router = empty()
|
|
103
103
|
events->Array.forEach(config => {
|
|
104
104
|
router->addOrThrow(
|
|
105
|
-
config.id,
|
|
105
|
+
config.eventConfig.id,
|
|
106
106
|
config,
|
|
107
|
-
~contractName=config.contractName,
|
|
108
|
-
~eventName=config.name,
|
|
107
|
+
~contractName=config.eventConfig.contractName,
|
|
108
|
+
~eventName=config.eventConfig.name,
|
|
109
109
|
~chain,
|
|
110
110
|
~isWildcard=config.isWildcard,
|
|
111
111
|
)
|
|
@@ -131,21 +131,26 @@ type svmProgramOrdering = {
|
|
|
131
131
|
byteLengthsDesc: array<int>,
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
let fromSvmEventConfigsOrThrow = (events: array<Internal.
|
|
135
|
-
t<Internal.
|
|
134
|
+
let fromSvmEventConfigsOrThrow = (events: array<Internal.svmOnEventRegistration>, ~chain): (
|
|
135
|
+
t<Internal.svmOnEventRegistration>,
|
|
136
136
|
array<svmProgramOrdering>,
|
|
137
137
|
) => {
|
|
138
138
|
let router = empty()
|
|
139
139
|
events->Array.forEach(config => {
|
|
140
|
+
let eventConfig =
|
|
141
|
+
config.eventConfig->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
|
|
140
142
|
// The router tag must include the programId so two programs declaring the
|
|
141
143
|
// same discriminator coexist. The source-side lookup uses the same shape
|
|
142
144
|
// via `getSvmEventId(~programId, ~discriminator)`.
|
|
143
|
-
let routerTag = getSvmEventId(
|
|
145
|
+
let routerTag = getSvmEventId(
|
|
146
|
+
~programId=eventConfig.programId,
|
|
147
|
+
~discriminator=eventConfig.discriminator,
|
|
148
|
+
)
|
|
144
149
|
router->addOrThrow(
|
|
145
150
|
routerTag,
|
|
146
151
|
config,
|
|
147
|
-
~contractName=
|
|
148
|
-
~eventName=
|
|
152
|
+
~contractName=eventConfig.contractName,
|
|
153
|
+
~eventName=eventConfig.name,
|
|
149
154
|
~chain,
|
|
150
155
|
~isWildcard=config.isWildcard,
|
|
151
156
|
)
|
|
@@ -154,7 +159,9 @@ let fromSvmEventConfigsOrThrow = (events: array<Internal.svmInstructionEventConf
|
|
|
154
159
|
// Per-program list of declared discriminator byte lengths, sorted desc.
|
|
155
160
|
let byProgram: dict<Utils.Set.t<int>> = Dict.make()
|
|
156
161
|
events->Array.forEach(config => {
|
|
157
|
-
let
|
|
162
|
+
let eventConfig =
|
|
163
|
+
config.eventConfig->(Utils.magic: Internal.eventConfig => Internal.svmInstructionEventConfig)
|
|
164
|
+
let key = eventConfig.programId->SvmTypes.Pubkey.toString
|
|
158
165
|
let set = switch byProgram->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
159
166
|
| Some(s) => s
|
|
160
167
|
| None =>
|
|
@@ -162,7 +169,7 @@ let fromSvmEventConfigsOrThrow = (events: array<Internal.svmInstructionEventConf
|
|
|
162
169
|
byProgram->Dict.set(key, s)
|
|
163
170
|
s
|
|
164
171
|
}
|
|
165
|
-
let _ = set->Utils.Set.add(
|
|
172
|
+
let _ = set->Utils.Set.add(eventConfig.discriminatorByteLen)
|
|
166
173
|
})
|
|
167
174
|
let ordering =
|
|
168
175
|
byProgram
|
|
@@ -103,7 +103,7 @@ function getEvmEventId(sighash, topicCount) {
|
|
|
103
103
|
|
|
104
104
|
function fromEvmEventModsOrThrow(events, chain) {
|
|
105
105
|
let router = {};
|
|
106
|
-
events.forEach(config => addOrThrow$1(router, config.id, config, config.contractName, config.isWildcard, config.name, chain));
|
|
106
|
+
events.forEach(config => addOrThrow$1(router, config.eventConfig.id, config, config.eventConfig.contractName, config.isWildcard, config.eventConfig.name, chain));
|
|
107
107
|
return router;
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -118,12 +118,14 @@ function getSvmEventId(programId, discriminator) {
|
|
|
118
118
|
function fromSvmEventConfigsOrThrow(events, chain) {
|
|
119
119
|
let router = {};
|
|
120
120
|
events.forEach(config => {
|
|
121
|
-
let
|
|
122
|
-
|
|
121
|
+
let eventConfig = config.eventConfig;
|
|
122
|
+
let routerTag = getSvmEventId(eventConfig.programId, eventConfig.discriminator);
|
|
123
|
+
addOrThrow$1(router, routerTag, config, eventConfig.contractName, config.isWildcard, eventConfig.name, chain);
|
|
123
124
|
});
|
|
124
125
|
let byProgram = {};
|
|
125
126
|
events.forEach(config => {
|
|
126
|
-
let
|
|
127
|
+
let eventConfig = config.eventConfig;
|
|
128
|
+
let key = eventConfig.programId;
|
|
127
129
|
let s = byProgram[key];
|
|
128
130
|
let set;
|
|
129
131
|
if (s !== undefined) {
|
|
@@ -133,7 +135,7 @@ function fromSvmEventConfigsOrThrow(events, chain) {
|
|
|
133
135
|
byProgram[key] = s$1;
|
|
134
136
|
set = s$1;
|
|
135
137
|
}
|
|
136
|
-
set.add(
|
|
138
|
+
set.add(eventConfig.discriminatorByteLen);
|
|
137
139
|
});
|
|
138
140
|
let ordering = Object.entries(byProgram).map(param => {
|
|
139
141
|
let sorted = Array.from(param[1]).toSorted((a, b) => b - a | 0);
|
package/src/sources/Evm.res
CHANGED
|
@@ -10,7 +10,9 @@ type payload = {
|
|
|
10
10
|
srcAddress: Address.t,
|
|
11
11
|
logIndex: int,
|
|
12
12
|
transaction?: Internal.eventTransaction,
|
|
13
|
-
block
|
|
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,
|
|
14
16
|
}
|
|
15
17
|
external fromPayload: payload => Internal.eventPayload = "%identity"
|
|
16
18
|
external toPayload: Internal.eventPayload => payload = "%identity"
|
|
@@ -27,6 +29,42 @@ let transactionFields =
|
|
|
27
29
|
// per event at config build and cached on the event config.
|
|
28
30
|
let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields)
|
|
29
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
|
+
|
|
30
68
|
let cleanUpRawEventFieldsInPlace: JSON.t => unit = %raw(`fields => {
|
|
31
69
|
delete fields.hash
|
|
32
70
|
delete fields.number
|
|
@@ -63,8 +101,8 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
63
101
|
Logging.createChildFrom(
|
|
64
102
|
~logger,
|
|
65
103
|
~params={
|
|
66
|
-
"contract": eventItem.eventConfig.contractName,
|
|
67
|
-
"event": eventItem.eventConfig.name,
|
|
104
|
+
"contract": eventItem.onEventRegistration.eventConfig.contractName,
|
|
105
|
+
"event": eventItem.onEventRegistration.eventConfig.name,
|
|
68
106
|
"chainId": eventItem.chain->ChainMap.Chain.toChainId,
|
|
69
107
|
"block": eventItem.blockNumber,
|
|
70
108
|
"logIndex": eventItem.logIndex,
|
|
@@ -74,7 +112,17 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
74
112
|
toRawEvent: eventItem => {
|
|
75
113
|
let payload = eventItem.payload->toPayload
|
|
76
114
|
let eventConfig =
|
|
77
|
-
eventItem.eventConfig->(
|
|
115
|
+
eventItem.onEventRegistration.eventConfig->(
|
|
116
|
+
Utils.magic: Internal.eventConfig => Internal.evmEventConfig
|
|
117
|
+
)
|
|
118
|
+
// Store-backed payloads get `block` written at batch prep and inline
|
|
119
|
+
// sources carry it from the start, with hash/timestamp always selected —
|
|
120
|
+
// so both are present by the time a raw event is built.
|
|
121
|
+
let header = switch payload.block {
|
|
122
|
+
| Some(block) => block->(Utils.magic: Internal.eventBlock => {"hash": string, "timestamp": int})
|
|
123
|
+
| None =>
|
|
124
|
+
JsError.throwWithMessage("Unexpected case: The event block is missing for a raw event")
|
|
125
|
+
}
|
|
78
126
|
eventItem->RawEvent.make(
|
|
79
127
|
~block=payload.block,
|
|
80
128
|
~transaction=payload.transaction,
|
|
@@ -84,6 +132,8 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
84
132
|
? ()->(Utils.magic: unit => Internal.eventParams)
|
|
85
133
|
: payload.params,
|
|
86
134
|
~srcAddress=payload.srcAddress,
|
|
135
|
+
~blockHash=header["hash"],
|
|
136
|
+
~blockTimestamp=header["timestamp"],
|
|
87
137
|
~cleanUpRawEventFieldsInPlace,
|
|
88
138
|
)
|
|
89
139
|
},
|
package/src/sources/Evm.res.mjs
CHANGED
|
@@ -3,11 +3,46 @@
|
|
|
3
3
|
import * as Logging from "../Logging.res.mjs";
|
|
4
4
|
import * as Internal from "../Internal.res.mjs";
|
|
5
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";
|
|
6
9
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
7
10
|
import * as TransactionStore from "./TransactionStore.res.mjs";
|
|
8
11
|
|
|
9
12
|
let eventTransactionFieldMask = TransactionStore.makeMaskFn(Internal.allEvmTransactionFields);
|
|
10
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);
|
|
45
|
+
|
|
11
46
|
let cleanUpRawEventFieldsInPlace = (fields => {
|
|
12
47
|
delete fields.hash
|
|
13
48
|
delete fields.number
|
|
@@ -27,8 +62,8 @@ function make(logger) {
|
|
|
27
62
|
logger: logger,
|
|
28
63
|
toEvent: eventItem => eventItem.payload,
|
|
29
64
|
toEventLogger: eventItem => Logging.createChildFrom(logger, {
|
|
30
|
-
contract: eventItem.eventConfig.contractName,
|
|
31
|
-
event: eventItem.eventConfig.name,
|
|
65
|
+
contract: eventItem.onEventRegistration.eventConfig.contractName,
|
|
66
|
+
event: eventItem.onEventRegistration.eventConfig.name,
|
|
32
67
|
chainId: eventItem.chain,
|
|
33
68
|
block: eventItem.blockNumber,
|
|
34
69
|
logIndex: eventItem.logIndex,
|
|
@@ -36,8 +71,10 @@ function make(logger) {
|
|
|
36
71
|
}),
|
|
37
72
|
toRawEvent: eventItem => {
|
|
38
73
|
let payload = eventItem.payload;
|
|
39
|
-
let eventConfig = eventItem.eventConfig;
|
|
40
|
-
|
|
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);
|
|
41
78
|
}
|
|
42
79
|
};
|
|
43
80
|
}
|
|
@@ -47,6 +84,8 @@ let transactionFields = Internal.allEvmTransactionFields;
|
|
|
47
84
|
export {
|
|
48
85
|
transactionFields,
|
|
49
86
|
eventTransactionFieldMask,
|
|
87
|
+
blockFields,
|
|
88
|
+
eventBlockFieldMask,
|
|
50
89
|
cleanUpRawEventFieldsInPlace,
|
|
51
90
|
make,
|
|
52
91
|
}
|
package/src/sources/EvmChain.res
CHANGED
|
@@ -39,39 +39,35 @@ let getSyncConfig = (
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
let collectEventParams = (
|
|
42
|
+
let collectEventParams = (onEventRegistrations: array<Internal.evmOnEventRegistration>): array<
|
|
43
43
|
HyperSyncClient.Decoder.eventParamsInput,
|
|
44
44
|
> => {
|
|
45
45
|
let result = []
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
})
|
|
56
|
-
->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,
|
|
57
55
|
})
|
|
56
|
+
->ignore
|
|
58
57
|
})
|
|
59
58
|
result
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
let makeSources = (
|
|
63
62
|
~chain,
|
|
64
|
-
~
|
|
63
|
+
~onEventRegistrations: array<Internal.evmOnEventRegistration>,
|
|
65
64
|
~hyperSync,
|
|
66
65
|
~rpcs: array<rpc>,
|
|
67
66
|
~lowercaseAddresses,
|
|
68
67
|
) => {
|
|
69
|
-
let eventRouter =
|
|
70
|
-
contracts
|
|
71
|
-
->Array.flatMap(contract => contract.events)
|
|
72
|
-
->EventRouter.fromEvmEventModsOrThrow(~chain)
|
|
68
|
+
let eventRouter = onEventRegistrations->EventRouter.fromEvmEventModsOrThrow(~chain)
|
|
73
69
|
|
|
74
|
-
let allEventParams = collectEventParams(
|
|
70
|
+
let allEventParams = collectEventParams(onEventRegistrations)
|
|
75
71
|
|
|
76
72
|
let sources = switch hyperSync {
|
|
77
73
|
| Some(endpointUrl) => [
|
|
@@ -20,25 +20,24 @@ function getSyncConfig(param) {
|
|
|
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,
|
|
@@ -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`)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function fieldCodes(fields) {
|
|
5
|
+
let codes = {};
|
|
6
|
+
fields.forEach((name, i) => {
|
|
7
|
+
codes[name] = i;
|
|
8
|
+
});
|
|
9
|
+
return codes;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let pow2 = (c => Math.pow(2, c));
|
|
13
|
+
|
|
14
|
+
function maskFromFields(selectedFields, codes) {
|
|
15
|
+
let mask = {
|
|
16
|
+
contents: 0
|
|
17
|
+
};
|
|
18
|
+
selectedFields.forEach(name => {
|
|
19
|
+
let code = codes[name];
|
|
20
|
+
if (code !== undefined) {
|
|
21
|
+
mask.contents = mask.contents + pow2(code);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return mask.contents;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function makeMaskFn(fields) {
|
|
29
|
+
let codes = fieldCodes(fields);
|
|
30
|
+
return selectedFields => maskFromFields(selectedFields, codes);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let orMask = ((a, b) => (a | b) >>> 0);
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
fieldCodes,
|
|
37
|
+
pow2,
|
|
38
|
+
maskFromFields,
|
|
39
|
+
makeMaskFn,
|
|
40
|
+
orMask,
|
|
41
|
+
}
|
|
42
|
+
/* No side effect */
|
package/src/sources/Fuel.res
CHANGED
|
@@ -44,8 +44,8 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
44
44
|
Logging.createChildFrom(
|
|
45
45
|
~logger,
|
|
46
46
|
~params={
|
|
47
|
-
"contract": eventItem.eventConfig.contractName,
|
|
48
|
-
"event": eventItem.eventConfig.name,
|
|
47
|
+
"contract": eventItem.onEventRegistration.eventConfig.contractName,
|
|
48
|
+
"event": eventItem.onEventRegistration.eventConfig.name,
|
|
49
49
|
"chainId": eventItem.chain->ChainMap.Chain.toChainId,
|
|
50
50
|
"block": eventItem.blockNumber,
|
|
51
51
|
"logIndex": eventItem.logIndex,
|
|
@@ -54,11 +54,14 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
54
54
|
),
|
|
55
55
|
toRawEvent: eventItem => {
|
|
56
56
|
let payload = eventItem.payload->toPayload
|
|
57
|
+
let header = payload.block->(Utils.magic: Internal.eventBlock => {"id": string, "time": int})
|
|
57
58
|
eventItem->RawEvent.make(
|
|
58
59
|
~block=payload.block,
|
|
59
60
|
~transaction=payload.transaction,
|
|
60
61
|
~params=payload.params,
|
|
61
62
|
~srcAddress=payload.srcAddress,
|
|
63
|
+
~blockHash=header["id"],
|
|
64
|
+
~blockTimestamp=header["time"],
|
|
62
65
|
~cleanUpRawEventFieldsInPlace,
|
|
63
66
|
)
|
|
64
67
|
},
|
package/src/sources/Fuel.res.mjs
CHANGED
|
@@ -23,8 +23,8 @@ function make(logger) {
|
|
|
23
23
|
logger: logger,
|
|
24
24
|
toEvent: eventItem => eventItem.payload,
|
|
25
25
|
toEventLogger: eventItem => Logging.createChildFrom(logger, {
|
|
26
|
-
contract: eventItem.eventConfig.contractName,
|
|
27
|
-
event: eventItem.eventConfig.name,
|
|
26
|
+
contract: eventItem.onEventRegistration.eventConfig.contractName,
|
|
27
|
+
event: eventItem.onEventRegistration.eventConfig.name,
|
|
28
28
|
chainId: eventItem.chain,
|
|
29
29
|
block: eventItem.blockNumber,
|
|
30
30
|
logIndex: eventItem.logIndex,
|
|
@@ -32,7 +32,8 @@ function make(logger) {
|
|
|
32
32
|
}),
|
|
33
33
|
toRawEvent: eventItem => {
|
|
34
34
|
let payload = eventItem.payload;
|
|
35
|
-
|
|
35
|
+
let header = payload.block;
|
|
36
|
+
return RawEvent.make(eventItem, payload.block, payload.transaction, payload.params, payload.srcAddress, header.id, header.time, cleanUpRawEventFieldsInPlace);
|
|
36
37
|
}
|
|
37
38
|
};
|
|
38
39
|
}
|
|
@@ -13,7 +13,7 @@ type selectionConfig = {
|
|
|
13
13
|
getRecieptsSelection: (
|
|
14
14
|
~addressesByContractName: dict<array<Address.t>>,
|
|
15
15
|
) => array<HyperFuelClient.QueryTypes.receiptSelection>,
|
|
16
|
-
eventRouter: EventRouter.t<Internal.
|
|
16
|
+
eventRouter: EventRouter.t<Internal.fuelOnEventRegistration>,
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
let logDataReceiptTypeSelection: array<FuelSDK.receiptType> = [LogData]
|
|
@@ -127,39 +127,40 @@ let getSelectionConfig = (selection: FetchState.selection, ~chain) => {
|
|
|
127
127
|
|
|
128
128
|
let contractNames = Utils.Set.make()
|
|
129
129
|
|
|
130
|
-
selection.
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
selection.onEventRegistrations->Array.forEach(reg => {
|
|
131
|
+
let eventConfig =
|
|
132
|
+
reg.eventConfig->(Utils.magic: Internal.eventConfig => Internal.fuelEventConfig)
|
|
133
133
|
let contractName = eventConfig.contractName
|
|
134
|
-
|
|
134
|
+
let isWildcard = reg.isWildcard
|
|
135
|
+
if !isWildcard {
|
|
135
136
|
let _ = contractNames->Utils.Set.add(contractName)
|
|
136
137
|
}
|
|
137
138
|
eventRouter->EventRouter.addOrThrow(
|
|
138
139
|
eventConfig.id,
|
|
139
|
-
|
|
140
|
+
reg,
|
|
140
141
|
~contractName,
|
|
141
142
|
~eventName=eventConfig.name,
|
|
142
143
|
~chain,
|
|
143
|
-
~isWildcard
|
|
144
|
+
~isWildcard,
|
|
144
145
|
)
|
|
145
146
|
|
|
146
|
-
switch eventConfig {
|
|
147
|
-
|
|
|
148
|
-
|
|
|
149
|
-
|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
147
|
+
switch (eventConfig.kind, isWildcard) {
|
|
148
|
+
| (Mint, true) => addNonLogDataWildcardReceiptTypes(Mint)
|
|
149
|
+
| (Mint, false) => addNonLogDataReceiptType(contractName, Mint)
|
|
150
|
+
| (Burn, true) => addNonLogDataWildcardReceiptTypes(Burn)
|
|
151
|
+
| (Burn, false) => addNonLogDataReceiptType(contractName, Burn)
|
|
152
|
+
| (Transfer, true) => {
|
|
152
153
|
addNonLogDataWildcardReceiptTypes(Transfer)
|
|
153
154
|
addNonLogDataWildcardReceiptTypes(TransferOut)
|
|
154
155
|
}
|
|
155
|
-
|
|
|
156
|
+
| (Transfer, false) => {
|
|
156
157
|
addNonLogDataReceiptType(contractName, Transfer)
|
|
157
158
|
addNonLogDataReceiptType(contractName, TransferOut)
|
|
158
159
|
}
|
|
159
|
-
|
|
|
160
|
-
|
|
|
160
|
+
| (Call, true) => addNonLogDataWildcardReceiptTypes(Call)
|
|
161
|
+
| (Call, false) =>
|
|
161
162
|
JsError.throwWithMessage("Call receipt indexing currently supported only in wildcard mode")
|
|
162
|
-
|
|
|
163
|
+
| (LogData({logId}), _) => {
|
|
163
164
|
let rb = logId->BigInt.fromStringOrThrow
|
|
164
165
|
if isWildcard {
|
|
165
166
|
wildcardLogDataRbs->Array.push(rb)->ignore
|
|
@@ -239,6 +240,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
239
240
|
~knownHeight,
|
|
240
241
|
~partitionId as _,
|
|
241
242
|
~selection: FetchState.selection,
|
|
243
|
+
~itemsTarget as _,
|
|
242
244
|
~retry,
|
|
243
245
|
~logger,
|
|
244
246
|
) => {
|
|
@@ -250,11 +252,6 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
250
252
|
let startFetchingBatchTimeRef = Performance.now()
|
|
251
253
|
|
|
252
254
|
//fetch batch
|
|
253
|
-
Prometheus.SourceRequestCount.increment(
|
|
254
|
-
~sourceName=name,
|
|
255
|
-
~chainId=chain->ChainMap.Chain.toChainId,
|
|
256
|
-
~method="getLogs",
|
|
257
|
-
)
|
|
258
255
|
let pageUnsafe = try await HyperFuel.GetLogs.query(
|
|
259
256
|
~client,
|
|
260
257
|
~fromBlock,
|
|
@@ -306,6 +303,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
306
303
|
}
|
|
307
304
|
|
|
308
305
|
let pageFetchTime = startFetchingBatchTimeRef->Performance.secondsSince
|
|
306
|
+
let requestStats = [{Source.method: "getLogs", seconds: pageFetchTime}]
|
|
309
307
|
|
|
310
308
|
//set height and next from block
|
|
311
309
|
let knownHeight = pageUnsafe.archiveHeight
|
|
@@ -330,7 +328,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
330
328
|
| Call(_) => callEventTag
|
|
331
329
|
}
|
|
332
330
|
|
|
333
|
-
let
|
|
331
|
+
let onEventRegistration = switch selectionConfig.eventRouter->EventRouter.get(
|
|
334
332
|
~tag=eventId,
|
|
335
333
|
~contractNameByAddress,
|
|
336
334
|
~contractAddress,
|
|
@@ -351,9 +349,14 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
351
349
|
~logger,
|
|
352
350
|
)
|
|
353
351
|
}
|
|
354
|
-
| Some(
|
|
352
|
+
| Some(onEventRegistration) => onEventRegistration
|
|
355
353
|
}
|
|
356
354
|
|
|
355
|
+
let eventConfig =
|
|
356
|
+
onEventRegistration.eventConfig->(
|
|
357
|
+
Utils.magic: Internal.eventConfig => Internal.fuelEventConfig
|
|
358
|
+
)
|
|
359
|
+
|
|
357
360
|
let params = switch (eventConfig, receipt) {
|
|
358
361
|
| ({kind: LogData({decode})}, LogData({data})) =>
|
|
359
362
|
try decode(data) catch {
|
|
@@ -407,11 +410,9 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
407
410
|
}
|
|
408
411
|
|
|
409
412
|
Internal.Event({
|
|
410
|
-
|
|
411
|
-
timestamp: block.time,
|
|
413
|
+
onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
|
|
412
414
|
chain,
|
|
413
415
|
blockNumber: block.height,
|
|
414
|
-
blockHash: block.id,
|
|
415
416
|
logIndex: receiptIndex,
|
|
416
417
|
// Fuel carries the transaction inline on the payload; the store key is
|
|
417
418
|
// unused (Fuel identifies transactions by hash, kept on the payload).
|
|
@@ -458,13 +459,15 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
458
459
|
{
|
|
459
460
|
latestFetchedBlockTimestamp,
|
|
460
461
|
parsedQueueItems,
|
|
461
|
-
// Fuel keeps transaction on the payload; no store
|
|
462
|
+
// Fuel keeps transaction and block on the payload; no store pages.
|
|
462
463
|
transactionStore: None,
|
|
464
|
+
blockStore: None,
|
|
463
465
|
latestFetchedBlockNumber: heighestBlockQueried,
|
|
464
466
|
stats,
|
|
465
467
|
knownHeight,
|
|
466
468
|
blockHashes,
|
|
467
469
|
fromBlockQueried: fromBlock,
|
|
470
|
+
requestStats,
|
|
468
471
|
}
|
|
469
472
|
}
|
|
470
473
|
|
|
@@ -492,18 +495,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
492
495
|
}
|
|
493
496
|
}
|
|
494
497
|
let seconds = timerRef->Performance.secondsSince
|
|
495
|
-
|
|
496
|
-
~sourceName=name,
|
|
497
|
-
~chainId=chain->ChainMap.Chain.toChainId,
|
|
498
|
-
~method="getHeight",
|
|
499
|
-
)
|
|
500
|
-
Prometheus.SourceRequestCount.addSeconds(
|
|
501
|
-
~sourceName=name,
|
|
502
|
-
~chainId=chain->ChainMap.Chain.toChainId,
|
|
503
|
-
~method="getHeight",
|
|
504
|
-
~seconds,
|
|
505
|
-
)
|
|
506
|
-
height
|
|
498
|
+
{height, requestStats: [{method: "getHeight", seconds}]}
|
|
507
499
|
},
|
|
508
500
|
getItemsOrThrow,
|
|
509
501
|
}
|