envio 3.3.0-alpha.4 → 3.3.0-alpha.6
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/package.json +7 -7
- package/src/Batch.res.mjs +1 -1
- package/src/BatchProcessing.res +5 -0
- package/src/BatchProcessing.res.mjs +6 -0
- package/src/ChainState.res +41 -7
- package/src/ChainState.res.mjs +22 -18
- package/src/ChainState.resi +3 -1
- package/src/Config.res +3 -0
- package/src/Config.res.mjs +3 -1
- package/src/Core.res +0 -3
- package/src/ExitOnCaughtUp.res +10 -2
- package/src/ExitOnCaughtUp.res.mjs +10 -1
- package/src/FetchState.res +63 -133
- package/src/FetchState.res.mjs +107 -166
- package/src/IndexerState.res +4 -0
- package/src/IndexerState.res.mjs +8 -1
- package/src/IndexerState.resi +1 -0
- package/src/IndexingAddresses.res +105 -0
- package/src/IndexingAddresses.res.mjs +100 -0
- package/src/IndexingAddresses.resi +32 -0
- package/src/Main.res +4 -15
- package/src/Main.res.mjs +6 -14
- package/src/Metrics.res +33 -0
- package/src/Metrics.res.mjs +39 -0
- package/src/Prometheus.res +2 -20
- package/src/Prometheus.res.mjs +83 -95
- package/src/SimulateDeadInputTracker.res +85 -0
- package/src/SimulateDeadInputTracker.res.mjs +73 -0
- package/src/SimulateDeadInputTracker.resi +12 -0
- package/src/SimulateItems.res +23 -42
- package/src/SimulateItems.res.mjs +12 -30
- package/src/TestIndexer.res +3 -27
- package/src/TestIndexer.res.mjs +2 -9
- package/src/bindings/Viem.res +0 -41
- package/src/bindings/Viem.res.mjs +1 -43
- package/src/sources/Evm.res +7 -36
- package/src/sources/Evm.res.mjs +4 -36
- package/src/sources/EvmChain.res +4 -2
- package/src/sources/EvmChain.res.mjs +3 -2
- package/src/sources/EvmRpcClient.res +36 -5
- package/src/sources/EvmRpcClient.res.mjs +7 -4
- package/src/sources/HyperSyncClient.res +0 -35
- package/src/sources/HyperSyncClient.res.mjs +1 -8
- package/src/sources/Rpc.res +15 -47
- package/src/sources/Rpc.res.mjs +25 -56
- package/src/sources/RpcSource.res +289 -204
- package/src/sources/RpcSource.res.mjs +293 -303
- package/src/sources/SimulateSource.res +1 -0
- package/src/sources/SimulateSource.res.mjs +2 -1
- package/src/sources/Source.res +4 -0
- package/src/sources/Svm.res +7 -15
- package/src/sources/Svm.res.mjs +4 -15
- package/src/sources/TransactionStore.res +14 -2
- package/src/sources/TransactionStore.res.mjs +10 -2
|
@@ -165,34 +165,6 @@ function deriveSrcAddress(providedSrcAddress, eventConfig, chainConfig) {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
function validateSrcAddresses(simulateItems, config, chainConfig, indexingAddresses) {
|
|
169
|
-
let known = new Set();
|
|
170
|
-
indexingAddresses.forEach(ia => {
|
|
171
|
-
known.add(ia.address);
|
|
172
|
-
});
|
|
173
|
-
simulateItems.forEach(rawJson => {
|
|
174
|
-
let match = rawJson.contract;
|
|
175
|
-
let match$1 = rawJson.event;
|
|
176
|
-
if (match === undefined) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (match$1 === undefined) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
let eventConfig = findEventConfig(config, match, match$1);
|
|
183
|
-
if (eventConfig === undefined) {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
if (eventConfig.isWildcard) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
let srcAddress = deriveSrcAddress(rawJson.srcAddress, eventConfig, chainConfig);
|
|
190
|
-
if (!known.has(srcAddress)) {
|
|
191
|
-
return Stdlib_JsError.throwWithMessage(`simulate: ` + match + `.` + match$1 + ` resolved to address ` + srcAddress + `, which isn't indexed on chain ` + chainConfig.id.toString() + `. ` + (`Provide a "srcAddress" configured or registered for ` + match + ` on this chain, or use a wildcard event.`));
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
168
|
function parse(simulateItems, config, chainConfig) {
|
|
197
169
|
let chain = ChainMap.Chain.makeUnsafe(chainConfig.id);
|
|
198
170
|
let chainId = chainConfig.id;
|
|
@@ -204,7 +176,8 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
204
176
|
contents: 0
|
|
205
177
|
};
|
|
206
178
|
let items = [];
|
|
207
|
-
|
|
179
|
+
let seenCoordinates = {};
|
|
180
|
+
simulateItems.forEach((rawJson, itemIndex) => {
|
|
208
181
|
let match = rawJson.contract;
|
|
209
182
|
let match$1 = rawJson.event;
|
|
210
183
|
if (match === undefined) {
|
|
@@ -221,6 +194,9 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
221
194
|
let li = rawJson.logIndex;
|
|
222
195
|
let logIndex;
|
|
223
196
|
if (li !== undefined) {
|
|
197
|
+
if (li >= currentLogIndex.contents) {
|
|
198
|
+
currentLogIndex.contents = li + 1 | 0;
|
|
199
|
+
}
|
|
224
200
|
logIndex = li;
|
|
225
201
|
} else {
|
|
226
202
|
let li$1 = currentLogIndex.contents;
|
|
@@ -272,6 +248,13 @@ function parse(simulateItems, config, chainConfig) {
|
|
|
272
248
|
break;
|
|
273
249
|
}
|
|
274
250
|
currentBlock.contents = blockNumber;
|
|
251
|
+
let coordinate = blockNumber.toString() + `:` + logIndex.toString();
|
|
252
|
+
let firstIndex = seenCoordinates[coordinate];
|
|
253
|
+
if (firstIndex !== undefined) {
|
|
254
|
+
Stdlib_JsError.throwWithMessage(`simulate: items at index ` + firstIndex.toString() + ` and ` + itemIndex.toString() + ` on chain ` + chainId.toString() + ` both resolve to block ` + blockNumber.toString() + `, logIndex ` + logIndex.toString() + `. Give each item a distinct logIndex (or omit logIndex so they auto-increment).`);
|
|
255
|
+
} else {
|
|
256
|
+
seenCoordinates[coordinate] = itemIndex;
|
|
257
|
+
}
|
|
275
258
|
items.push({
|
|
276
259
|
kind: 0,
|
|
277
260
|
eventConfig: eventConfig,
|
|
@@ -360,7 +343,6 @@ export {
|
|
|
360
343
|
dummySrcAddress,
|
|
361
344
|
firstContractAddress,
|
|
362
345
|
deriveSrcAddress,
|
|
363
|
-
validateSrcAddresses,
|
|
364
346
|
parse,
|
|
365
347
|
patchConfig,
|
|
366
348
|
}
|
package/src/TestIndexer.res
CHANGED
|
@@ -667,11 +667,7 @@ let makeCreateTestIndexer = (~config: Config.t, ~workerPath: string): (
|
|
|
667
667
|
Int.compare(aId, bId)
|
|
668
668
|
})
|
|
669
669
|
|
|
670
|
-
// Parse and validate the block ranges upfront before starting any
|
|
671
|
-
// workers. srcAddress validation can't run here: it depends on which
|
|
672
|
-
// events are wildcard, and `config` reflects config.yaml only —
|
|
673
|
-
// handler-level `wildcard: true` takes effect only once registrations
|
|
674
|
-
// are applied.
|
|
670
|
+
// Parse and validate the block ranges upfront before starting any workers.
|
|
675
671
|
let chainEntries = sortedChainKeys->Array.map(chainIdStr => {
|
|
676
672
|
let rawChainConfig = rawChains->Dict.getUnsafe(chainIdStr)
|
|
677
673
|
let chainId = switch chainIdStr->Int.fromString {
|
|
@@ -868,26 +864,6 @@ let initTestWorker = () => {
|
|
|
868
864
|
}
|
|
869
865
|
|
|
870
866
|
let patchConfig = (config: Config.t, _registrations) => {
|
|
871
|
-
// `config` here has handler registrations applied, so `isWildcard`
|
|
872
|
-
// reflects `indexer.onEvent({ wildcard: true })` — which the main thread
|
|
873
|
-
// can't see. Validate srcAddresses against it before patching in the
|
|
874
|
-
// simulate source.
|
|
875
|
-
switch simulate {
|
|
876
|
-
| Some(simulateItems) =>
|
|
877
|
-
let chainConfig = config.chainMap->ChainMap.get(ChainMap.Chain.makeUnsafe(~chainId))
|
|
878
|
-
let indexingAddresses =
|
|
879
|
-
initialState.chains
|
|
880
|
-
->Array.find(c => c.id == chainId)
|
|
881
|
-
->Option.mapOr([], c => c.indexingAddresses)
|
|
882
|
-
SimulateItems.validateSrcAddresses(
|
|
883
|
-
~simulateItems,
|
|
884
|
-
~config,
|
|
885
|
-
~chainConfig,
|
|
886
|
-
~indexingAddresses,
|
|
887
|
-
)
|
|
888
|
-
| None => ()
|
|
889
|
-
}
|
|
890
|
-
|
|
891
867
|
let config = SimulateItems.patchConfig(~config, ~processConfig)
|
|
892
868
|
|
|
893
869
|
// In auto-exit mode, set batchSize=1 to process one block checkpoint at a time
|
|
@@ -900,8 +876,8 @@ let initTestWorker = () => {
|
|
|
900
876
|
Main.start(~persistence, ~isTest=true, ~patchConfig, ~exitAfterFirstEventBlock)
|
|
901
877
|
->Promise.catch(exn => {
|
|
902
878
|
// `Main.start` rejects on any fatal error: a runtime failure arrives wrapped
|
|
903
|
-
// in `Main.FatalError` (already logged), a setup throw (e.g.
|
|
904
|
-
//
|
|
879
|
+
// in `Main.FatalError` (already logged), a setup throw (e.g. an invalid
|
|
880
|
+
// simulate item) arrives raw. The parent only learns of failures
|
|
905
881
|
// through the worker `error` event, which fires on an *uncaught* exception.
|
|
906
882
|
// Throwing synchronously in this catch would just reject the catch's own
|
|
907
883
|
// promise (swallowed by `ignore`); `setImmediate` re-throws outside the
|
package/src/TestIndexer.res.mjs
CHANGED
|
@@ -586,11 +586,9 @@ function initTestWorker() {
|
|
|
586
586
|
Process.exit(1);
|
|
587
587
|
return;
|
|
588
588
|
}
|
|
589
|
-
let initialState = workerData.initialState;
|
|
590
589
|
let simulate = workerData.simulate;
|
|
591
590
|
let endBlock = workerData.endBlock;
|
|
592
|
-
let
|
|
593
|
-
let chainIdStr = chainId.toString();
|
|
591
|
+
let chainIdStr = workerData.chainId.toString();
|
|
594
592
|
let exitAfterFirstEventBlock = Stdlib_Option.isNone(endBlock);
|
|
595
593
|
let resolvedChainDict = {};
|
|
596
594
|
resolvedChainDict["startBlock"] = workerData.startBlock;
|
|
@@ -605,7 +603,7 @@ function initTestWorker() {
|
|
|
605
603
|
let processConfig = {
|
|
606
604
|
chains: resolvedChainsDict
|
|
607
605
|
};
|
|
608
|
-
let proxy = TestIndexerProxyStorage.make(parentPort, initialState);
|
|
606
|
+
let proxy = TestIndexerProxyStorage.make(parentPort, workerData.initialState);
|
|
609
607
|
let storage = TestIndexerProxyStorage.makeStorage(proxy);
|
|
610
608
|
let config = Config.loadWithoutRegistrations();
|
|
611
609
|
let persistence = Persistence.make(config.userEntities, config.allEnums, storage);
|
|
@@ -615,11 +613,6 @@ function initTestWorker() {
|
|
|
615
613
|
Logging.setLogLevel("silent");
|
|
616
614
|
}
|
|
617
615
|
let patchConfig = (config, _registrations) => {
|
|
618
|
-
if (simulate !== undefined) {
|
|
619
|
-
let chainConfig = ChainMap.get(config.chainMap, ChainMap.Chain.makeUnsafe(chainId));
|
|
620
|
-
let indexingAddresses = Stdlib_Option.mapOr(initialState.chains.find(c => c.id === chainId), [], c => c.indexingAddresses);
|
|
621
|
-
SimulateItems.validateSrcAddresses(simulate, config, chainConfig, indexingAddresses);
|
|
622
|
-
}
|
|
623
616
|
let config$1 = SimulateItems.patchConfig(config, processConfig);
|
|
624
617
|
if (exitAfterFirstEventBlock) {
|
|
625
618
|
return {
|
package/src/bindings/Viem.res
CHANGED
|
@@ -1,52 +1,11 @@
|
|
|
1
|
-
type eventLog = {
|
|
2
|
-
abi: EvmTypes.Abi.t,
|
|
3
|
-
data: string,
|
|
4
|
-
topics: array<EvmTypes.Hex.t>,
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
type decodedEvent<'a> = {
|
|
8
|
-
eventName: string,
|
|
9
|
-
args: 'a,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
@module("viem") external decodeEventLogOrThrow: eventLog => decodedEvent<'a> = "decodeEventLog"
|
|
13
|
-
|
|
14
1
|
type hex = EvmTypes.Hex.t
|
|
15
2
|
@module("viem") external toHex: 'a => hex = "toHex"
|
|
16
3
|
@module("viem") external keccak256: hex => hex = "keccak256"
|
|
17
|
-
@module("viem") external keccak256Bytes: Uint8Array.t => hex = "keccak256"
|
|
18
4
|
@module("viem") external pad: hex => hex = "pad"
|
|
19
|
-
@module("viem")
|
|
20
|
-
external encodePacked: (~types: array<string>, ~values: array<'a>) => hex = "encodePacked"
|
|
21
5
|
|
|
22
6
|
type sizeOptions = {size: int}
|
|
23
|
-
@module("viem") external intToHex: (int, ~options: sizeOptions=?) => hex = "numberToHex"
|
|
24
7
|
@module("viem") external bigintToHex: (bigint, ~options: sizeOptions=?) => hex = "numberToHex"
|
|
25
8
|
@module("viem") external stringToHex: (string, ~options: sizeOptions=?) => hex = "stringToHex"
|
|
26
9
|
@module("viem") external boolToHex: (bool, ~options: sizeOptions=?) => hex = "boolToHex"
|
|
27
10
|
@module("viem") external bytesToHex: (Uint8Array.t, ~options: sizeOptions=?) => hex = "bytesToHex"
|
|
28
11
|
@module("viem") external concat: array<hex> => hex = "concat"
|
|
29
|
-
|
|
30
|
-
exception ParseError(exn)
|
|
31
|
-
exception UnknownContractName({contractName: string})
|
|
32
|
-
|
|
33
|
-
let parseLogOrThrow = (
|
|
34
|
-
contractNameAbiMapping: dict<EvmTypes.Abi.t>,
|
|
35
|
-
~contractName,
|
|
36
|
-
~topics,
|
|
37
|
-
~data,
|
|
38
|
-
) => {
|
|
39
|
-
switch contractNameAbiMapping->Utils.Dict.dangerouslyGetNonOption(contractName) {
|
|
40
|
-
| None => throw(UnknownContractName({contractName: contractName}))
|
|
41
|
-
| Some(abi) =>
|
|
42
|
-
let viemLog: eventLog = {
|
|
43
|
-
abi,
|
|
44
|
-
data,
|
|
45
|
-
topics,
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
try viemLog->decodeEventLogOrThrow catch {
|
|
49
|
-
| exn => throw(ParseError(exn))
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,44 +1,2 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Viem from "viem";
|
|
4
|
-
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
5
|
-
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
6
|
-
|
|
7
|
-
let ParseError = /* @__PURE__ */Primitive_exceptions.create("Viem.ParseError");
|
|
8
|
-
|
|
9
|
-
let UnknownContractName = /* @__PURE__ */Primitive_exceptions.create("Viem.UnknownContractName");
|
|
10
|
-
|
|
11
|
-
function parseLogOrThrow(contractNameAbiMapping, contractName, topics, data) {
|
|
12
|
-
let abi = contractNameAbiMapping[contractName];
|
|
13
|
-
if (abi !== undefined) {
|
|
14
|
-
let viemLog_abi = Primitive_option.valFromOption(abi);
|
|
15
|
-
let viemLog = {
|
|
16
|
-
abi: viemLog_abi,
|
|
17
|
-
data: data,
|
|
18
|
-
topics: topics
|
|
19
|
-
};
|
|
20
|
-
try {
|
|
21
|
-
return Viem.decodeEventLog(viemLog);
|
|
22
|
-
} catch (raw_exn) {
|
|
23
|
-
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
24
|
-
throw {
|
|
25
|
-
RE_EXN_ID: ParseError,
|
|
26
|
-
_1: exn,
|
|
27
|
-
Error: new Error()
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
} else {
|
|
31
|
-
throw {
|
|
32
|
-
RE_EXN_ID: UnknownContractName,
|
|
33
|
-
contractName: contractName,
|
|
34
|
-
Error: new Error()
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export {
|
|
40
|
-
ParseError,
|
|
41
|
-
UnknownContractName,
|
|
42
|
-
parseLogOrThrow,
|
|
43
|
-
}
|
|
44
|
-
/* viem Not a pure module */
|
|
2
|
+
/* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
|
package/src/sources/Evm.res
CHANGED
|
@@ -15,42 +15,13 @@ type payload = {
|
|
|
15
15
|
external fromPayload: payload => Internal.eventPayload = "%identity"
|
|
16
16
|
external toPayload: Internal.eventPayload => payload = "%identity"
|
|
17
17
|
|
|
18
|
-
// Ordered transaction field names
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"gas",
|
|
26
|
-
"gasPrice",
|
|
27
|
-
"maxPriorityFeePerGas",
|
|
28
|
-
"maxFeePerGas",
|
|
29
|
-
"cumulativeGasUsed",
|
|
30
|
-
"effectiveGasPrice",
|
|
31
|
-
"gasUsed",
|
|
32
|
-
"input",
|
|
33
|
-
"nonce",
|
|
34
|
-
"value",
|
|
35
|
-
"v",
|
|
36
|
-
"r",
|
|
37
|
-
"s",
|
|
38
|
-
"contractAddress",
|
|
39
|
-
"logsBloom",
|
|
40
|
-
"root",
|
|
41
|
-
"status",
|
|
42
|
-
"yParity",
|
|
43
|
-
"maxFeePerBlobGas",
|
|
44
|
-
"blobVersionedHashes",
|
|
45
|
-
"type",
|
|
46
|
-
"l1Fee",
|
|
47
|
-
"l1GasPrice",
|
|
48
|
-
"l1GasUsed",
|
|
49
|
-
"l1FeeScalar",
|
|
50
|
-
"gasUsedForL1",
|
|
51
|
-
"accessList",
|
|
52
|
-
"authorizationList",
|
|
53
|
-
]
|
|
18
|
+
// Ordered transaction field names, the field codes shared with the Rust store
|
|
19
|
+
// (`EvmTxField`). Derived from the typed field list so the two can't drift;
|
|
20
|
+
// `Internal.allEvmTransactionFields` is pinned to the Rust ordinal order by a test.
|
|
21
|
+
let transactionFields =
|
|
22
|
+
Internal.allEvmTransactionFields->(
|
|
23
|
+
Utils.magic: array<Internal.evmTransactionField> => array<string>
|
|
24
|
+
)
|
|
54
25
|
|
|
55
26
|
// One event's selected transaction fields → store selection bitmask. Computed
|
|
56
27
|
// per event at config build and cached on the event config.
|
package/src/sources/Evm.res.mjs
CHANGED
|
@@ -1,46 +1,12 @@
|
|
|
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";
|
|
5
6
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
6
7
|
import * as TransactionStore from "./TransactionStore.res.mjs";
|
|
7
8
|
|
|
8
|
-
let
|
|
9
|
-
"transactionIndex",
|
|
10
|
-
"hash",
|
|
11
|
-
"from",
|
|
12
|
-
"to",
|
|
13
|
-
"gas",
|
|
14
|
-
"gasPrice",
|
|
15
|
-
"maxPriorityFeePerGas",
|
|
16
|
-
"maxFeePerGas",
|
|
17
|
-
"cumulativeGasUsed",
|
|
18
|
-
"effectiveGasPrice",
|
|
19
|
-
"gasUsed",
|
|
20
|
-
"input",
|
|
21
|
-
"nonce",
|
|
22
|
-
"value",
|
|
23
|
-
"v",
|
|
24
|
-
"r",
|
|
25
|
-
"s",
|
|
26
|
-
"contractAddress",
|
|
27
|
-
"logsBloom",
|
|
28
|
-
"root",
|
|
29
|
-
"status",
|
|
30
|
-
"yParity",
|
|
31
|
-
"maxFeePerBlobGas",
|
|
32
|
-
"blobVersionedHashes",
|
|
33
|
-
"type",
|
|
34
|
-
"l1Fee",
|
|
35
|
-
"l1GasPrice",
|
|
36
|
-
"l1GasUsed",
|
|
37
|
-
"l1FeeScalar",
|
|
38
|
-
"gasUsedForL1",
|
|
39
|
-
"accessList",
|
|
40
|
-
"authorizationList"
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
let eventTransactionFieldMask = TransactionStore.makeMaskFn(transactionFields);
|
|
9
|
+
let eventTransactionFieldMask = TransactionStore.makeMaskFn(Internal.allEvmTransactionFields);
|
|
44
10
|
|
|
45
11
|
let cleanUpRawEventFieldsInPlace = (fields => {
|
|
46
12
|
delete fields.hash
|
|
@@ -76,6 +42,8 @@ function make(logger) {
|
|
|
76
42
|
};
|
|
77
43
|
}
|
|
78
44
|
|
|
45
|
+
let transactionFields = Internal.allEvmTransactionFields;
|
|
46
|
+
|
|
79
47
|
export {
|
|
80
48
|
transactionFields,
|
|
81
49
|
eventTransactionFieldMask,
|
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,7 +32,7 @@ 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),
|
|
@@ -89,7 +90,7 @@ let makeSources = (
|
|
|
89
90
|
]
|
|
90
91
|
| _ => []
|
|
91
92
|
}
|
|
92
|
-
rpcs->Array.forEach(({?syncConfig, url, sourceFor, ?ws}) => {
|
|
93
|
+
rpcs->Array.forEach(({?syncConfig, url, sourceFor, ?ws, ?headers}) => {
|
|
93
94
|
let source = RpcSource.make({
|
|
94
95
|
chain,
|
|
95
96
|
sourceFor,
|
|
@@ -99,6 +100,7 @@ let makeSources = (
|
|
|
99
100
|
allEventParams,
|
|
100
101
|
lowercaseAddresses,
|
|
101
102
|
?ws,
|
|
103
|
+
?headers,
|
|
102
104
|
})
|
|
103
105
|
let _ = sources->Array.push(source)
|
|
104
106
|
})
|
|
@@ -13,7 +13,7 @@ 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)
|
|
@@ -60,7 +60,8 @@ function makeSources(chain, contracts, hyperSync, rpcs, lowercaseAddresses) {
|
|
|
60
60
|
eventRouter: eventRouter,
|
|
61
61
|
allEventParams: allEventParams,
|
|
62
62
|
lowercaseAddresses: lowercaseAddresses,
|
|
63
|
-
ws: param.ws
|
|
63
|
+
ws: param.ws,
|
|
64
|
+
headers: param.headers
|
|
64
65
|
});
|
|
65
66
|
sources.push(source);
|
|
66
67
|
});
|
|
@@ -1,9 +1,34 @@
|
|
|
1
|
-
type cfg = {url: string, httpReqTimeoutMillis?: int}
|
|
1
|
+
type cfg = {url: string, httpReqTimeoutMillis?: int, headers?: dict<string>}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// `addresses` omitted matches any address (a wildcard selection). Each `topics`
|
|
4
|
+
// position is `null` (match any) or a list of accepted topic hashes; the
|
|
5
|
+
// single-match case is a one-element list.
|
|
6
|
+
type getLogsParams = {
|
|
7
|
+
fromBlock: int,
|
|
8
|
+
toBlock: int,
|
|
9
|
+
addresses?: array<Address.t>,
|
|
10
|
+
topics: array<Nullable.t<array<string>>>,
|
|
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
|
+
type t = {
|
|
21
|
+
getHeight: unit => promise<int>,
|
|
22
|
+
getLogs: getLogsParams => promise<array<rpcEventItem>>,
|
|
23
|
+
}
|
|
4
24
|
|
|
5
25
|
@send
|
|
6
|
-
external classNew: (
|
|
26
|
+
external classNew: (
|
|
27
|
+
Core.evmRpcClientCtor,
|
|
28
|
+
cfg,
|
|
29
|
+
array<HyperSyncClient.Decoder.eventParamsInput>,
|
|
30
|
+
~checksumAddresses: bool,
|
|
31
|
+
) => t = "new"
|
|
7
32
|
|
|
8
33
|
// Rust encodes JSON-RPC errors as a JSON payload in the napi error's
|
|
9
34
|
// message: `{"kind":"JsonRpcError","code":-32005,"message":"..."}`.
|
|
@@ -34,9 +59,15 @@ let coerceErrorOrThrow = exn =>
|
|
|
34
59
|
| None => exn->throw
|
|
35
60
|
}
|
|
36
61
|
|
|
37
|
-
let make = (~url, ~httpReqTimeoutMillis
|
|
38
|
-
let client =
|
|
62
|
+
let make = (~url, ~checksumAddresses, ~httpReqTimeoutMillis=?, ~headers=?, ~allEventParams=[]) => {
|
|
63
|
+
let client =
|
|
64
|
+
Core.getAddon().evmRpcClient->classNew(
|
|
65
|
+
{url, ?httpReqTimeoutMillis, ?headers},
|
|
66
|
+
allEventParams,
|
|
67
|
+
~checksumAddresses,
|
|
68
|
+
)
|
|
39
69
|
{
|
|
40
70
|
getHeight: () => client.getHeight()->Promise.catch(coerceErrorOrThrow),
|
|
71
|
+
getLogs: params => client.getLogs(params)->Promise.catch(coerceErrorOrThrow),
|
|
41
72
|
}
|
|
42
73
|
}
|
|
@@ -46,13 +46,16 @@ function coerceErrorOrThrow(exn) {
|
|
|
46
46
|
throw exn;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function make(url, httpReqTimeoutMillis) {
|
|
49
|
+
function make(url, checksumAddresses, 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
|
+
}, allEventParams, checksumAddresses);
|
|
54
56
|
return {
|
|
55
|
-
getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow)
|
|
57
|
+
getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow),
|
|
58
|
+
getLogs: params => Stdlib_Promise.$$catch(client.getLogs(params), coerceErrorOrThrow)
|
|
56
59
|
};
|
|
57
60
|
}
|
|
58
61
|
|
|
@@ -208,22 +208,6 @@ module ResponseTypes = {
|
|
|
208
208
|
mixHash?: string,
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
type log = {
|
|
212
|
-
removed?: bool,
|
|
213
|
-
@as("logIndex") index?: int,
|
|
214
|
-
transactionIndex?: int,
|
|
215
|
-
transactionHash?: string,
|
|
216
|
-
blockHash?: string,
|
|
217
|
-
blockNumber?: int,
|
|
218
|
-
address?: Address.t,
|
|
219
|
-
data?: string,
|
|
220
|
-
topics?: array<Nullable.t<EvmTypes.Hex.t>>,
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Only the log is needed for decoding; the transaction/block are served from
|
|
224
|
-
// the store (event items) or unused (block-hash query).
|
|
225
|
-
type event = {log: log}
|
|
226
|
-
|
|
227
211
|
type rollbackGuard = {
|
|
228
212
|
blockNumber: int,
|
|
229
213
|
timestamp: int,
|
|
@@ -253,25 +237,6 @@ module Decoder = {
|
|
|
253
237
|
contractName: string,
|
|
254
238
|
params: array<Internal.paramMeta>,
|
|
255
239
|
}
|
|
256
|
-
|
|
257
|
-
// Decoded params keyed by contract name. Contracts that emit the same-signature
|
|
258
|
-
// event share one decode but get their own param names, so the caller picks the
|
|
259
|
-
// entry for the contract its router resolved the log to.
|
|
260
|
-
type tWithParams = {
|
|
261
|
-
decodeLogs: array<ResponseTypes.event> => promise<
|
|
262
|
-
array<Nullable.t<dict<Internal.eventParams>>>,
|
|
263
|
-
>,
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
@send
|
|
267
|
-
external classFromParams: (
|
|
268
|
-
Core.evmDecoderCtor,
|
|
269
|
-
array<eventParamsInput>,
|
|
270
|
-
~checksumAddresses: bool=?,
|
|
271
|
-
) => tWithParams = "fromParams"
|
|
272
|
-
|
|
273
|
-
let fromParams = (eventParams, ~checksumAddresses=?) =>
|
|
274
|
-
Core.getAddon().evmDecoder->classFromParams(eventParams, ~checksumAddresses?)
|
|
275
240
|
}
|
|
276
241
|
|
|
277
242
|
module EventItems = {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Core from "../Core.res.mjs";
|
|
4
4
|
import * as Utils from "../Utils.res.mjs";
|
|
5
|
-
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
6
5
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
7
6
|
|
|
8
7
|
let serializationFormatSchema = S$RescriptSchema.$$enum([
|
|
@@ -37,13 +36,7 @@ let QueryTypes = {
|
|
|
37
36
|
|
|
38
37
|
let ResponseTypes = {};
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
return Core.getAddon().EvmDecoder.fromParams(eventParams, checksumAddresses !== undefined ? Primitive_option.valFromOption(checksumAddresses) : undefined);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let Decoder = {
|
|
45
|
-
fromParams: fromParams
|
|
46
|
-
};
|
|
39
|
+
let Decoder = {};
|
|
47
40
|
|
|
48
41
|
let EventItems = {};
|
|
49
42
|
|
package/src/sources/Rpc.res
CHANGED
|
@@ -38,7 +38,21 @@ let jsonRpcFetcher: Rest.ApiFetcher.t = async args => {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
let makeClient =
|
|
41
|
+
let makeClient = (url, ~headers=?) => {
|
|
42
|
+
let fetcher: Rest.ApiFetcher.t = switch headers {
|
|
43
|
+
| None => jsonRpcFetcher
|
|
44
|
+
| Some(customHeaders) =>
|
|
45
|
+
async args => {
|
|
46
|
+
let headers = switch args.headers {
|
|
47
|
+
| Some(headers) => headers
|
|
48
|
+
| None => Dict.make()
|
|
49
|
+
}
|
|
50
|
+
customHeaders->Dict.forEachWithKey((value, key) => headers->Dict.set(key, value->Obj.magic))
|
|
51
|
+
await jsonRpcFetcher({...args, headers: Some(headers)})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
Rest.client(url, ~fetcher)
|
|
55
|
+
}
|
|
42
56
|
|
|
43
57
|
type hex = string
|
|
44
58
|
let makeHexSchema = fromStr =>
|
|
@@ -70,13 +84,7 @@ let decimalFloatSchema: S.schema<float> = S.string->S.transform(s => {
|
|
|
70
84
|
module GetLogs = {
|
|
71
85
|
@unboxed
|
|
72
86
|
type topicFilter = Single(hex) | Multiple(array<hex>) | @as(null) Null
|
|
73
|
-
let topicFilterSchema = S.union([
|
|
74
|
-
S.literal(Null),
|
|
75
|
-
S.schema(s => Multiple(s.matches(S.array(S.string)))),
|
|
76
|
-
S.schema(s => Single(s.matches(S.string))),
|
|
77
|
-
])
|
|
78
87
|
type topicQuery = array<topicFilter>
|
|
79
|
-
let topicQuerySchema = S.array(topicFilterSchema)
|
|
80
88
|
|
|
81
89
|
let makeTopicQuery = (~topic0=[], ~topic1=[], ~topic2=[], ~topic3=[]) => {
|
|
82
90
|
let topics = [topic0, topic1, topic2, topic3]
|
|
@@ -106,51 +114,15 @@ module GetLogs = {
|
|
|
106
114
|
let mapTopicQuery = ({topic0, topic1, topic2, topic3}: Internal.topicSelection): topicQuery =>
|
|
107
115
|
makeTopicQuery(~topic0, ~topic1, ~topic2, ~topic3)
|
|
108
116
|
|
|
109
|
-
type param = {
|
|
110
|
-
fromBlock: int,
|
|
111
|
-
toBlock: int,
|
|
112
|
-
address?: array<Address.t>,
|
|
113
|
-
topics: topicQuery,
|
|
114
|
-
// blockHash?: string,
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
let paramsSchema = S.object((s): param => {
|
|
118
|
-
fromBlock: s.field("fromBlock", hexIntSchema),
|
|
119
|
-
toBlock: s.field("toBlock", hexIntSchema),
|
|
120
|
-
address: ?s.field("address", S.option(S.array(Address.schema))),
|
|
121
|
-
topics: s.field("topics", topicQuerySchema),
|
|
122
|
-
// blockHash: ?s.field("blockHash", S.option(S.string)),
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
let fullParamsSchema = S.tuple1(paramsSchema)
|
|
126
|
-
|
|
127
117
|
type log = {
|
|
128
118
|
address: Address.t,
|
|
129
119
|
topics: array<hex>,
|
|
130
|
-
data: hex,
|
|
131
120
|
blockNumber: int,
|
|
132
121
|
transactionHash: hex,
|
|
133
122
|
transactionIndex: int,
|
|
134
123
|
blockHash: hex,
|
|
135
124
|
logIndex: int,
|
|
136
|
-
removed: bool,
|
|
137
125
|
}
|
|
138
|
-
|
|
139
|
-
let logSchema = S.object((s): log => {
|
|
140
|
-
address: s.field("address", Address.schema),
|
|
141
|
-
topics: s.field("topics", S.array(S.string)),
|
|
142
|
-
data: s.field("data", S.string),
|
|
143
|
-
blockNumber: s.field("blockNumber", hexIntSchema),
|
|
144
|
-
transactionHash: s.field("transactionHash", S.string),
|
|
145
|
-
transactionIndex: s.field("transactionIndex", hexIntSchema),
|
|
146
|
-
blockHash: s.field("blockHash", S.string),
|
|
147
|
-
logIndex: s.field("logIndex", hexIntSchema),
|
|
148
|
-
removed: s.field("removed", S.bool),
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
let resultSchema = S.array(logSchema)
|
|
152
|
-
|
|
153
|
-
let route = makeRpcRoute("eth_getLogs", fullParamsSchema, resultSchema)
|
|
154
126
|
}
|
|
155
127
|
|
|
156
128
|
module GetBlockByNumber = {
|
|
@@ -230,10 +202,6 @@ module GetTransactionReceipt = {
|
|
|
230
202
|
)
|
|
231
203
|
}
|
|
232
204
|
|
|
233
|
-
let getLogs = async (~client: Rest.client, ~param: GetLogs.param) => {
|
|
234
|
-
await GetLogs.route->Rest.fetch(param, ~client)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
205
|
let getBlock = async (~client: Rest.client, ~blockNumber: int) => {
|
|
238
206
|
await GetBlockByNumber.route->Rest.fetch(
|
|
239
207
|
{"blockNumber": blockNumber, "includeTransactions": false},
|