envio 3.13.0-alpha.0 → 3.13.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 -8
- package/src/BatchProcessing.res +4 -5
- package/src/BatchProcessing.res.mjs +4 -11
- package/src/ChainFetching.res +18 -38
- package/src/ChainFetching.res.mjs +14 -20
- package/src/ChainState.res +113 -77
- package/src/ChainState.res.mjs +90 -73
- package/src/ChainState.resi +14 -5
- package/src/Config.res +14 -17
- package/src/Config.res.mjs +5 -20
- package/src/CrossChainState.res +68 -20
- package/src/CrossChainState.res.mjs +48 -5
- package/src/CrossChainState.resi +1 -0
- package/src/Env.res +2 -2
- package/src/ErrorHandling.res +15 -4
- package/src/ErrorHandling.res.mjs +16 -5
- package/src/FinalizeBackfill.res +21 -9
- package/src/FinalizeBackfill.res.mjs +17 -4
- package/src/HandlerRegister.res +0 -37
- package/src/HandlerRegister.res.mjs +0 -18
- package/src/IndexerState.res +2 -0
- package/src/IndexerState.res.mjs +5 -0
- package/src/IndexerState.resi +1 -0
- package/src/Logging.res +6 -38
- package/src/Logging.res.mjs +5 -32
- package/src/Main.res +6 -9
- package/src/Main.res.mjs +3 -5
- package/src/Metrics.res +9 -4
- package/src/Metrics.res.mjs +3 -3
- package/src/Persistence.res +6 -3
- package/src/Persistence.res.mjs +5 -3
- package/src/PgStorage.res +2 -2
- package/src/PgStorage.res.mjs +2 -2
- package/src/Rollback.res +3 -3
- package/src/Server.res +0 -3
- package/src/SimulateItems.res +1 -1
- package/src/SimulateItems.res.mjs +1 -1
- package/src/Supervisor.res +236 -109
- package/src/Supervisor.res.mjs +185 -55
- package/src/TestIndexer.res +6 -3
- package/src/TestIndexer.res.mjs +3 -3
- package/src/Worker.res +6 -0
- package/src/Worker.res.mjs +2 -1
- package/src/bindings/NodeJs.res +25 -16
- package/src/bindings/NodeJs.res.mjs +8 -1
- package/src/sources/AddressSet.res +5 -3
- package/src/sources/AddressStore.res +19 -11
- package/src/sources/AddressStore.res.mjs +2 -7
- package/src/sources/BlockStore.res +10 -9
- package/src/sources/BlockStore.res.mjs +4 -4
- package/src/sources/ChainSources.res +4 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +4 -0
- package/src/sources/EvmChain.res.mjs +3 -1
- package/src/sources/EvmEventItem.res +56 -0
- package/src/sources/EvmEventItem.res.mjs +32 -0
- package/src/sources/EvmHyperSyncSource.res +7 -37
- package/src/sources/EvmHyperSyncSource.res.mjs +8 -29
- package/src/sources/EvmRpcClient.res +41 -59
- package/src/sources/EvmRpcClient.res.mjs +4 -54
- package/src/sources/EvmRpcWs.res +4 -1
- package/src/sources/EvmRpcWs.res.mjs +6 -2
- package/src/sources/FuelHyperSyncSource.res +6 -4
- package/src/sources/FuelHyperSyncSource.res.mjs +7 -9
- package/src/sources/HeightFeed.res +7 -1
- package/src/sources/HeightFeed.res.mjs +9 -7
- package/src/sources/HyperSyncClient.res +1 -14
- package/src/sources/RequestStat.res +7 -0
- package/src/sources/RequestStat.res.mjs +14 -1
- package/src/sources/RpcSource.res +100 -1043
- package/src/sources/RpcSource.res.mjs +81 -1056
- package/src/sources/SimulateSource.res +2 -5
- package/src/sources/SimulateSource.res.mjs +2 -3
- package/src/sources/Source.res +34 -12
- package/src/sources/Source.res.mjs +11 -0
- package/src/sources/SourceManager.res +111 -31
- package/src/sources/SourceManager.res.mjs +65 -31
- package/src/sources/SourceManager.resi +4 -0
- package/src/sources/StartBlockResolver.res +8 -5
- package/src/sources/StartBlockResolver.res.mjs +4 -2
- package/src/sources/SvmHyperSyncSource.res +4 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +4 -6
- package/src/sources/TransactionStore.res +9 -6
- package/src/sources/TransactionStore.res.mjs +2 -2
- package/src/tui/Tui.res +4 -18
- package/src/tui/Tui.res.mjs +3 -6
- package/src/LazyLoader.res +0 -135
- package/src/LazyLoader.res.mjs +0 -118
- package/src/bindings/SDSL.res +0 -12
- package/src/bindings/SDSL.res.mjs +0 -9
- package/src/bindings/Yargs.res +0 -8
- package/src/bindings/Yargs.res.mjs +0 -2
- package/src/sources/Rpc.res +0 -185
- package/src/sources/Rpc.res.mjs +0 -183
|
@@ -12,16 +12,15 @@
|
|
|
12
12
|
// blocks still inside the reorg threshold.
|
|
13
13
|
type t
|
|
14
14
|
|
|
15
|
-
@send external newEvm:
|
|
15
|
+
@send external newEvm: Core.blockStoreCtor => t = "newEvm"
|
|
16
16
|
@send external newSvm: Core.blockStoreCtor => t = "newSvm"
|
|
17
17
|
@send external newFuel: Core.blockStoreCtor => t = "newFuel"
|
|
18
18
|
|
|
19
|
-
// The store's ecosystem is fixed here, from the chain's config.
|
|
20
|
-
|
|
21
|
-
let make = (~ecosystem: Ecosystem.name, ~shouldChecksum: bool): t => {
|
|
19
|
+
// The store's ecosystem is fixed here, from the chain's config.
|
|
20
|
+
let make = (~ecosystem: Ecosystem.name): t => {
|
|
22
21
|
let ctor = Core.getAddon().blockStore
|
|
23
22
|
switch ecosystem {
|
|
24
|
-
| Evm => ctor->newEvm
|
|
23
|
+
| Evm => ctor->newEvm
|
|
25
24
|
| Svm => ctor->newSvm
|
|
26
25
|
| Fuel => ctor->newFuel
|
|
27
26
|
}
|
|
@@ -41,7 +40,7 @@ type svmBlockInput = {slot: int, hash?: string, time?: int}
|
|
|
41
40
|
type fuelBlockInput = {height: int, id?: string, time?: int}
|
|
42
41
|
|
|
43
42
|
@send
|
|
44
|
-
external fromJsEvm: (Core.blockStoreCtor, array<evmBlockInput
|
|
43
|
+
external fromJsEvm: (Core.blockStoreCtor, array<evmBlockInput>) => t = "fromJsEvm"
|
|
45
44
|
@send
|
|
46
45
|
external fromJsSvm: (Core.blockStoreCtor, array<svmBlockInput>) => t = "fromJsSvm"
|
|
47
46
|
@send
|
|
@@ -53,7 +52,7 @@ type inputBlock = {blockNumber: int, blockHash?: string, blockTimestamp?: int}
|
|
|
53
52
|
|
|
54
53
|
// Build a page from JS-observed blocks (RPC responses, stored reorg
|
|
55
54
|
// checkpoints) for merging into the per-chain store.
|
|
56
|
-
let fromJs = (blocks: array<inputBlock>, ~ecosystem: Ecosystem.name
|
|
55
|
+
let fromJs = (blocks: array<inputBlock>, ~ecosystem: Ecosystem.name): t => {
|
|
57
56
|
let ctor = Core.getAddon().blockStore
|
|
58
57
|
switch ecosystem {
|
|
59
58
|
| Evm =>
|
|
@@ -63,7 +62,6 @@ let fromJs = (blocks: array<inputBlock>, ~ecosystem: Ecosystem.name, ~shouldChec
|
|
|
63
62
|
hash: ?b.blockHash,
|
|
64
63
|
timestamp: ?b.blockTimestamp,
|
|
65
64
|
}),
|
|
66
|
-
shouldChecksum,
|
|
67
65
|
)
|
|
68
66
|
| Svm =>
|
|
69
67
|
ctor->fromJsSvm(
|
|
@@ -122,12 +120,15 @@ external latestValidBlockFromStore: (t, t, array<int>) => Null.t<int> = "latestV
|
|
|
122
120
|
|
|
123
121
|
// Bulk-materialise blocks off the JS thread, one row per `blockNumbers[i]` key,
|
|
124
122
|
// decoding only the fields set in that row's own `masks[i]`. Result is aligned
|
|
125
|
-
// with the input.
|
|
123
|
+
// with the input. `shouldChecksum` is the caller's spelling for the addresses
|
|
124
|
+
// decoded out of the store: the rows themselves are raw bytes, so a page merged
|
|
125
|
+
// from a differently configured store can't fix it in.
|
|
126
126
|
@send
|
|
127
127
|
external materialize: (
|
|
128
128
|
t,
|
|
129
129
|
~blockNumbers: array<int>,
|
|
130
130
|
~masks: array<float>,
|
|
131
|
+
~shouldChecksum: bool,
|
|
131
132
|
) => promise<array<Internal.eventBlock>> = "materialize"
|
|
132
133
|
|
|
133
134
|
// Drop blocks at or below the given block (already processed), keeping the
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
import * as Core from "../Core.res.mjs";
|
|
4
4
|
import * as FieldMask from "./FieldMask.res.mjs";
|
|
5
5
|
|
|
6
|
-
function make(ecosystem
|
|
6
|
+
function make(ecosystem) {
|
|
7
7
|
let ctor = Core.getAddon().BlockStore;
|
|
8
8
|
switch (ecosystem) {
|
|
9
9
|
case "evm" :
|
|
10
|
-
return ctor.newEvm(
|
|
10
|
+
return ctor.newEvm();
|
|
11
11
|
case "fuel" :
|
|
12
12
|
return ctor.newFuel();
|
|
13
13
|
case "svm" :
|
|
@@ -15,7 +15,7 @@ function make(ecosystem, shouldChecksum) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function fromJs(blocks, ecosystem
|
|
18
|
+
function fromJs(blocks, ecosystem) {
|
|
19
19
|
let ctor = Core.getAddon().BlockStore;
|
|
20
20
|
switch (ecosystem) {
|
|
21
21
|
case "evm" :
|
|
@@ -23,7 +23,7 @@ function fromJs(blocks, ecosystem, shouldChecksum) {
|
|
|
23
23
|
number: b.blockNumber,
|
|
24
24
|
hash: b.blockHash,
|
|
25
25
|
timestamp: b.blockTimestamp
|
|
26
|
-
}))
|
|
26
|
+
})));
|
|
27
27
|
case "fuel" :
|
|
28
28
|
return ctor.fromJsFuel(blocks.map(b => ({
|
|
29
29
|
height: b.blockNumber,
|
|
@@ -6,6 +6,8 @@ let make = (
|
|
|
6
6
|
~onEventRegistrations: array<Internal.onEventRegistration>,
|
|
7
7
|
~addressStore: AddressStore.t,
|
|
8
8
|
~lowercaseAddresses: bool,
|
|
9
|
+
~blockStore: BlockStore.t,
|
|
10
|
+
~transactionStore: TransactionStore.t,
|
|
9
11
|
): array<Source.t> => {
|
|
10
12
|
let chainId = chainConfig.id
|
|
11
13
|
switch chainConfig.sourceConfig {
|
|
@@ -31,6 +33,8 @@ let make = (
|
|
|
31
33
|
~rpcs=evmRpcs,
|
|
32
34
|
~lowercaseAddresses,
|
|
33
35
|
~addressStore,
|
|
36
|
+
~blockStore,
|
|
37
|
+
~transactionStore,
|
|
34
38
|
)
|
|
35
39
|
| Config.FuelSourceConfig({hypersync}) => [
|
|
36
40
|
FuelHyperSyncSource.make({
|
|
@@ -7,7 +7,7 @@ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js
|
|
|
7
7
|
import * as SvmHyperSyncSource from "./SvmHyperSyncSource.res.mjs";
|
|
8
8
|
import * as FuelHyperSyncSource from "./FuelHyperSyncSource.res.mjs";
|
|
9
9
|
|
|
10
|
-
function make(chainConfig, onEventRegistrations, addressStore, lowercaseAddresses) {
|
|
10
|
+
function make(chainConfig, onEventRegistrations, addressStore, lowercaseAddresses, blockStore, transactionStore) {
|
|
11
11
|
let chainId = chainConfig.id;
|
|
12
12
|
let sources = chainConfig.sourceConfig;
|
|
13
13
|
switch (sources.TAG) {
|
|
@@ -24,7 +24,7 @@ function make(chainConfig, onEventRegistrations, addressStore, lowercaseAddresse
|
|
|
24
24
|
headers: headers
|
|
25
25
|
};
|
|
26
26
|
});
|
|
27
|
-
return EvmChain.makeSources(chainId, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
|
|
27
|
+
return EvmChain.makeSources(chainId, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore, blockStore, transactionStore);
|
|
28
28
|
case "FuelSourceConfig" :
|
|
29
29
|
return [FuelHyperSyncSource.make({
|
|
30
30
|
chainId: chainId,
|
package/src/sources/EvmChain.res
CHANGED
|
@@ -46,6 +46,8 @@ let makeSources = (
|
|
|
46
46
|
~rpcs: array<rpc>,
|
|
47
47
|
~lowercaseAddresses,
|
|
48
48
|
~addressStore,
|
|
49
|
+
~blockStore,
|
|
50
|
+
~transactionStore,
|
|
49
51
|
) => {
|
|
50
52
|
let sources = switch hyperSync {
|
|
51
53
|
| Some(endpointUrl) => [
|
|
@@ -73,6 +75,8 @@ let makeSources = (
|
|
|
73
75
|
onEventRegistrations,
|
|
74
76
|
lowercaseAddresses,
|
|
75
77
|
addressStore,
|
|
78
|
+
blockStore,
|
|
79
|
+
transactionStore,
|
|
76
80
|
?ws,
|
|
77
81
|
?headers,
|
|
78
82
|
})
|
|
@@ -19,7 +19,7 @@ function getSyncConfig(param) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function makeSources(chainId, onEventRegistrations, hyperSync, rpcs, lowercaseAddresses, addressStore) {
|
|
22
|
+
function makeSources(chainId, onEventRegistrations, hyperSync, rpcs, lowercaseAddresses, addressStore, blockStore, transactionStore) {
|
|
23
23
|
let sources = hyperSync !== undefined ? [EvmHyperSyncSource.make({
|
|
24
24
|
chainId: chainId,
|
|
25
25
|
endpointUrl: hyperSync,
|
|
@@ -41,6 +41,8 @@ function makeSources(chainId, onEventRegistrations, hyperSync, rpcs, lowercaseAd
|
|
|
41
41
|
onEventRegistrations: onEventRegistrations,
|
|
42
42
|
lowercaseAddresses: lowercaseAddresses,
|
|
43
43
|
addressStore: addressStore,
|
|
44
|
+
blockStore: blockStore,
|
|
45
|
+
transactionStore: transactionStore,
|
|
44
46
|
ws: param.ws,
|
|
45
47
|
headers: param.headers
|
|
46
48
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// A decoded EVM log that routed to a registration, as both EVM sources return
|
|
2
|
+
// it. Everything else the log carried — its topics, its transaction hash, its
|
|
3
|
+
// block hash — is consumed on the Rust side, so what crosses the boundary is
|
|
4
|
+
// what an item is actually built from.
|
|
5
|
+
type t = {
|
|
6
|
+
logIndex: int,
|
|
7
|
+
srcAddress: Address.t,
|
|
8
|
+
// Number of the block this log belongs to; the block itself is resolved from
|
|
9
|
+
// the chain's block store, deduplicated across items sharing a block.
|
|
10
|
+
blockNumber: int,
|
|
11
|
+
// Key (with the block number) into the transaction store; the transaction
|
|
12
|
+
// is resolved from the store on demand.
|
|
13
|
+
transactionIndex: int,
|
|
14
|
+
// The registration this log routed to, by chain-scoped index. Logs that
|
|
15
|
+
// route to no registration never cross the boundary.
|
|
16
|
+
onEventRegistrationIndex: int,
|
|
17
|
+
params: Internal.eventParams,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// `block` and `transaction` are omitted from the payload: both sources leave
|
|
21
|
+
// them to be materialised from the per-chain stores at batch prep.
|
|
22
|
+
let toInternalItem = (
|
|
23
|
+
item: t,
|
|
24
|
+
~onEventRegistration: Internal.evmOnEventRegistration,
|
|
25
|
+
~chainId,
|
|
26
|
+
): Internal.item => {
|
|
27
|
+
let {transactionIndex, logIndex, srcAddress, blockNumber, params} = item
|
|
28
|
+
|
|
29
|
+
Internal.Event({
|
|
30
|
+
onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
|
|
31
|
+
chainId,
|
|
32
|
+
blockNumber,
|
|
33
|
+
logIndex,
|
|
34
|
+
transactionIndex,
|
|
35
|
+
payload: {
|
|
36
|
+
contractName: onEventRegistration.eventConfig.contractName,
|
|
37
|
+
eventName: onEventRegistration.eventConfig.name,
|
|
38
|
+
chainId,
|
|
39
|
+
params,
|
|
40
|
+
srcAddress,
|
|
41
|
+
logIndex,
|
|
42
|
+
}->Evm.fromPayload,
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let toInternalItems = (
|
|
47
|
+
items: array<t>,
|
|
48
|
+
~onEventRegistrations: array<Internal.evmOnEventRegistration>,
|
|
49
|
+
~chainId,
|
|
50
|
+
): array<Internal.item> =>
|
|
51
|
+
items->Array.map(item =>
|
|
52
|
+
item->toInternalItem(
|
|
53
|
+
~onEventRegistration=onEventRegistrations->Array.getUnsafe(item.onEventRegistrationIndex),
|
|
54
|
+
~chainId,
|
|
55
|
+
)
|
|
56
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function toInternalItem(item, onEventRegistration, chainId) {
|
|
5
|
+
let logIndex = item.logIndex;
|
|
6
|
+
return {
|
|
7
|
+
kind: 0,
|
|
8
|
+
onEventRegistration: onEventRegistration,
|
|
9
|
+
chainId: chainId,
|
|
10
|
+
blockNumber: item.blockNumber,
|
|
11
|
+
logIndex: logIndex,
|
|
12
|
+
transactionIndex: item.transactionIndex,
|
|
13
|
+
payload: {
|
|
14
|
+
contractName: onEventRegistration.eventConfig.contractName,
|
|
15
|
+
eventName: onEventRegistration.eventConfig.name,
|
|
16
|
+
params: item.params,
|
|
17
|
+
chainId: chainId,
|
|
18
|
+
srcAddress: item.srcAddress,
|
|
19
|
+
logIndex: logIndex
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toInternalItems(items, onEventRegistrations, chainId) {
|
|
25
|
+
return items.map(item => toInternalItem(item, onEventRegistrations[item.onEventRegistrationIndex], chainId));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
toInternalItem,
|
|
30
|
+
toInternalItems,
|
|
31
|
+
}
|
|
32
|
+
/* No side effect */
|
|
@@ -55,31 +55,6 @@ let make = (
|
|
|
55
55
|
)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
let makeEventBatchQueueItem = (
|
|
59
|
-
item: HyperSyncClient.EventItems.item,
|
|
60
|
-
~onEventRegistration: Internal.evmOnEventRegistration,
|
|
61
|
-
): Internal.item => {
|
|
62
|
-
let {transactionIndex, logIndex, srcAddress} = item
|
|
63
|
-
|
|
64
|
-
Internal.Event({
|
|
65
|
-
onEventRegistration: (onEventRegistration :> Internal.onEventRegistration),
|
|
66
|
-
chainId,
|
|
67
|
-
blockNumber: item.blockNumber,
|
|
68
|
-
logIndex,
|
|
69
|
-
transactionIndex,
|
|
70
|
-
// `block` and `transaction` are omitted; they're materialised from the
|
|
71
|
-
// per-chain stores onto the payload at batch prep.
|
|
72
|
-
payload: {
|
|
73
|
-
contractName: onEventRegistration.eventConfig.contractName,
|
|
74
|
-
eventName: onEventRegistration.eventConfig.name,
|
|
75
|
-
chainId,
|
|
76
|
-
params: item.params,
|
|
77
|
-
srcAddress,
|
|
78
|
-
logIndex,
|
|
79
|
-
}->Evm.fromPayload,
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
|
|
83
58
|
let getItemsOrThrow = async (
|
|
84
59
|
~fromBlock,
|
|
85
60
|
~toBlock,
|
|
@@ -96,6 +71,8 @@ let make = (
|
|
|
96
71
|
|
|
97
72
|
let startFetchingBatchTimeRef = Performance.now()
|
|
98
73
|
|
|
74
|
+
let fetchStats = () => RequestStat.single(~method="getLogs", ~sentAt=startFetchingBatchTimeRef)
|
|
75
|
+
|
|
99
76
|
//fetch batch
|
|
100
77
|
let pageUnsafe = try await HyperSync.GetLogs.query(
|
|
101
78
|
~client,
|
|
@@ -108,12 +85,12 @@ let make = (
|
|
|
108
85
|
~includeAllBlocks,
|
|
109
86
|
) catch {
|
|
110
87
|
| HyperSync.GetLogs.Error(WrongInstance) =>
|
|
111
|
-
throw(Source.SourceBehindHead({blockNumber: fromBlock, requestStats:
|
|
88
|
+
throw(Source.SourceBehindHead({blockNumber: fromBlock, requestStats: fetchStats()}))
|
|
112
89
|
| HyperSync.GetLogs.Error(UnexpectedMissingParams({missingParams})) =>
|
|
113
90
|
throw(
|
|
114
91
|
Source.GetItemsError(
|
|
115
92
|
Source.FailedGettingItems({
|
|
116
|
-
|
|
93
|
+
requestStats: fetchStats(),
|
|
117
94
|
attemptedToBlock: toBlock->Option.getOr(knownHeight),
|
|
118
95
|
retry: ImpossibleForTheQuery({
|
|
119
96
|
message: `Source returned invalid data with missing required fields: ${missingParams->Array.joinUnsafe(
|
|
@@ -128,6 +105,7 @@ let make = (
|
|
|
128
105
|
throw(
|
|
129
106
|
Source.GetItemsError(
|
|
130
107
|
Source.FailedGettingItems({
|
|
108
|
+
requestStats: fetchStats(),
|
|
131
109
|
exn,
|
|
132
110
|
attemptedToBlock: toBlock->Option.getOr(knownHeight),
|
|
133
111
|
retry: WithBackoff({
|
|
@@ -157,15 +135,8 @@ let make = (
|
|
|
157
135
|
|
|
158
136
|
let parsingTimeRef = Performance.now()
|
|
159
137
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
pageUnsafe.items->Array.forEach(item => {
|
|
164
|
-
let onEventRegistration = onEventRegistrations->Array.getUnsafe(item.onEventRegistrationIndex)
|
|
165
|
-
parsedQueueItems
|
|
166
|
-
->Array.push(makeEventBatchQueueItem(item, ~onEventRegistration))
|
|
167
|
-
->ignore
|
|
168
|
-
})
|
|
138
|
+
let parsedQueueItems =
|
|
139
|
+
pageUnsafe.items->EvmEventItem.toInternalItems(~onEventRegistrations, ~chainId)
|
|
169
140
|
|
|
170
141
|
let parsingTimeElapsed = parsingTimeRef->Performance.secondsSince
|
|
171
142
|
|
|
@@ -186,7 +157,6 @@ let make = (
|
|
|
186
157
|
latestFetchedBlockNumber: heighestBlockQueried,
|
|
187
158
|
stats,
|
|
188
159
|
knownHeight,
|
|
189
|
-
fromBlockQueried: fromBlock,
|
|
190
160
|
requestStats,
|
|
191
161
|
}
|
|
192
162
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import * as Source from "./Source.res.mjs";
|
|
4
4
|
import * as HyperSync from "./HyperSync.res.mjs";
|
|
5
5
|
import * as Performance from "../bindings/Performance.res.mjs";
|
|
6
|
+
import * as RequestStat from "./RequestStat.res.mjs";
|
|
7
|
+
import * as EvmEventItem from "./EvmEventItem.res.mjs";
|
|
6
8
|
import * as HyperSyncSSE from "./HyperSyncSSE.res.mjs";
|
|
7
9
|
import * as ErrorHandling from "../ErrorHandling.res.mjs";
|
|
8
10
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
@@ -25,25 +27,6 @@ function make(param) {
|
|
|
25
27
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
26
28
|
client = ErrorHandling.mkLogAndRaise(undefined, "Failed to instantiate the hypersync client, please double check your ABI", exn);
|
|
27
29
|
}
|
|
28
|
-
let makeEventBatchQueueItem = (item, onEventRegistration) => {
|
|
29
|
-
let logIndex = item.logIndex;
|
|
30
|
-
return {
|
|
31
|
-
kind: 0,
|
|
32
|
-
onEventRegistration: onEventRegistration,
|
|
33
|
-
chainId: chainId,
|
|
34
|
-
blockNumber: item.blockNumber,
|
|
35
|
-
logIndex: logIndex,
|
|
36
|
-
transactionIndex: item.transactionIndex,
|
|
37
|
-
payload: {
|
|
38
|
-
contractName: onEventRegistration.eventConfig.contractName,
|
|
39
|
-
eventName: onEventRegistration.eventConfig.name,
|
|
40
|
-
params: item.params,
|
|
41
|
-
chainId: chainId,
|
|
42
|
-
srcAddress: item.srcAddress,
|
|
43
|
-
logIndex: logIndex
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
30
|
let getItemsOrThrow = async (fromBlock, toBlock, addressSet, includeAllBlocks, knownHeight, param, selection, itemsTarget, retry, param$1) => {
|
|
48
31
|
let totalTimeRef = Performance.now();
|
|
49
32
|
let startFetchingBatchTimeRef = Performance.now();
|
|
@@ -58,7 +41,7 @@ function make(param) {
|
|
|
58
41
|
throw {
|
|
59
42
|
RE_EXN_ID: Source.SourceBehindHead,
|
|
60
43
|
blockNumber: fromBlock,
|
|
61
|
-
requestStats:
|
|
44
|
+
requestStats: RequestStat.single("getLogs", startFetchingBatchTimeRef),
|
|
62
45
|
Error: new Error()
|
|
63
46
|
};
|
|
64
47
|
}
|
|
@@ -66,12 +49,12 @@ function make(param) {
|
|
|
66
49
|
RE_EXN_ID: Source.GetItemsError,
|
|
67
50
|
_1: {
|
|
68
51
|
TAG: "FailedGettingItems",
|
|
69
|
-
exn: null,
|
|
70
52
|
attemptedToBlock: Stdlib_Option.getOr(toBlock, knownHeight),
|
|
71
53
|
retry: {
|
|
72
54
|
TAG: "ImpossibleForTheQuery",
|
|
73
55
|
message: `Source returned invalid data with missing required fields: ` + match.missingParams.join(", ")
|
|
74
|
-
}
|
|
56
|
+
},
|
|
57
|
+
requestStats: RequestStat.single("getLogs", startFetchingBatchTimeRef)
|
|
75
58
|
},
|
|
76
59
|
Error: new Error()
|
|
77
60
|
};
|
|
@@ -92,7 +75,8 @@ function make(param) {
|
|
|
92
75
|
TAG: "WithBackoff",
|
|
93
76
|
message: `Unexpected issue while fetching events from HyperSync client. Attempt a retry.`,
|
|
94
77
|
backoffMillis: retry !== 0 ? 1000 * retry | 0 : 500
|
|
95
|
-
}
|
|
78
|
+
},
|
|
79
|
+
requestStats: RequestStat.single("getLogs", startFetchingBatchTimeRef)
|
|
96
80
|
},
|
|
97
81
|
Error: new Error()
|
|
98
82
|
};
|
|
@@ -106,11 +90,7 @@ function make(param) {
|
|
|
106
90
|
let knownHeight$1 = pageUnsafe.archiveHeight;
|
|
107
91
|
let heighestBlockQueried = pageUnsafe.nextBlock - 1 | 0;
|
|
108
92
|
let parsingTimeRef = Performance.now();
|
|
109
|
-
let parsedQueueItems =
|
|
110
|
-
pageUnsafe.items.forEach(item => {
|
|
111
|
-
let onEventRegistration = onEventRegistrations[item.onEventRegistrationIndex];
|
|
112
|
-
parsedQueueItems.push(makeEventBatchQueueItem(item, onEventRegistration));
|
|
113
|
-
});
|
|
93
|
+
let parsedQueueItems = EvmEventItem.toInternalItems(pageUnsafe.items, onEventRegistrations, chainId);
|
|
114
94
|
let parsingTimeElapsed = Performance.secondsSince(parsingTimeRef);
|
|
115
95
|
let totalTimeElapsed = Performance.secondsSince(totalTimeRef);
|
|
116
96
|
let stats_parsing$unknowntime$unknown$lpars$rpar = parsingTimeElapsed;
|
|
@@ -125,7 +105,6 @@ function make(param) {
|
|
|
125
105
|
parsedQueueItems: parsedQueueItems,
|
|
126
106
|
transactionStore: Primitive_option.some(pageUnsafe.transactionStore),
|
|
127
107
|
blockStore: pageUnsafe.blockStore,
|
|
128
|
-
fromBlockQueried: fromBlock,
|
|
129
108
|
latestFetchedBlockNumber: heighestBlockQueried,
|
|
130
109
|
stats: stats,
|
|
131
110
|
requestStats: requestStats
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type cfg = {
|
|
2
2
|
url: string,
|
|
3
|
-
|
|
3
|
+
maxConcurrentRequests?: int,
|
|
4
4
|
headers?: dict<string>,
|
|
5
5
|
initialBlockInterval: int,
|
|
6
6
|
backoffMultiplicative: float,
|
|
@@ -10,38 +10,55 @@ type cfg = {
|
|
|
10
10
|
queryTimeoutMillis: int,
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// Only logs that resolved to a registration cross the boundary, each carrying
|
|
14
|
-
// its registration's chain-scoped index.
|
|
15
|
-
type rpcEventItem = {
|
|
16
|
-
log: Rpc.GetLogs.log,
|
|
17
|
-
onEventRegistrationIndex: int,
|
|
18
|
-
params: Internal.eventParams,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
13
|
type nextPageParams = {
|
|
22
14
|
fromBlock: int,
|
|
23
15
|
toBlockCeiling: int,
|
|
24
16
|
partitionId: string,
|
|
25
|
-
// The partition's registration selection, by chain-scoped index. Log
|
|
26
|
-
// selections and the routing index are derived on the Rust side from the
|
|
27
|
-
// registrations passed at construction.
|
|
28
17
|
registrationIndexes: array<int>,
|
|
29
|
-
// Contract names to fetch address-free even though their registrations
|
|
30
|
-
// depend on addresses (client-side filtering). None/empty means
|
|
31
|
-
// every address-dependent contract is filtered server-side.
|
|
32
18
|
clientFilteredContracts: option<array<string>>,
|
|
19
|
+
retry: int,
|
|
33
20
|
}
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
// Rust reports what to do about a page it could not read rather than throwing
|
|
23
|
+
// it; see `NextPageResult` in `evm_rpc_source/mod.rs` for which fields each
|
|
24
|
+
// `kind` populates. A thrown error from `getNextPage` is a genuine bug, not an
|
|
25
|
+
// outcome to recover from.
|
|
26
|
+
type pageOutcome =
|
|
27
|
+
| @as("Ok") Ok
|
|
28
|
+
| @as("SuggestedToBlock") SuggestedToBlock
|
|
29
|
+
| @as("Backoff") Backoff
|
|
30
|
+
| @as("FieldSelection") FieldSelection
|
|
31
|
+
|
|
32
|
+
type nextPageResult = {
|
|
33
|
+
kind: pageOutcome,
|
|
34
|
+
requestStats: array<Source.requestStat>,
|
|
37
35
|
toBlock: int,
|
|
36
|
+
items: array<EvmEventItem.t>,
|
|
37
|
+
message: option<string>,
|
|
38
|
+
providerMessage: option<string>,
|
|
39
|
+
blockNumber: option<int>,
|
|
40
|
+
retryToBlock: option<int>,
|
|
41
|
+
backoffMillis: option<int>,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// `message` is set when the read failed, in which case the page returned
|
|
45
|
+
// alongside it is empty.
|
|
46
|
+
type blockHashResult = {
|
|
47
|
+
message: option<string>,
|
|
38
48
|
requestStats: array<Source.requestStat>,
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
// The caller provides a range; Rust decides the actual `toBlock` and returns it.
|
|
42
51
|
type t = {
|
|
43
|
-
getHeight: unit => promise<int>,
|
|
44
|
-
|
|
52
|
+
getHeight: unit => promise<(int, array<Source.requestStat>)>,
|
|
53
|
+
// The stores it returns are this page's own, for the caller to merge.
|
|
54
|
+
getNextPage: (
|
|
55
|
+
nextPageParams,
|
|
56
|
+
AddressSet.t,
|
|
57
|
+
BlockStore.t,
|
|
58
|
+
TransactionStore.t,
|
|
59
|
+
) => promise<(nextPageResult, BlockStore.t, TransactionStore.t)>,
|
|
60
|
+
getBlockHashes: array<int> => promise<(blockHashResult, BlockStore.t)>,
|
|
61
|
+
onReorg: unit => unit,
|
|
45
62
|
}
|
|
46
63
|
|
|
47
64
|
@send
|
|
@@ -53,48 +70,19 @@ external classNew: (
|
|
|
53
70
|
~addressStore: AddressStore.t,
|
|
54
71
|
) => t = "new"
|
|
55
72
|
|
|
56
|
-
// Rust encodes JSON-RPC errors as a JSON payload in the napi error's
|
|
57
|
-
// message: `{"kind":"JsonRpcError","code":-32005,"message":"..."}`.
|
|
58
|
-
// Parse it back so callers keep matching on Rpc.JsonRpcError.
|
|
59
|
-
let getJsonRpcError = (exn: exn): option<Rpc.rpcError> =>
|
|
60
|
-
switch exn {
|
|
61
|
-
| JsExn(e) =>
|
|
62
|
-
switch e->JsExn.message {
|
|
63
|
-
| Some(msg) =>
|
|
64
|
-
switch msg->JSON.parseOrThrow->JSON.Decode.object {
|
|
65
|
-
| exception _ => None
|
|
66
|
-
| None => None
|
|
67
|
-
| Some(obj) =>
|
|
68
|
-
switch (obj->Dict.get("kind"), obj->Dict.get("code"), obj->Dict.get("message")) {
|
|
69
|
-
| (Some(String("JsonRpcError")), Some(Number(code)), Some(String(message))) =>
|
|
70
|
-
Some({code: code->Float.toInt, message})
|
|
71
|
-
| _ => None
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
| None => None
|
|
75
|
-
}
|
|
76
|
-
| _ => None
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
let coerceErrorOrThrow = exn =>
|
|
80
|
-
switch exn->getJsonRpcError {
|
|
81
|
-
| Some(rpcError) => throw(Rpc.JsonRpcError(rpcError))
|
|
82
|
-
| None => exn->throw
|
|
83
|
-
}
|
|
84
|
-
|
|
85
73
|
let make = (
|
|
86
74
|
~url,
|
|
87
75
|
~checksumAddresses,
|
|
88
76
|
~syncConfig: Config.sourceSync,
|
|
89
|
-
~
|
|
77
|
+
~maxConcurrentRequests=?,
|
|
90
78
|
~headers=?,
|
|
91
79
|
~eventRegistrations=[],
|
|
92
80
|
~addressStore,
|
|
93
|
-
) =>
|
|
94
|
-
|
|
81
|
+
) =>
|
|
82
|
+
Core.getAddon().evmRpcClient->classNew(
|
|
95
83
|
{
|
|
96
84
|
url,
|
|
97
|
-
?
|
|
85
|
+
?maxConcurrentRequests,
|
|
98
86
|
?headers,
|
|
99
87
|
initialBlockInterval: syncConfig.initialBlockInterval,
|
|
100
88
|
backoffMultiplicative: syncConfig.backoffMultiplicative,
|
|
@@ -107,9 +95,3 @@ let make = (
|
|
|
107
95
|
~checksumAddresses,
|
|
108
96
|
~addressStore,
|
|
109
97
|
)
|
|
110
|
-
{
|
|
111
|
-
getHeight: () => client.getHeight()->Promise.catch(coerceErrorOrThrow),
|
|
112
|
-
getNextPage: (params, addressSet) =>
|
|
113
|
-
client.getNextPage(params, addressSet)->Promise.catch(coerceErrorOrThrow),
|
|
114
|
-
}
|
|
115
|
-
}
|
|
@@ -1,56 +1,12 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Rpc from "./Rpc.res.mjs";
|
|
4
3
|
import * as Core from "../Core.res.mjs";
|
|
5
|
-
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
6
|
-
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
7
|
-
import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
8
4
|
|
|
9
|
-
function
|
|
10
|
-
if (exn.RE_EXN_ID !== "JsExn") {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
let msg = Stdlib_JsExn.message(exn._1);
|
|
14
|
-
if (msg === undefined) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
let obj;
|
|
18
|
-
try {
|
|
19
|
-
obj = Stdlib_JSON.Decode.object(JSON.parse(msg));
|
|
20
|
-
} catch (exn$1) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
if (obj === undefined) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
let match = obj["kind"];
|
|
27
|
-
let match$1 = obj["code"];
|
|
28
|
-
let match$2 = obj["message"];
|
|
29
|
-
if (match === "JsonRpcError" && typeof match$1 === "number" && typeof match$2 === "string") {
|
|
30
|
-
return {
|
|
31
|
-
code: match$1 | 0,
|
|
32
|
-
message: match$2
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function coerceErrorOrThrow(exn) {
|
|
38
|
-
let rpcError = getJsonRpcError(exn);
|
|
39
|
-
if (rpcError !== undefined) {
|
|
40
|
-
throw {
|
|
41
|
-
RE_EXN_ID: Rpc.JsonRpcError,
|
|
42
|
-
_1: rpcError,
|
|
43
|
-
Error: new Error()
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
throw exn;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers, eventRegistrationsOpt, addressStore) {
|
|
5
|
+
function make(url, checksumAddresses, syncConfig, maxConcurrentRequests, headers, eventRegistrationsOpt, addressStore) {
|
|
50
6
|
let eventRegistrations = eventRegistrationsOpt !== undefined ? eventRegistrationsOpt : [];
|
|
51
|
-
|
|
7
|
+
return Core.getAddon().EvmRpcClient.new({
|
|
52
8
|
url: url,
|
|
53
|
-
|
|
9
|
+
maxConcurrentRequests: maxConcurrentRequests,
|
|
54
10
|
headers: headers,
|
|
55
11
|
initialBlockInterval: syncConfig.initialBlockInterval,
|
|
56
12
|
backoffMultiplicative: syncConfig.backoffMultiplicative,
|
|
@@ -59,15 +15,9 @@ function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers,
|
|
|
59
15
|
backoffMillis: syncConfig.backoffMillis,
|
|
60
16
|
queryTimeoutMillis: syncConfig.queryTimeoutMillis
|
|
61
17
|
}, eventRegistrations, checksumAddresses, addressStore);
|
|
62
|
-
return {
|
|
63
|
-
getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow),
|
|
64
|
-
getNextPage: (params, addressSet) => Stdlib_Promise.$$catch(client.getNextPage(params, addressSet), coerceErrorOrThrow)
|
|
65
|
-
};
|
|
66
18
|
}
|
|
67
19
|
|
|
68
20
|
export {
|
|
69
|
-
getJsonRpcError,
|
|
70
|
-
coerceErrorOrThrow,
|
|
71
21
|
make,
|
|
72
22
|
}
|
|
73
|
-
/*
|
|
23
|
+
/* Core Not a pure module */
|
package/src/sources/EvmRpcWs.res
CHANGED
|
@@ -43,6 +43,9 @@ let refusesSubscribe = (id, ~subscribed) =>
|
|
|
43
43
|
| Some(id) => id === subscribeRequestIdText
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
external number: string => int = "Number"
|
|
47
|
+
let hexIntSchema: S.schema<int> = S.string->S.transform(_ => {parser: str => str->number})
|
|
48
|
+
|
|
46
49
|
let wsMessageSchema = S.union([
|
|
47
50
|
S.object(s => {
|
|
48
51
|
let _ = s.field("method", S.literal("eth_subscription"))
|
|
@@ -54,7 +57,7 @@ let wsMessageSchema = S.union([
|
|
|
54
57
|
"result",
|
|
55
58
|
S.object(
|
|
56
59
|
s => {
|
|
57
|
-
s.field("number",
|
|
60
|
+
s.field("number", hexIntSchema)
|
|
58
61
|
},
|
|
59
62
|
),
|
|
60
63
|
)
|