envio 3.5.0-alpha.2 → 3.5.0-rc.0
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/package.json +6 -6
- package/src/Batch.res +8 -8
- package/src/Batch.res.mjs +4 -3
- package/src/BatchProcessing.res +24 -0
- package/src/BatchProcessing.res.mjs +12 -0
- package/src/ChainFetching.res +20 -19
- package/src/ChainFetching.res.mjs +14 -14
- package/src/ChainId.res +58 -0
- package/src/ChainId.res.mjs +49 -0
- package/src/ChainId.resi +27 -0
- package/src/ChainMap.res +12 -26
- package/src/ChainMap.res.mjs +5 -41
- package/src/ChainMap.resi +5 -20
- package/src/ChainMetadata.res +1 -1
- package/src/ChainMetadata.res.mjs +3 -2
- package/src/ChainState.res +45 -24
- package/src/ChainState.res.mjs +40 -16
- package/src/ChainState.resi +3 -1
- package/src/Config.res +43 -26
- package/src/Config.res.mjs +19 -16
- package/src/ContractRegisterContext.res +1 -1
- package/src/ContractRegisterContext.res.mjs +1 -1
- package/src/Core.res +1 -0
- package/src/CrossChainState.res +90 -22
- package/src/CrossChainState.res.mjs +55 -16
- package/src/CrossChainState.resi +5 -1
- package/src/Env.res +0 -3
- package/src/Env.res.mjs +0 -3
- package/src/EventConfigBuilder.res +1 -1
- package/src/EventProcessing.res +4 -4
- package/src/EventProcessing.res.mjs +5 -5
- package/src/FetchState.res +489 -153
- package/src/FetchState.res.mjs +399 -129
- package/src/FinalizeBackfill.res +61 -0
- package/src/FinalizeBackfill.res.mjs +44 -0
- package/src/HandlerRegister.res +8 -8
- package/src/HandlerRegister.res.mjs +5 -4
- package/src/HandlerRegister.resi +1 -1
- package/src/InMemoryStore.res +4 -0
- package/src/InMemoryStore.res.mjs +1 -1
- package/src/InMemoryTable.res +15 -15
- package/src/InMemoryTable.res.mjs +14 -14
- package/src/IndexerLoop.res +9 -2
- package/src/IndexerLoop.res.mjs +8 -1
- package/src/IndexerState.res +52 -23
- package/src/IndexerState.res.mjs +52 -14
- package/src/IndexerState.resi +11 -6
- package/src/Internal.res +19 -17
- package/src/Internal.res.mjs +18 -9
- package/src/LoadLayer.res +10 -2
- package/src/LoadLayer.res.mjs +3 -1
- package/src/LogSelection.res +2 -2
- package/src/Main.res +7 -11
- package/src/Main.res.mjs +4 -6
- package/src/Metrics.res +6 -6
- package/src/Metrics.res.mjs +4 -3
- package/src/Persistence.res +23 -4
- package/src/PgStorage.res +447 -100
- package/src/PgStorage.res.mjs +285 -99
- package/src/RawEvent.res +1 -2
- package/src/RawEvent.res.mjs +1 -1
- package/src/Rollback.res +18 -20
- package/src/Rollback.res.mjs +9 -9
- package/src/RollbackCommit.res +2 -2
- package/src/RollbackCommit.res.mjs +3 -3
- package/src/SafeCheckpointTracking.res +2 -2
- package/src/SimulateDeadInputTracker.res +17 -21
- package/src/SimulateDeadInputTracker.res.mjs +9 -8
- package/src/SimulateItems.res +8 -10
- package/src/SimulateItems.res.mjs +9 -10
- package/src/Sink.res +2 -2
- package/src/Sink.res.mjs +3 -2
- package/src/TestIndexer.res +14 -14
- package/src/TestIndexer.res.mjs +16 -10
- package/src/UserContext.res +2 -2
- package/src/UserContext.res.mjs +2 -1
- package/src/Utils.res +0 -4
- package/src/Utils.res.mjs +1 -6
- package/src/bindings/ClickHouse.res +20 -2
- package/src/bindings/ClickHouse.res.mjs +25 -14
- package/src/db/EntityFilter.res +0 -4
- package/src/db/EntityFilter.res.mjs +1 -8
- package/src/db/EntityHistory.res +1 -1
- package/src/db/IndexCatalog.res +248 -0
- package/src/db/IndexCatalog.res.mjs +222 -0
- package/src/db/IndexDefinition.res +131 -0
- package/src/db/IndexDefinition.res.mjs +138 -0
- package/src/db/IndexManager.res +145 -0
- package/src/db/IndexManager.res.mjs +130 -0
- package/src/db/InternalTable.res +54 -23
- package/src/db/InternalTable.res.mjs +38 -23
- package/src/db/Table.res +24 -14
- package/src/db/Table.res.mjs +21 -16
- package/src/sources/AddressSet.res +5 -4
- package/src/sources/AddressStore.res +25 -14
- package/src/sources/AddressStore.res.mjs +10 -12
- package/src/sources/Evm.res +2 -2
- package/src/sources/Evm.res.mjs +1 -1
- package/src/sources/EvmChain.res +3 -3
- package/src/sources/EvmChain.res.mjs +3 -3
- package/src/sources/EvmHyperSyncSource.res +7 -7
- package/src/sources/EvmHyperSyncSource.res.mjs +6 -6
- package/src/sources/Fuel.res +2 -2
- package/src/sources/Fuel.res.mjs +1 -1
- package/src/sources/FuelHyperSyncClient.res +3 -0
- package/src/sources/FuelHyperSyncClient.res.mjs +1 -0
- package/src/sources/FuelHyperSyncSource.res +5 -7
- package/src/sources/FuelHyperSyncSource.res.mjs +5 -5
- package/src/sources/HyperSync.resi +2 -2
- package/src/sources/HyperSyncClient.res +5 -0
- package/src/sources/HyperSyncClient.res.mjs +1 -0
- package/src/sources/RpcSource.res +13 -14
- package/src/sources/RpcSource.res.mjs +13 -12
- package/src/sources/SimulateSource.res +42 -15
- package/src/sources/SimulateSource.res.mjs +27 -9
- package/src/sources/Source.res +1 -5
- package/src/sources/SourceManager.res +12 -12
- package/src/sources/SourceManager.res.mjs +11 -10
- package/src/sources/SourceManager.resi +2 -2
- package/src/sources/Svm.res +4 -5
- package/src/sources/Svm.res.mjs +5 -4
- package/src/sources/SvmHyperSyncClient.res +3 -0
- package/src/sources/SvmHyperSyncClient.res.mjs +1 -0
- package/src/sources/SvmHyperSyncSource.res +4 -4
- package/src/sources/SvmHyperSyncSource.res.mjs +3 -3
- package/src/tui/Tui.res +1 -1
- package/src/tui/Tui.res.mjs +2 -1
- package/src/tui/components/CustomHooks.res +5 -4
- package/src/tui/components/CustomHooks.res.mjs +4 -2
|
@@ -43,7 +43,7 @@ let parseBlockInfo = (json: JSON.t): blockInfo => {
|
|
|
43
43
|
let getKnownRawBlockWithBackoff = async (
|
|
44
44
|
~client,
|
|
45
45
|
~sourceName,
|
|
46
|
-
~
|
|
46
|
+
~chainId,
|
|
47
47
|
~blockNumber,
|
|
48
48
|
~backoffMsOnFailure,
|
|
49
49
|
~recordRequest: (~method: string, ~seconds: float) => unit,
|
|
@@ -60,7 +60,7 @@ let getKnownRawBlockWithBackoff = async (
|
|
|
60
60
|
"err": err->Utils.prettifyExn,
|
|
61
61
|
"msg": `Issue while running fetching batch of events from the RPC. Will wait ${currentBackoff.contents->Int.toString}ms and try again.`,
|
|
62
62
|
"source": sourceName,
|
|
63
|
-
"chainId":
|
|
63
|
+
"chainId": chainId,
|
|
64
64
|
"type": "EXPONENTIAL_BACKOFF",
|
|
65
65
|
})
|
|
66
66
|
await Time.resolvePromiseAfterDelay(~delayMilliseconds=currentBackoff.contents)
|
|
@@ -613,7 +613,7 @@ type options = {
|
|
|
613
613
|
sourceFor: Source.sourceFor,
|
|
614
614
|
syncConfig: Config.sourceSync,
|
|
615
615
|
url: string,
|
|
616
|
-
|
|
616
|
+
chainId: ChainId.t,
|
|
617
617
|
// The chain's registrations, indexed by their sequential `index`.
|
|
618
618
|
onEventRegistrations: array<Internal.evmOnEventRegistration>,
|
|
619
619
|
lowercaseAddresses: bool,
|
|
@@ -628,7 +628,7 @@ let make = (
|
|
|
628
628
|
sourceFor,
|
|
629
629
|
syncConfig,
|
|
630
630
|
url,
|
|
631
|
-
|
|
631
|
+
chainId,
|
|
632
632
|
onEventRegistrations,
|
|
633
633
|
lowercaseAddresses,
|
|
634
634
|
addressStore,
|
|
@@ -636,11 +636,10 @@ let make = (
|
|
|
636
636
|
?headers,
|
|
637
637
|
}: options,
|
|
638
638
|
): t => {
|
|
639
|
-
let chainId = chain->ChainMap.Chain.toChainId
|
|
640
639
|
let urlHost = switch Utils.Url.getHostFromUrl(url) {
|
|
641
640
|
| None =>
|
|
642
641
|
JsError.throwWithMessage(
|
|
643
|
-
`The RPC url for chain ${chainId->
|
|
642
|
+
`The RPC url for chain ${chainId->ChainId.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
|
|
644
643
|
)
|
|
645
644
|
| Some(host) => host
|
|
646
645
|
}
|
|
@@ -694,7 +693,7 @@ let make = (
|
|
|
694
693
|
"msg": `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ${(am._retryDelayMillis / 1000)
|
|
695
694
|
->Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
|
|
696
695
|
"source": name,
|
|
697
|
-
"chainId":
|
|
696
|
+
"chainId": chainId,
|
|
698
697
|
"metadata": {
|
|
699
698
|
{
|
|
700
699
|
"asyncTaskName": "transactionLoader: fetching transaction data - `getTransaction` rpc call",
|
|
@@ -711,7 +710,7 @@ let make = (
|
|
|
711
710
|
getKnownRawBlockWithBackoff(
|
|
712
711
|
~client,
|
|
713
712
|
~sourceName=name,
|
|
714
|
-
~
|
|
713
|
+
~chainId,
|
|
715
714
|
~backoffMsOnFailure=1000,
|
|
716
715
|
~blockNumber,
|
|
717
716
|
~recordRequest,
|
|
@@ -723,7 +722,7 @@ let make = (
|
|
|
723
722
|
"msg": `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ${(am._retryDelayMillis / 1000)
|
|
724
723
|
->Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
|
|
725
724
|
"source": name,
|
|
726
|
-
"chainId":
|
|
725
|
+
"chainId": chainId,
|
|
727
726
|
"metadata": {
|
|
728
727
|
{
|
|
729
728
|
"asyncTaskName": "blockLoader: fetching block data - `getBlock` rpc call",
|
|
@@ -754,7 +753,7 @@ let make = (
|
|
|
754
753
|
"msg": `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ${(am._retryDelayMillis / 1000)
|
|
755
754
|
->Int.toString} seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
|
|
756
755
|
"source": name,
|
|
757
|
-
"chainId":
|
|
756
|
+
"chainId": chainId,
|
|
758
757
|
"metadata": {
|
|
759
758
|
{
|
|
760
759
|
"asyncTaskName": "receiptLoader: fetching transaction receipt - `getTransactionReceipt` rpc call",
|
|
@@ -926,13 +925,13 @@ let make = (
|
|
|
926
925
|
Internal.Event({
|
|
927
926
|
onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
|
|
928
927
|
blockNumber: block->getBlockNumber,
|
|
929
|
-
|
|
928
|
+
chainId,
|
|
930
929
|
logIndex: log.logIndex,
|
|
931
930
|
transactionIndex: log.transactionIndex,
|
|
932
931
|
payload: {
|
|
933
932
|
contractName: eventConfig.contractName,
|
|
934
933
|
eventName: eventConfig.name,
|
|
935
|
-
chainId:
|
|
934
|
+
chainId: chainId,
|
|
936
935
|
params: decoded,
|
|
937
936
|
block,
|
|
938
937
|
transaction,
|
|
@@ -1024,13 +1023,13 @@ let make = (
|
|
|
1024
1023
|
|
|
1025
1024
|
let createHeightSubscription =
|
|
1026
1025
|
ws->Option.map(wsUrl =>
|
|
1027
|
-
(~onHeight) => RpcWebSocketHeightStream.subscribe(~wsUrl, ~chainId, ~onHeight)
|
|
1026
|
+
(~onHeight) => RpcWebSocketHeightStream.subscribe(~wsUrl, ~chainId=chainId, ~onHeight)
|
|
1028
1027
|
)
|
|
1029
1028
|
|
|
1030
1029
|
{
|
|
1031
1030
|
name,
|
|
1032
1031
|
sourceFor,
|
|
1033
|
-
|
|
1032
|
+
chainId,
|
|
1034
1033
|
poweredByHyperSync: false,
|
|
1035
1034
|
pollingInterval: syncConfig.pollingInterval,
|
|
1036
1035
|
getBlockHashes,
|
|
@@ -6,6 +6,7 @@ import * as Time from "../Time.res.mjs";
|
|
|
6
6
|
import * as Utils from "../Utils.res.mjs";
|
|
7
7
|
import * as Source from "./Source.res.mjs";
|
|
8
8
|
import * as Address from "../Address.res.mjs";
|
|
9
|
+
import * as ChainId from "../ChainId.res.mjs";
|
|
9
10
|
import * as Logging from "../Logging.res.mjs";
|
|
10
11
|
import * as Internal from "../Internal.res.mjs";
|
|
11
12
|
import * as LazyLoader from "../LazyLoader.res.mjs";
|
|
@@ -44,7 +45,7 @@ function parseBlockInfo(json) {
|
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
async function getKnownRawBlockWithBackoff(client, sourceName,
|
|
48
|
+
async function getKnownRawBlockWithBackoff(client, sourceName, chainId, blockNumber, backoffMsOnFailure, recordRequest) {
|
|
48
49
|
let currentBackoff = backoffMsOnFailure;
|
|
49
50
|
let result;
|
|
50
51
|
while (Stdlib_Option.isNone(result)) {
|
|
@@ -61,7 +62,7 @@ async function getKnownRawBlockWithBackoff(client, sourceName, chain, blockNumbe
|
|
|
61
62
|
err: Utils.prettifyExn(err),
|
|
62
63
|
msg: `Issue while running fetching batch of events from the RPC. Will wait ` + currentBackoff.toString() + `ms and try again.`,
|
|
63
64
|
source: sourceName,
|
|
64
|
-
chainId:
|
|
65
|
+
chainId: chainId,
|
|
65
66
|
type: "EXPONENTIAL_BACKOFF"
|
|
66
67
|
});
|
|
67
68
|
await Time.resolvePromiseAfterDelay(currentBackoff);
|
|
@@ -727,11 +728,11 @@ function make(param) {
|
|
|
727
728
|
let headers = param.headers;
|
|
728
729
|
let lowercaseAddresses = param.lowercaseAddresses;
|
|
729
730
|
let onEventRegistrations = param.onEventRegistrations;
|
|
730
|
-
let
|
|
731
|
+
let chainId = param.chainId;
|
|
731
732
|
let url = param.url;
|
|
732
733
|
let syncConfig = param.syncConfig;
|
|
733
734
|
let host = Utils.Url.getHostFromUrl(url);
|
|
734
|
-
let urlHost = host !== undefined ? host : Stdlib_JsError.throwWithMessage(`The RPC url for chain ` +
|
|
735
|
+
let urlHost = host !== undefined ? host : Stdlib_JsError.throwWithMessage(`The RPC url for chain ` + ChainId.toString(chainId) + ` is in incorrect format. The RPC url needs to start with either http:// or https://`);
|
|
735
736
|
let name = `RPC (` + urlHost + `)`;
|
|
736
737
|
let client = Rpc.makeClient(url, headers);
|
|
737
738
|
let rpcClient = EvmRpcClient.make(url, !lowercaseAddresses, syncConfig, undefined, headers, HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), param.addressStore);
|
|
@@ -757,17 +758,17 @@ function make(param) {
|
|
|
757
758
|
err: Utils.prettifyExn(exn),
|
|
758
759
|
msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
|
|
759
760
|
source: name,
|
|
760
|
-
chainId:
|
|
761
|
+
chainId: chainId,
|
|
761
762
|
metadata: {
|
|
762
763
|
asyncTaskName: "transactionLoader: fetching transaction data - `getTransaction` rpc call",
|
|
763
764
|
suggestedFix: "This likely means the RPC url you are using is not responding correctly. Please try another RPC endipoint."
|
|
764
765
|
}
|
|
765
766
|
}), undefined, undefined, undefined, undefined);
|
|
766
|
-
let makeBlockLoader = () => LazyLoader.make(blockNumber => getKnownRawBlockWithBackoff(client, name,
|
|
767
|
+
let makeBlockLoader = () => LazyLoader.make(blockNumber => getKnownRawBlockWithBackoff(client, name, chainId, blockNumber, 1000, recordRequest), (am, exn) => Logging.error({
|
|
767
768
|
err: Utils.prettifyExn(exn),
|
|
768
769
|
msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
|
|
769
770
|
source: name,
|
|
770
|
-
chainId:
|
|
771
|
+
chainId: chainId,
|
|
771
772
|
metadata: {
|
|
772
773
|
asyncTaskName: "blockLoader: fetching block data - `getBlock` rpc call",
|
|
773
774
|
suggestedFix: "This likely means the RPC url you are using is not responding correctly. Please try another RPC endipoint."
|
|
@@ -783,7 +784,7 @@ function make(param) {
|
|
|
783
784
|
err: Utils.prettifyExn(exn),
|
|
784
785
|
msg: `Top level promise timeout reached. Please review other errors or warnings in the code. This function will retry in ` + (am._retryDelayMillis / 1000 | 0).toString() + ` seconds. It is highly likely that your indexer isn't syncing on one or more chains currently. Also take a look at the "suggestedFix" in the metadata of this command`,
|
|
785
786
|
source: name,
|
|
786
|
-
chainId:
|
|
787
|
+
chainId: chainId,
|
|
787
788
|
metadata: {
|
|
788
789
|
asyncTaskName: "receiptLoader: fetching transaction receipt - `getTransactionReceipt` rpc call",
|
|
789
790
|
suggestedFix: "This likely means the RPC url you are using is not responding correctly. Please try another RPC endipoint."
|
|
@@ -924,7 +925,7 @@ function make(param) {
|
|
|
924
925
|
return {
|
|
925
926
|
kind: 0,
|
|
926
927
|
onEventRegistration: onEventRegistration,
|
|
927
|
-
|
|
928
|
+
chainId: chainId,
|
|
928
929
|
blockNumber: block.number,
|
|
929
930
|
logIndex: log.logIndex,
|
|
930
931
|
transactionIndex: log.transactionIndex,
|
|
@@ -932,7 +933,7 @@ function make(param) {
|
|
|
932
933
|
contractName: eventConfig.contractName,
|
|
933
934
|
eventName: eventConfig.name,
|
|
934
935
|
params: decoded,
|
|
935
|
-
chainId:
|
|
936
|
+
chainId: chainId,
|
|
936
937
|
srcAddress: log.address,
|
|
937
938
|
logIndex: log.logIndex,
|
|
938
939
|
transaction: Primitive_option.some(match[1]),
|
|
@@ -1011,11 +1012,11 @@ function make(param) {
|
|
|
1011
1012
|
},
|
|
1012
1013
|
requestStats: drainRequestStats()
|
|
1013
1014
|
}));
|
|
1014
|
-
let createHeightSubscription = Stdlib_Option.map(param.ws, wsUrl => (onHeight => RpcWebSocketHeightStream.subscribe(wsUrl,
|
|
1015
|
+
let createHeightSubscription = Stdlib_Option.map(param.ws, wsUrl => (onHeight => RpcWebSocketHeightStream.subscribe(wsUrl, chainId, onHeight)));
|
|
1015
1016
|
return {
|
|
1016
1017
|
name: name,
|
|
1017
1018
|
sourceFor: param.sourceFor,
|
|
1018
|
-
|
|
1019
|
+
chainId: chainId,
|
|
1019
1020
|
poweredByHyperSync: false,
|
|
1020
1021
|
pollingInterval: syncConfig.pollingInterval,
|
|
1021
1022
|
getBlockHashes: getBlockHashes,
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
let make = (
|
|
1
|
+
let make = (
|
|
2
|
+
~items: array<Internal.item>,
|
|
3
|
+
~endBlock: int,
|
|
4
|
+
~chainId: ChainId.t,
|
|
5
|
+
~addressStore: AddressStore.t,
|
|
6
|
+
): Source.t => {
|
|
2
7
|
let reportedHeight = max(endBlock, 1)
|
|
3
8
|
|
|
4
9
|
{
|
|
5
10
|
name: "SimulateSource",
|
|
6
|
-
simulateItems: items,
|
|
7
11
|
sourceFor: Sync,
|
|
8
|
-
|
|
12
|
+
chainId,
|
|
9
13
|
poweredByHyperSync: false,
|
|
10
14
|
pollingInterval: 0,
|
|
11
15
|
getBlockHashes: (~blockNumbers as _, ~logger as _) => {
|
|
@@ -27,8 +31,9 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
|
|
|
27
31
|
~logger as _,
|
|
28
32
|
) => {
|
|
29
33
|
// Mirror a real backend: return only the items this query would match —
|
|
30
|
-
// in the block range, part of the selection,
|
|
31
|
-
//
|
|
34
|
+
// in the block range, part of the selection, at or after the
|
|
35
|
+
// registration's own start block, and passing the same address gates a
|
|
36
|
+
// real source applies natively while routing (the emitter must be
|
|
32
37
|
// registered for the event's contract at or before the item's block, and
|
|
33
38
|
// any address-valued param filter must hold). Overlapping queries may
|
|
34
39
|
// return the same item more than once; the buffer dedups it.
|
|
@@ -36,27 +41,49 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
|
|
|
36
41
|
| Some(toBlock) => toBlock
|
|
37
42
|
| None => reportedHeight
|
|
38
43
|
}
|
|
39
|
-
|
|
44
|
+
// By registration index, not `eventConfig.id`: two registrations of the
|
|
45
|
+
// same event are independent and may sit in different partitions, so
|
|
46
|
+
// matching on the shared event id would let a sibling's items leak into
|
|
47
|
+
// this query.
|
|
48
|
+
let selectionRegistrationIndexes = Utils.Set.make()
|
|
40
49
|
selection.onEventRegistrations->Array.forEach(reg =>
|
|
41
|
-
|
|
50
|
+
selectionRegistrationIndexes->Utils.Set.add(reg.index)->ignore
|
|
42
51
|
)
|
|
43
52
|
|
|
53
|
+
// A client-filtered contract is queried address-free, so this partition
|
|
54
|
+
// holds none of its addresses and only the chain-wide store can answer
|
|
55
|
+
// for it — exactly the fallback every real source's router makes.
|
|
56
|
+
let isAddressAllowed = (address, ~contractName, ~blockNumber) =>
|
|
57
|
+
switch selection.clientFilteredContracts {
|
|
58
|
+
| Some(contractNames) if contractNames->Array.includes(contractName) =>
|
|
59
|
+
addressStore->AddressStore.isIndexedAt(address, contractName, blockNumber)
|
|
60
|
+
| _ => addressSet->AddressSet.containsAt(address, contractName, blockNumber)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// The registration's own start block, which the contract-wide address gate
|
|
64
|
+
// can't express. Every native router applies it while routing.
|
|
65
|
+
let hasStarted = (onEventRegistration: Internal.onEventRegistration, ~blockNumber) =>
|
|
66
|
+
switch onEventRegistration.startBlock {
|
|
67
|
+
| Some(startBlock) => blockNumber >= startBlock
|
|
68
|
+
| None => true
|
|
69
|
+
}
|
|
70
|
+
|
|
44
71
|
let parsedQueueItems = items->Array.filter(item => {
|
|
45
72
|
let eventItem = item->Internal.castUnsafeEventItem
|
|
46
73
|
let {blockNumber, onEventRegistration} = eventItem
|
|
47
74
|
if blockNumber < fromBlock || blockNumber > toBlockQueried {
|
|
48
75
|
false
|
|
49
|
-
} else if !(
|
|
76
|
+
} else if !(selectionRegistrationIndexes->Utils.Set.has(onEventRegistration.index)) {
|
|
77
|
+
false
|
|
78
|
+
} else if !(onEventRegistration->hasStarted(~blockNumber)) {
|
|
50
79
|
false
|
|
51
80
|
} else {
|
|
52
81
|
let contractName = onEventRegistration.eventConfig.contractName
|
|
53
82
|
let emitterAllowed =
|
|
54
83
|
onEventRegistration.isWildcard ||
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
blockNumber,
|
|
59
|
-
)
|
|
84
|
+
eventItem.payload
|
|
85
|
+
->Internal.getPayloadSrcAddress
|
|
86
|
+
->isAddressAllowed(~contractName, ~blockNumber)
|
|
60
87
|
emitterAllowed &&
|
|
61
88
|
switch onEventRegistration.addressFilterParamGroups {
|
|
62
89
|
| None
|
|
@@ -64,8 +91,8 @@ let make = (~items: array<Internal.item>, ~endBlock: int, ~chain: ChainMap.Chain
|
|
|
64
91
|
| Some(groups) =>
|
|
65
92
|
let params = eventItem.payload->Internal.getPayloadAddressParams
|
|
66
93
|
groups->Array.some(group =>
|
|
67
|
-
group->Array.every(
|
|
68
|
-
|
|
94
|
+
group->Array.every(
|
|
95
|
+
name => params->Dict.getUnsafe(name)->isAddressAllowed(~contractName, ~blockNumber),
|
|
69
96
|
)
|
|
70
97
|
)
|
|
71
98
|
}
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
4
4
|
|
|
5
|
-
function make(items, endBlock,
|
|
5
|
+
function make(items, endBlock, chainId, addressStore) {
|
|
6
6
|
let reportedHeight = Primitive_int.max(endBlock, 1);
|
|
7
7
|
return {
|
|
8
8
|
name: "SimulateSource",
|
|
9
9
|
sourceFor: "Sync",
|
|
10
|
-
|
|
10
|
+
chainId: chainId,
|
|
11
11
|
poweredByHyperSync: false,
|
|
12
12
|
pollingInterval: 0,
|
|
13
13
|
getBlockHashes: (param, param$1) => Promise.resolve({
|
|
@@ -23,21 +23,40 @@ function make(items, endBlock, chain) {
|
|
|
23
23
|
}),
|
|
24
24
|
getItemsOrThrow: (fromBlock, toBlock, addressSet, param, param$1, selection, param$2, param$3, param$4) => {
|
|
25
25
|
let toBlockQueried = toBlock !== undefined ? toBlock : reportedHeight;
|
|
26
|
-
let
|
|
26
|
+
let selectionRegistrationIndexes = new Set();
|
|
27
27
|
selection.onEventRegistrations.forEach(reg => {
|
|
28
|
-
|
|
28
|
+
selectionRegistrationIndexes.add(reg.index);
|
|
29
29
|
});
|
|
30
|
+
let isAddressAllowed = (address, contractName, blockNumber) => {
|
|
31
|
+
let contractNames = selection.clientFilteredContracts;
|
|
32
|
+
if (contractNames !== undefined && contractNames.includes(contractName)) {
|
|
33
|
+
return addressStore.isIndexedAt(address, contractName, blockNumber);
|
|
34
|
+
} else {
|
|
35
|
+
return addressSet.containsAt(address, contractName, blockNumber);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
let hasStarted = (onEventRegistration, blockNumber) => {
|
|
39
|
+
let startBlock = onEventRegistration.startBlock;
|
|
40
|
+
if (startBlock !== undefined) {
|
|
41
|
+
return blockNumber >= startBlock;
|
|
42
|
+
} else {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
30
46
|
let parsedQueueItems = items.filter(item => {
|
|
31
47
|
let blockNumber = item.blockNumber;
|
|
32
48
|
if (blockNumber < fromBlock || blockNumber > toBlockQueried) {
|
|
33
49
|
return false;
|
|
34
50
|
}
|
|
35
51
|
let onEventRegistration = item.onEventRegistration;
|
|
36
|
-
if (!
|
|
52
|
+
if (!selectionRegistrationIndexes.has(onEventRegistration.index)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (!hasStarted(onEventRegistration, blockNumber)) {
|
|
37
56
|
return false;
|
|
38
57
|
}
|
|
39
58
|
let contractName = onEventRegistration.eventConfig.contractName;
|
|
40
|
-
let emitterAllowed = onEventRegistration.isWildcard ||
|
|
59
|
+
let emitterAllowed = onEventRegistration.isWildcard || isAddressAllowed(item.payload.srcAddress, contractName, blockNumber);
|
|
41
60
|
if (!emitterAllowed) {
|
|
42
61
|
return false;
|
|
43
62
|
}
|
|
@@ -49,7 +68,7 @@ function make(items, endBlock, chain) {
|
|
|
49
68
|
return true;
|
|
50
69
|
}
|
|
51
70
|
let params = item.payload.params;
|
|
52
|
-
return groups.some(group => group.every(name =>
|
|
71
|
+
return groups.some(group => group.every(name => isAddressAllowed(params[name], contractName, blockNumber)));
|
|
53
72
|
});
|
|
54
73
|
return Promise.resolve({
|
|
55
74
|
knownHeight: reportedHeight,
|
|
@@ -65,8 +84,7 @@ function make(items, endBlock, chain) {
|
|
|
65
84
|
},
|
|
66
85
|
requestStats: []
|
|
67
86
|
});
|
|
68
|
-
}
|
|
69
|
-
simulateItems: items
|
|
87
|
+
}
|
|
70
88
|
};
|
|
71
89
|
}
|
|
72
90
|
|
package/src/sources/Source.res
CHANGED
|
@@ -64,7 +64,7 @@ type sourceFor = Sync | Fallback | Realtime
|
|
|
64
64
|
type t = {
|
|
65
65
|
name: string,
|
|
66
66
|
sourceFor: sourceFor,
|
|
67
|
-
|
|
67
|
+
chainId: ChainId.t,
|
|
68
68
|
poweredByHyperSync: bool,
|
|
69
69
|
/* Frequency (in ms) used when polling for new events on this network. */
|
|
70
70
|
pollingInterval: int,
|
|
@@ -95,8 +95,4 @@ type t = {
|
|
|
95
95
|
// Invoked by SourceManager once a rollback target is known so the source can
|
|
96
96
|
// drop any state that may now point at an orphaned chain (e.g. RPC block cache).
|
|
97
97
|
onReorg?: (~rollbackTargetBlock: int) => unit,
|
|
98
|
-
// Present only on the simulate source: the items a test fed in. The chain
|
|
99
|
-
// tracks which of these never reach a handler so the run can report dead
|
|
100
|
-
// simulate inputs on completion.
|
|
101
|
-
simulateItems?: array<Internal.item>,
|
|
102
98
|
}
|
|
@@ -32,7 +32,7 @@ let recordRequestStats = (sourceState: sourceState, requestStats: array<Source.r
|
|
|
32
32
|
// inline into the /metrics response.
|
|
33
33
|
type requestStatSample = {
|
|
34
34
|
sourceName: string,
|
|
35
|
-
chainId:
|
|
35
|
+
chainId: ChainId.t,
|
|
36
36
|
method: string,
|
|
37
37
|
count: int,
|
|
38
38
|
seconds: float,
|
|
@@ -81,7 +81,7 @@ let getActiveSource = sourceManager => sourceManager.activeSource
|
|
|
81
81
|
let getRequestStatSamples = (sourceManager: t): array<requestStatSample> => {
|
|
82
82
|
let samples = []
|
|
83
83
|
sourceManager.sourcesState->Array.forEach(sourceState => {
|
|
84
|
-
let chainId = sourceState.source.
|
|
84
|
+
let chainId = sourceState.source.chainId
|
|
85
85
|
sourceState.requestStats->Utils.Dict.forEachWithKey((agg, method) => {
|
|
86
86
|
samples
|
|
87
87
|
->Array.push({
|
|
@@ -101,7 +101,7 @@ let getRequestStatSamples = (sourceManager: t): array<requestStatSample> => {
|
|
|
101
101
|
// observed height yet are skipped.
|
|
102
102
|
type sourceHeightSample = {
|
|
103
103
|
sourceName: string,
|
|
104
|
-
chainId:
|
|
104
|
+
chainId: ChainId.t,
|
|
105
105
|
height: int,
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -111,7 +111,7 @@ let getSourceHeightSamples = (sourceManager: t): array<sourceHeightSample> => {
|
|
|
111
111
|
if sourceState.knownHeight > 0 {
|
|
112
112
|
samples->Array.push({
|
|
113
113
|
sourceName: sourceState.source.name,
|
|
114
|
-
chainId: sourceState.source.
|
|
114
|
+
chainId: sourceState.source.chainId,
|
|
115
115
|
height: sourceState.knownHeight,
|
|
116
116
|
})
|
|
117
117
|
}
|
|
@@ -425,7 +425,7 @@ let getSourceNewHeight = async (
|
|
|
425
425
|
logger->Logging.childTrace({
|
|
426
426
|
"msg": "onHeight subscription stale, switching to polling fallback",
|
|
427
427
|
"source": source.name,
|
|
428
|
-
"chainId": source.
|
|
428
|
+
"chainId": source.chainId,
|
|
429
429
|
})
|
|
430
430
|
let h = ref(initialHeight)
|
|
431
431
|
while h.contents <= knownHeight && !(newHeight.contents > initialHeight) {
|
|
@@ -585,7 +585,7 @@ let waitForNewBlock = async (sourceManager: t, ~knownHeight, ~isRealtime, ~reduc
|
|
|
585
585
|
|
|
586
586
|
let logger = Logging.createChild(
|
|
587
587
|
~params={
|
|
588
|
-
"chainId": sourceManager.activeSource.
|
|
588
|
+
"chainId": sourceManager.activeSource.chainId,
|
|
589
589
|
"knownHeight": knownHeight,
|
|
590
590
|
},
|
|
591
591
|
)
|
|
@@ -725,7 +725,7 @@ let executeQuery = async (
|
|
|
725
725
|
| Some(s) =>
|
|
726
726
|
if s.source !== sourceManager.activeSource {
|
|
727
727
|
let logger = Logging.createChild(
|
|
728
|
-
~params={"chainId": sourceManager.activeSource.
|
|
728
|
+
~params={"chainId": sourceManager.activeSource.chainId},
|
|
729
729
|
)
|
|
730
730
|
logger->Logging.childInfo({
|
|
731
731
|
"msg": "Switching data-source",
|
|
@@ -737,7 +737,7 @@ let executeQuery = async (
|
|
|
737
737
|
s
|
|
738
738
|
| None =>
|
|
739
739
|
let logger = Logging.createChild(
|
|
740
|
-
~params={"chainId": sourceManager.activeSource.
|
|
740
|
+
~params={"chainId": sourceManager.activeSource.chainId},
|
|
741
741
|
)
|
|
742
742
|
%raw(`null`)->ErrorHandling.mkLogAndRaise(~logger, ~msg=noSourcesError)
|
|
743
743
|
}
|
|
@@ -748,7 +748,7 @@ let executeQuery = async (
|
|
|
748
748
|
|
|
749
749
|
let logger = Logging.createChild(
|
|
750
750
|
~params={
|
|
751
|
-
"chainId": source.
|
|
751
|
+
"chainId": source.chainId,
|
|
752
752
|
"logType": "Block Range Query",
|
|
753
753
|
"partitionId": query.partitionId,
|
|
754
754
|
"source": source.name,
|
|
@@ -766,7 +766,7 @@ let executeQuery = async (
|
|
|
766
766
|
~addressSet=query.addresses,
|
|
767
767
|
~partitionId=query.partitionId,
|
|
768
768
|
~knownHeight,
|
|
769
|
-
~selection=query.selection,
|
|
769
|
+
~selection=query.selection->FetchState.narrowSelectionToRange(~toBlock),
|
|
770
770
|
~itemsTarget=query.itemsTarget,
|
|
771
771
|
~retry,
|
|
772
772
|
~logger,
|
|
@@ -896,7 +896,7 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
|
|
|
896
896
|
| Some(s) => s
|
|
897
897
|
| None =>
|
|
898
898
|
let logger = Logging.createChild(
|
|
899
|
-
~params={"chainId": sourceManager.activeSource.
|
|
899
|
+
~params={"chainId": sourceManager.activeSource.chainId},
|
|
900
900
|
)
|
|
901
901
|
%raw(`null`)->ErrorHandling.mkLogAndRaise(
|
|
902
902
|
~logger,
|
|
@@ -909,7 +909,7 @@ let getBlockHashes = async (sourceManager: t, ~blockNumbers: array<int>, ~isReal
|
|
|
909
909
|
|
|
910
910
|
let logger = Logging.createChild(
|
|
911
911
|
~params={
|
|
912
|
-
"chainId": source.
|
|
912
|
+
"chainId": source.chainId,
|
|
913
913
|
"logType": "Block Hash Query",
|
|
914
914
|
"source": source.name,
|
|
915
915
|
"retry": retry,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as Utils from "../Utils.res.mjs";
|
|
4
4
|
import * as Source from "./Source.res.mjs";
|
|
5
5
|
import * as Logging from "../Logging.res.mjs";
|
|
6
|
+
import * as FetchState from "../FetchState.res.mjs";
|
|
6
7
|
import * as Performance from "../bindings/Performance.res.mjs";
|
|
7
8
|
import * as ErrorHandling from "../ErrorHandling.res.mjs";
|
|
8
9
|
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
@@ -36,7 +37,7 @@ function getActiveSource(sourceManager) {
|
|
|
36
37
|
function getRequestStatSamples(sourceManager) {
|
|
37
38
|
let samples = [];
|
|
38
39
|
sourceManager.sourcesState.forEach(sourceState => {
|
|
39
|
-
let chainId = sourceState.source.
|
|
40
|
+
let chainId = sourceState.source.chainId;
|
|
40
41
|
Utils.Dict.forEachWithKey(sourceState.requestStats, (agg, method) => {
|
|
41
42
|
samples.push({
|
|
42
43
|
sourceName: sourceState.source.name,
|
|
@@ -56,7 +57,7 @@ function getSourceHeightSamples(sourceManager) {
|
|
|
56
57
|
if (sourceState.knownHeight > 0) {
|
|
57
58
|
samples.push({
|
|
58
59
|
sourceName: sourceState.source.name,
|
|
59
|
-
chainId: sourceState.source.
|
|
60
|
+
chainId: sourceState.source.chainId,
|
|
60
61
|
height: sourceState.knownHeight
|
|
61
62
|
});
|
|
62
63
|
return;
|
|
@@ -350,7 +351,7 @@ async function getSourceNewHeight(sourceManager, sourceState, knownHeight, stall
|
|
|
350
351
|
Logging.childTrace(logger, {
|
|
351
352
|
msg: "onHeight subscription stale, switching to polling fallback",
|
|
352
353
|
source: source.name,
|
|
353
|
-
chainId: source.
|
|
354
|
+
chainId: source.chainId
|
|
354
355
|
});
|
|
355
356
|
let h = initialHeight;
|
|
356
357
|
while (h <= knownHeight && newHeight.contents <= initialHeight) {
|
|
@@ -506,7 +507,7 @@ function getNextSource(sourceManager, isRealtime, excludedSources) {
|
|
|
506
507
|
async function waitForNewBlock(sourceManager, knownHeight, isRealtime, reducedPolling) {
|
|
507
508
|
let sourcesState = sourceManager.sourcesState;
|
|
508
509
|
let logger = Logging.createChild({
|
|
509
|
-
chainId: sourceManager.activeSource.
|
|
510
|
+
chainId: sourceManager.activeSource.chainId,
|
|
510
511
|
knownHeight: knownHeight
|
|
511
512
|
});
|
|
512
513
|
if (!sourceManager.waitingLogged) {
|
|
@@ -569,7 +570,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
569
570
|
if (s !== undefined) {
|
|
570
571
|
if (s.source !== sourceManager.activeSource) {
|
|
571
572
|
let logger = Logging.createChild({
|
|
572
|
-
chainId: sourceManager.activeSource.
|
|
573
|
+
chainId: sourceManager.activeSource.chainId
|
|
573
574
|
});
|
|
574
575
|
Logging.childInfo(logger, {
|
|
575
576
|
msg: "Switching data-source",
|
|
@@ -581,7 +582,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
581
582
|
sourceState = s;
|
|
582
583
|
} else {
|
|
583
584
|
let logger$1 = Logging.createChild({
|
|
584
|
-
chainId: sourceManager.activeSource.
|
|
585
|
+
chainId: sourceManager.activeSource.chainId
|
|
585
586
|
});
|
|
586
587
|
sourceState = ErrorHandling.mkLogAndRaise(logger$1, "The indexer doesn't have data-sources which can continue fetching. Please, check the error logs or reach out to the Envio team.", null);
|
|
587
588
|
}
|
|
@@ -590,7 +591,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
590
591
|
let toBlock = toBlockRef;
|
|
591
592
|
let retry = retryRef;
|
|
592
593
|
let logger$2 = Logging.createChild({
|
|
593
|
-
chainId: source.
|
|
594
|
+
chainId: source.chainId,
|
|
594
595
|
logType: "Block Range Query",
|
|
595
596
|
partitionId: query.partitionId,
|
|
596
597
|
source: source.name,
|
|
@@ -600,7 +601,7 @@ async function executeQuery(sourceManager, query, knownHeight, isRealtime) {
|
|
|
600
601
|
retry: retry
|
|
601
602
|
});
|
|
602
603
|
try {
|
|
603
|
-
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addresses, knownHeight, query.partitionId, query.selection, query.itemsTarget, retry, logger$2);
|
|
604
|
+
let response = await source.getItemsOrThrow(query.fromBlock, toBlock, query.addresses, knownHeight, query.partitionId, FetchState.narrowSelectionToRange(query.selection, toBlock), query.itemsTarget, retry, logger$2);
|
|
604
605
|
recordRequestStats(sourceState, response.requestStats);
|
|
605
606
|
sourceState.lastFailedAt = undefined;
|
|
606
607
|
if (response.knownHeight > sourceState.knownHeight) {
|
|
@@ -725,7 +726,7 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
|
|
|
725
726
|
sourceState = s;
|
|
726
727
|
} else {
|
|
727
728
|
let logger = Logging.createChild({
|
|
728
|
-
chainId: sourceManager.activeSource.
|
|
729
|
+
chainId: sourceManager.activeSource.chainId
|
|
729
730
|
});
|
|
730
731
|
sourceState = ErrorHandling.mkLogAndRaise(logger, "No data-sources available for fetching block hashes.", null);
|
|
731
732
|
}
|
|
@@ -733,7 +734,7 @@ async function getBlockHashes(sourceManager, blockNumbers, isRealtime) {
|
|
|
733
734
|
let source = sourceState.source;
|
|
734
735
|
let retry = retryRef;
|
|
735
736
|
let logger$1 = Logging.createChild({
|
|
736
|
-
chainId: source.
|
|
737
|
+
chainId: source.chainId,
|
|
737
738
|
logType: "Block Hash Query",
|
|
738
739
|
source: source.name,
|
|
739
740
|
retry: retry
|
|
@@ -23,7 +23,7 @@ let getActiveSource: t => Source.t
|
|
|
23
23
|
|
|
24
24
|
type requestStatSample = {
|
|
25
25
|
sourceName: string,
|
|
26
|
-
chainId:
|
|
26
|
+
chainId: ChainId.t,
|
|
27
27
|
method: string,
|
|
28
28
|
count: int,
|
|
29
29
|
seconds: float,
|
|
@@ -33,7 +33,7 @@ let getRequestStatSamples: t => array<requestStatSample>
|
|
|
33
33
|
|
|
34
34
|
type sourceHeightSample = {
|
|
35
35
|
sourceName: string,
|
|
36
|
-
chainId:
|
|
36
|
+
chainId: ChainId.t,
|
|
37
37
|
height: int,
|
|
38
38
|
}
|
|
39
39
|
|
package/src/sources/Svm.res
CHANGED
|
@@ -50,7 +50,7 @@ let make = (~logger: Pino.t): Ecosystem.t => {
|
|
|
50
50
|
~params={
|
|
51
51
|
"program": eventItem.onEventRegistration.eventConfig.contractName,
|
|
52
52
|
"instruction": eventItem.onEventRegistration.eventConfig.name,
|
|
53
|
-
"chainId": eventItem.
|
|
53
|
+
"chainId": eventItem.chainId,
|
|
54
54
|
"slot": eventItem.blockNumber,
|
|
55
55
|
"programId": instruction.programId,
|
|
56
56
|
},
|
|
@@ -70,14 +70,13 @@ module GetFinalizedSlot = {
|
|
|
70
70
|
)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
let makeRPCSource = (~
|
|
73
|
+
let makeRPCSource = (~chainId, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): Source.t => {
|
|
74
74
|
let client = Rest.client(rpc)
|
|
75
|
-
let chainId = chain->ChainMap.Chain.toChainId
|
|
76
75
|
|
|
77
76
|
let urlHost = switch Utils.Url.getHostFromUrl(rpc) {
|
|
78
77
|
| None =>
|
|
79
78
|
JsError.throwWithMessage(
|
|
80
|
-
`The RPC url for chain ${chainId->
|
|
79
|
+
`The RPC url for chain ${chainId->ChainId.toString} is in incorrect format. The RPC url needs to start with either http:// or https://`,
|
|
81
80
|
)
|
|
82
81
|
| Some(host) => host
|
|
83
82
|
}
|
|
@@ -86,7 +85,7 @@ let makeRPCSource = (~chain, ~rpc: string, ~sourceFor: Source.sourceFor=Sync): S
|
|
|
86
85
|
{
|
|
87
86
|
name,
|
|
88
87
|
sourceFor,
|
|
89
|
-
|
|
88
|
+
chainId,
|
|
90
89
|
poweredByHyperSync: false,
|
|
91
90
|
pollingInterval: 10_000,
|
|
92
91
|
getBlockHashes: (~blockNumbers as _, ~logger as _) =>
|