envio 3.5.0-alpha.1 → 3.5.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +10 -6
- package/package.json +6 -6
- package/src/BatchProcessing.res +27 -0
- package/src/BatchProcessing.res.mjs +12 -0
- package/src/ChainFetching.res +5 -5
- package/src/ChainFetching.res.mjs +3 -4
- package/src/ChainState.res +48 -25
- package/src/ChainState.res.mjs +23 -29
- package/src/ChainState.resi +2 -6
- package/src/Change.res +3 -3
- package/src/Config.res +2 -6
- package/src/Config.res.mjs +2 -12
- package/src/Core.res +3 -0
- package/src/CrossChainState.res +33 -10
- package/src/CrossChainState.res.mjs +21 -6
- package/src/CrossChainState.resi +3 -0
- package/src/EntityId.res +15 -0
- package/src/EntityId.res.mjs +9 -0
- package/src/EventConfigBuilder.res +1 -58
- package/src/EventConfigBuilder.res.mjs +1 -33
- package/src/FetchState.res +510 -460
- package/src/FetchState.res.mjs +413 -428
- package/src/FinalizeBackfill.res +34 -0
- package/src/FinalizeBackfill.res.mjs +26 -0
- package/src/HandlerRegister.res +45 -0
- package/src/HandlerRegister.res.mjs +43 -0
- package/src/InMemoryStore.res +11 -4
- package/src/InMemoryTable.res +26 -24
- package/src/InMemoryTable.res.mjs +19 -18
- package/src/IndexerState.res +71 -2
- package/src/IndexerState.res.mjs +67 -3
- package/src/IndexerState.resi +5 -0
- package/src/Internal.res +17 -8
- package/src/LoadLayer.res +9 -1
- package/src/LoadLayer.res.mjs +1 -0
- package/src/LogSelection.res +2 -1
- package/src/Metrics.res +37 -6
- package/src/Metrics.res.mjs +5 -1
- package/src/Persistence.res +12 -1
- package/src/PgStorage.res +411 -57
- package/src/PgStorage.res.mjs +262 -41
- package/src/SimulateItems.res +2 -2
- package/src/TestIndexer.res +9 -5
- package/src/TestIndexer.res.mjs +5 -2
- package/src/UserContext.res +3 -3
- package/src/Writing.res +12 -2
- package/src/Writing.res.mjs +13 -2
- package/src/bindings/ClickHouse.res +5 -3
- package/src/bindings/ClickHouse.res.mjs +2 -5
- package/src/bindings/Vitest.res +22 -1
- package/src/bindings/Vitest.res.mjs +15 -0
- package/src/db/EntityFilter.res +0 -4
- package/src/db/EntityFilter.res.mjs +1 -8
- package/src/db/EntityHistory.res +16 -7
- package/src/db/EntityHistory.res.mjs +7 -6
- package/src/db/IndexRegistry.res +219 -0
- package/src/db/IndexRegistry.res.mjs +195 -0
- package/src/db/InternalTable.res +7 -0
- package/src/db/InternalTable.res.mjs +7 -0
- package/src/db/Table.res +53 -22
- package/src/db/Table.res.mjs +56 -18
- package/src/sources/AddressSet.res +48 -0
- package/src/sources/AddressSet.res.mjs +11 -0
- package/src/sources/AddressStore.res +152 -0
- package/src/sources/AddressStore.res.mjs +97 -0
- package/src/sources/EvmChain.res +3 -0
- package/src/sources/EvmChain.res.mjs +4 -2
- package/src/sources/EvmHyperSyncSource.res +6 -3
- package/src/sources/EvmHyperSyncSource.res.mjs +3 -3
- package/src/sources/EvmRpcClient.res +6 -3
- package/src/sources/EvmRpcClient.res.mjs +3 -3
- package/src/sources/FuelHyperSync.res +4 -3
- package/src/sources/FuelHyperSync.res.mjs +3 -3
- package/src/sources/FuelHyperSync.resi +2 -1
- package/src/sources/FuelHyperSyncClient.res +12 -6
- package/src/sources/FuelHyperSyncClient.res.mjs +2 -2
- package/src/sources/FuelHyperSyncSource.res +7 -4
- package/src/sources/FuelHyperSyncSource.res.mjs +3 -3
- package/src/sources/HyperSync.res +6 -4
- package/src/sources/HyperSync.res.mjs +2 -3
- package/src/sources/HyperSync.resi +1 -1
- package/src/sources/HyperSyncClient.res +18 -9
- package/src/sources/HyperSyncClient.res.mjs +4 -4
- package/src/sources/RpcSource.res +15 -10
- package/src/sources/RpcSource.res.mjs +3 -4
- package/src/sources/SimulateSource.res +26 -11
- package/src/sources/SimulateSource.res.mjs +13 -5
- package/src/sources/Source.res +4 -2
- package/src/sources/SourceManager.res +2 -3
- package/src/sources/SourceManager.res.mjs +2 -3
- package/src/sources/Svm.res +1 -2
- package/src/sources/Svm.res.mjs +1 -1
- package/src/sources/SvmHyperSyncClient.res +12 -3
- package/src/sources/SvmHyperSyncClient.res.mjs +2 -2
- package/src/sources/SvmHyperSyncSource.res +10 -5
- package/src/sources/SvmHyperSyncSource.res.mjs +5 -4
- package/src/IndexingAddresses.res +0 -151
- package/src/IndexingAddresses.res.mjs +0 -130
- package/src/IndexingAddresses.resi +0 -39
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Binding to the Rust `AddressStore` napi class: the chain-wide index of every
|
|
2
|
+
// address being indexed, config-declared or dynamically registered. It owns
|
|
3
|
+
// registration bookkeeping (conflict detection, `effectiveStartBlock`
|
|
4
|
+
// derivation, reorg rollback) and hands out `AddressSet` snapshots for the
|
|
5
|
+
// fetch state's partitions. One store lives per chain on `ChainState`, shared
|
|
6
|
+
// with that chain's source clients — which read it to drop items whose emitter
|
|
7
|
+
// (or address-valued param) isn't registered at the item's block.
|
|
8
|
+
type t
|
|
9
|
+
|
|
10
|
+
// A contract the chain has events for. An address registered for anything else
|
|
11
|
+
// gets the `NoEvents` verdict: persisted so a future config that adds events
|
|
12
|
+
// picks it up, but never fetched.
|
|
13
|
+
type contract = {name: string, startBlock: option<int>}
|
|
14
|
+
|
|
15
|
+
type registration = {
|
|
16
|
+
address: Address.t,
|
|
17
|
+
contractName: string,
|
|
18
|
+
// -1 for a config address (not dynamically registered).
|
|
19
|
+
registrationBlock: int,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type verdict =
|
|
23
|
+
| Added({effectiveStartBlock: int})
|
|
24
|
+
// Contract has no events; tracked and persisted, never fetched.
|
|
25
|
+
| NoEvents({effectiveStartBlock: int})
|
|
26
|
+
// Already registered for the same contract.
|
|
27
|
+
| Duplicate({effectiveStartBlock: int, existingEffectiveStartBlock: int})
|
|
28
|
+
// Already registered for a different contract.
|
|
29
|
+
| Conflict({existingContractName: string})
|
|
30
|
+
// Not a well-formed address for the ecosystem.
|
|
31
|
+
| Invalid
|
|
32
|
+
|
|
33
|
+
type rawVerdict = {
|
|
34
|
+
kind: string,
|
|
35
|
+
effectiveStartBlock: int,
|
|
36
|
+
existingContractName: Null.t<string>,
|
|
37
|
+
existingEffectiveStartBlock: Null.t<int>,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type makeSetOptions = {
|
|
41
|
+
// Only addresses whose id is at or above this. Ids are handed out in
|
|
42
|
+
// registration order, so a cursor read before a batch (`nextId`) selects
|
|
43
|
+
// exactly what that batch added.
|
|
44
|
+
minId?: int,
|
|
45
|
+
// Inclusive effectiveStartBlock bounds.
|
|
46
|
+
fromStartBlock?: int,
|
|
47
|
+
toStartBlock?: int,
|
|
48
|
+
offset?: int,
|
|
49
|
+
limit?: int,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@send
|
|
53
|
+
external newEvm: (Core.addressStoreCtor, bool, array<contract>) => t = "newEvm"
|
|
54
|
+
@send external newSvm: (Core.addressStoreCtor, array<contract>) => t = "newSvm"
|
|
55
|
+
@send external newFuel: (Core.addressStoreCtor, array<contract>) => t = "newFuel"
|
|
56
|
+
|
|
57
|
+
// The store's ecosystem is fixed here, from the chain's config, and decides how
|
|
58
|
+
// address strings are parsed and rendered back. EVM carries the chain's
|
|
59
|
+
// address-checksumming setting so entries render exactly as the sources do.
|
|
60
|
+
let make = (~ecosystem: Ecosystem.name, ~shouldChecksum: bool, ~contracts: array<contract>): t => {
|
|
61
|
+
let ctor = Core.getAddon().addressStore
|
|
62
|
+
switch ecosystem {
|
|
63
|
+
| Evm => ctor->newEvm(shouldChecksum, contracts)
|
|
64
|
+
| Svm => ctor->newSvm(contracts)
|
|
65
|
+
| Fuel => ctor->newFuel(contracts)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The contracts of a chain that have events, with the earliest block any of
|
|
70
|
+
// their events may fire at.
|
|
71
|
+
let contractsOf = (~onEventRegistrations: array<Internal.onEventRegistration>): array<contract> => {
|
|
72
|
+
let startBlocks = Dict.make()
|
|
73
|
+
let names = []
|
|
74
|
+
onEventRegistrations->Array.forEach(reg => {
|
|
75
|
+
let name = reg.eventConfig.contractName
|
|
76
|
+
switch startBlocks->Utils.Dict.dangerouslyGetNonOption(name) {
|
|
77
|
+
| None =>
|
|
78
|
+
names->Array.push(name)->ignore
|
|
79
|
+
startBlocks->Dict.set(name, reg.startBlock)
|
|
80
|
+
| Some(existing) =>
|
|
81
|
+
startBlocks->Dict.set(
|
|
82
|
+
name,
|
|
83
|
+
switch (existing, reg.startBlock) {
|
|
84
|
+
| (Some(a), Some(b)) => Some(Pervasives.min(a, b))
|
|
85
|
+
| (Some(_) as s, None) | (None, Some(_) as s) => s
|
|
86
|
+
| (None, None) => None
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
names->Array.map(name => {name, startBlock: startBlocks->Dict.getUnsafe(name)})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@send external nextId: t => int = "nextId"
|
|
95
|
+
|
|
96
|
+
// A set holding nothing — what an address-free (wildcard) partition carries, so
|
|
97
|
+
// every partition is queried through the same handle.
|
|
98
|
+
@send external emptySet: t => AddressSet.t = "emptySet"
|
|
99
|
+
|
|
100
|
+
// A set over exactly these addresses, in set order. Every set of a chain must
|
|
101
|
+
// come from that chain's one store — ids are store-scoped, so sets from
|
|
102
|
+
// different stores can't be merged.
|
|
103
|
+
@send external makeSetOf: (t, array<Address.t>) => AddressSet.t = "makeSetOf"
|
|
104
|
+
@send external registerBatchRaw: (t, array<registration>) => array<rawVerdict> = "registerBatch"
|
|
105
|
+
@send external makeSetRaw: (t, string, makeSetOptions) => AddressSet.t = "makeSet"
|
|
106
|
+
@send
|
|
107
|
+
external startBlockGroups: (t, string) => array<AddressSet.startBlockGroup> = "startBlockGroups"
|
|
108
|
+
@send external contractCount: (t, string) => int = "contractCount"
|
|
109
|
+
@send external size: t => int = "size"
|
|
110
|
+
|
|
111
|
+
// The gate routing applies, exposed for the simulate source — it has no real
|
|
112
|
+
// query boundary to gate at.
|
|
113
|
+
@send external has: (t, Address.t, string, int) => bool = "has"
|
|
114
|
+
|
|
115
|
+
// Drops every address registered after the target block, returning how many
|
|
116
|
+
// were dropped. Ids are tombstoned rather than reused, so sets built before the
|
|
117
|
+
// rollback still point at the right entries.
|
|
118
|
+
@send external rollback: (t, int) => int = "rollback"
|
|
119
|
+
|
|
120
|
+
@send external getRaw: (t, Address.t) => Null.t<Internal.indexingContract> = "get"
|
|
121
|
+
|
|
122
|
+
@send external contractAddresses: (t, string) => array<Address.t> = "contractAddresses"
|
|
123
|
+
|
|
124
|
+
@send external entries: t => array<Internal.indexingContract> = "entries"
|
|
125
|
+
|
|
126
|
+
let toVerdict = (raw: rawVerdict): verdict =>
|
|
127
|
+
switch raw.kind {
|
|
128
|
+
| "added" => Added({effectiveStartBlock: raw.effectiveStartBlock})
|
|
129
|
+
| "noEvents" => NoEvents({effectiveStartBlock: raw.effectiveStartBlock})
|
|
130
|
+
| "duplicate" =>
|
|
131
|
+
Duplicate({
|
|
132
|
+
effectiveStartBlock: raw.effectiveStartBlock,
|
|
133
|
+
existingEffectiveStartBlock: raw.existingEffectiveStartBlock->Null.getUnsafe,
|
|
134
|
+
})
|
|
135
|
+
| "conflict" => Conflict({existingContractName: raw.existingContractName->Null.getUnsafe})
|
|
136
|
+
| "invalid" => Invalid
|
|
137
|
+
| kind => JsError.throwWithMessage(`Unexpected address registration verdict "${kind}"`)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Registers a batch, resolving each address against both the store and the
|
|
141
|
+
// batch's own earlier entries — so two contracts claiming one address inside a
|
|
142
|
+
// single batch conflict just as they would across batches. Verdicts come back
|
|
143
|
+
// in the batch's order.
|
|
144
|
+
let registerBatch = (store: t, registrations: array<registration>): array<verdict> =>
|
|
145
|
+
store->registerBatchRaw(registrations)->Array.map(toVerdict)
|
|
146
|
+
|
|
147
|
+
let makeSet = (store: t, ~contractName, ~options={}: makeSetOptions) =>
|
|
148
|
+
store->makeSetRaw(contractName, options)
|
|
149
|
+
|
|
150
|
+
// The entry an address is registered under, whichever contract holds it —
|
|
151
|
+
// addresses are unique chain-wide. `None` once rolled back.
|
|
152
|
+
let get = (store: t, address) => store->getRaw(address)->Null.toOption
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Core from "../Core.res.mjs";
|
|
4
|
+
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
|
|
5
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
6
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
7
|
+
|
|
8
|
+
function make(ecosystem, shouldChecksum, contracts) {
|
|
9
|
+
let ctor = Core.getAddon().AddressStore;
|
|
10
|
+
switch (ecosystem) {
|
|
11
|
+
case "evm" :
|
|
12
|
+
return ctor.newEvm(shouldChecksum, contracts);
|
|
13
|
+
case "fuel" :
|
|
14
|
+
return ctor.newFuel(contracts);
|
|
15
|
+
case "svm" :
|
|
16
|
+
return ctor.newSvm(contracts);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function contractsOf(onEventRegistrations) {
|
|
21
|
+
let startBlocks = {};
|
|
22
|
+
let names = [];
|
|
23
|
+
onEventRegistrations.forEach(reg => {
|
|
24
|
+
let name = reg.eventConfig.contractName;
|
|
25
|
+
let existing = startBlocks[name];
|
|
26
|
+
if (existing !== undefined) {
|
|
27
|
+
let existing$1 = Primitive_option.valFromOption(existing);
|
|
28
|
+
let match = reg.startBlock;
|
|
29
|
+
startBlocks[name] = existing$1 !== undefined ? (
|
|
30
|
+
match !== undefined ? Primitive_int.min(existing$1, match) : existing$1
|
|
31
|
+
) : (
|
|
32
|
+
match !== undefined ? match : undefined
|
|
33
|
+
);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
names.push(name);
|
|
37
|
+
startBlocks[name] = reg.startBlock;
|
|
38
|
+
});
|
|
39
|
+
return names.map(name => ({
|
|
40
|
+
name: name,
|
|
41
|
+
startBlock: startBlocks[name]
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toVerdict(raw) {
|
|
46
|
+
let kind = raw.kind;
|
|
47
|
+
switch (kind) {
|
|
48
|
+
case "added" :
|
|
49
|
+
return {
|
|
50
|
+
TAG: "Added",
|
|
51
|
+
effectiveStartBlock: raw.effectiveStartBlock
|
|
52
|
+
};
|
|
53
|
+
case "conflict" :
|
|
54
|
+
return {
|
|
55
|
+
TAG: "Conflict",
|
|
56
|
+
existingContractName: raw.existingContractName
|
|
57
|
+
};
|
|
58
|
+
case "duplicate" :
|
|
59
|
+
return {
|
|
60
|
+
TAG: "Duplicate",
|
|
61
|
+
effectiveStartBlock: raw.effectiveStartBlock,
|
|
62
|
+
existingEffectiveStartBlock: raw.existingEffectiveStartBlock
|
|
63
|
+
};
|
|
64
|
+
case "invalid" :
|
|
65
|
+
return "Invalid";
|
|
66
|
+
case "noEvents" :
|
|
67
|
+
return {
|
|
68
|
+
TAG: "NoEvents",
|
|
69
|
+
effectiveStartBlock: raw.effectiveStartBlock
|
|
70
|
+
};
|
|
71
|
+
default:
|
|
72
|
+
return Stdlib_JsError.throwWithMessage(`Unexpected address registration verdict "` + kind + `"`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function registerBatch(store, registrations) {
|
|
77
|
+
return store.registerBatch(registrations).map(toVerdict);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function makeSet(store, contractName, optionsOpt) {
|
|
81
|
+
let options = optionsOpt !== undefined ? optionsOpt : ({});
|
|
82
|
+
return store.makeSet(contractName, options);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function get(store, address) {
|
|
86
|
+
return Primitive_option.fromNull(store.get(address));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
make,
|
|
91
|
+
contractsOf,
|
|
92
|
+
toVerdict,
|
|
93
|
+
registerBatch,
|
|
94
|
+
makeSet,
|
|
95
|
+
get,
|
|
96
|
+
}
|
|
97
|
+
/* Core Not a pure module */
|
package/src/sources/EvmChain.res
CHANGED
|
@@ -45,6 +45,7 @@ let makeSources = (
|
|
|
45
45
|
~hyperSync,
|
|
46
46
|
~rpcs: array<rpc>,
|
|
47
47
|
~lowercaseAddresses,
|
|
48
|
+
~addressStore,
|
|
48
49
|
) => {
|
|
49
50
|
let sources = switch hyperSync {
|
|
50
51
|
| Some(endpointUrl) => [
|
|
@@ -58,6 +59,7 @@ let makeSources = (
|
|
|
58
59
|
serializationFormat: Env.hypersyncClientSerializationFormat,
|
|
59
60
|
enableQueryCaching: Env.hypersyncClientEnableQueryCaching,
|
|
60
61
|
logLevel: Env.hypersyncLogLevel,
|
|
62
|
+
addressStore,
|
|
61
63
|
}),
|
|
62
64
|
]
|
|
63
65
|
| _ => []
|
|
@@ -70,6 +72,7 @@ let makeSources = (
|
|
|
70
72
|
url,
|
|
71
73
|
onEventRegistrations,
|
|
72
74
|
lowercaseAddresses,
|
|
75
|
+
addressStore,
|
|
73
76
|
?ws,
|
|
74
77
|
?headers,
|
|
75
78
|
})
|
|
@@ -19,7 +19,7 @@ function getSyncConfig(param) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function makeSources(chain, onEventRegistrations, hyperSync, rpcs, lowercaseAddresses) {
|
|
22
|
+
function makeSources(chain, onEventRegistrations, hyperSync, rpcs, lowercaseAddresses, addressStore) {
|
|
23
23
|
let sources = hyperSync !== undefined ? [EvmHyperSyncSource.make({
|
|
24
24
|
chain: chain,
|
|
25
25
|
endpointUrl: hyperSync,
|
|
@@ -29,7 +29,8 @@ function makeSources(chain, onEventRegistrations, hyperSync, rpcs, lowercaseAddr
|
|
|
29
29
|
lowercaseAddresses: lowercaseAddresses,
|
|
30
30
|
serializationFormat: Env.hypersyncClientSerializationFormat,
|
|
31
31
|
enableQueryCaching: Env.hypersyncClientEnableQueryCaching,
|
|
32
|
-
logLevel: Env.hypersyncLogLevel
|
|
32
|
+
logLevel: Env.hypersyncLogLevel,
|
|
33
|
+
addressStore: addressStore
|
|
33
34
|
})] : [];
|
|
34
35
|
rpcs.forEach(param => {
|
|
35
36
|
let source = RpcSource.make({
|
|
@@ -39,6 +40,7 @@ function makeSources(chain, onEventRegistrations, hyperSync, rpcs, lowercaseAddr
|
|
|
39
40
|
chain: chain,
|
|
40
41
|
onEventRegistrations: onEventRegistrations,
|
|
41
42
|
lowercaseAddresses: lowercaseAddresses,
|
|
43
|
+
addressStore: addressStore,
|
|
42
44
|
ws: param.ws,
|
|
43
45
|
headers: param.headers
|
|
44
46
|
});
|
|
@@ -16,6 +16,8 @@ type options = {
|
|
|
16
16
|
serializationFormat: HyperSyncClient.serializationFormat,
|
|
17
17
|
enableQueryCaching: bool,
|
|
18
18
|
logLevel: HyperSyncClient.logLevel,
|
|
19
|
+
// The chain's address index; the client reads it while routing.
|
|
20
|
+
addressStore: AddressStore.t,
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
let make = (
|
|
@@ -29,6 +31,7 @@ let make = (
|
|
|
29
31
|
serializationFormat,
|
|
30
32
|
enableQueryCaching,
|
|
31
33
|
logLevel,
|
|
34
|
+
addressStore,
|
|
32
35
|
}: options,
|
|
33
36
|
): t => {
|
|
34
37
|
let name = "HyperSync"
|
|
@@ -50,6 +53,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
50
53
|
~serializationFormat,
|
|
51
54
|
~enableQueryCaching,
|
|
52
55
|
~logLevel,
|
|
56
|
+
~addressStore,
|
|
53
57
|
) {
|
|
54
58
|
| client => client
|
|
55
59
|
| exception exn =>
|
|
@@ -86,8 +90,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
86
90
|
let getItemsOrThrow = async (
|
|
87
91
|
~fromBlock,
|
|
88
92
|
~toBlock,
|
|
89
|
-
~
|
|
90
|
-
~contractNameByAddress as _,
|
|
93
|
+
~addressSet,
|
|
91
94
|
~knownHeight,
|
|
92
95
|
~partitionId as _,
|
|
93
96
|
~selection: FetchState.selection,
|
|
@@ -106,7 +109,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
106
109
|
~toBlock,
|
|
107
110
|
~maxNumLogs=itemsTarget,
|
|
108
111
|
~registrationIndexes=selection.onEventRegistrations->Array.map(reg => reg.index),
|
|
109
|
-
~
|
|
112
|
+
~addressSet,
|
|
110
113
|
~clientFilteredContracts=selection.clientFilteredContracts,
|
|
111
114
|
) catch {
|
|
112
115
|
| HyperSync.GetLogs.Error(error) =>
|
|
@@ -28,7 +28,7 @@ Set the ENVIO_API_TOKEN environment variable in your .env file.
|
|
|
28
28
|
Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
29
29
|
let client;
|
|
30
30
|
try {
|
|
31
|
-
client = HyperSyncClient.make(endpointUrl, apiToken$1, param.clientTimeoutMillis, HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), !param.lowercaseAddresses, param.serializationFormat, param.enableQueryCaching, undefined, undefined, undefined, param.logLevel);
|
|
31
|
+
client = HyperSyncClient.make(endpointUrl, apiToken$1, param.clientTimeoutMillis, HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), !param.lowercaseAddresses, param.serializationFormat, param.enableQueryCaching, undefined, undefined, undefined, param.logLevel, param.addressStore);
|
|
32
32
|
} catch (raw_exn) {
|
|
33
33
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
34
34
|
client = ErrorHandling.mkLogAndRaise(undefined, "Failed to instantiate the hypersync client, please double check your ABI", exn);
|
|
@@ -52,12 +52,12 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
54
|
};
|
|
55
|
-
let getItemsOrThrow = async (fromBlock, toBlock,
|
|
55
|
+
let getItemsOrThrow = async (fromBlock, toBlock, addressSet, knownHeight, param, selection, itemsTarget, retry, param$1) => {
|
|
56
56
|
let totalTimeRef = Performance.now();
|
|
57
57
|
let startFetchingBatchTimeRef = Performance.now();
|
|
58
58
|
let pageUnsafe;
|
|
59
59
|
try {
|
|
60
|
-
pageUnsafe = await HyperSync.GetLogs.query(client, fromBlock, toBlock, itemsTarget, selection.onEventRegistrations.map(reg => reg.index),
|
|
60
|
+
pageUnsafe = await HyperSync.GetLogs.query(client, fromBlock, toBlock, itemsTarget, selection.onEventRegistrations.map(reg => reg.index), addressSet, selection.clientFilteredContracts);
|
|
61
61
|
} catch (raw_error) {
|
|
62
62
|
let error = Primitive_exceptions.internalToException(raw_error);
|
|
63
63
|
if (error.RE_EXN_ID === HyperSync.GetLogs.$$Error) {
|
|
@@ -26,7 +26,6 @@ type nextPageParams = {
|
|
|
26
26
|
// selections and the routing index are derived on the Rust side from the
|
|
27
27
|
// registrations passed at construction.
|
|
28
28
|
registrationIndexes: array<int>,
|
|
29
|
-
addressesByContractName: dict<array<Address.t>>,
|
|
30
29
|
// Contract names to fetch address-free even though their registrations
|
|
31
30
|
// depend on addresses (client-side filtering). None/empty means
|
|
32
31
|
// every address-dependent contract is filtered server-side.
|
|
@@ -42,7 +41,7 @@ type nextPageResponse = {
|
|
|
42
41
|
// The caller provides a range; Rust decides the actual `toBlock` and returns it.
|
|
43
42
|
type t = {
|
|
44
43
|
getHeight: unit => promise<int>,
|
|
45
|
-
getNextPage: nextPageParams => promise<nextPageResponse>,
|
|
44
|
+
getNextPage: (nextPageParams, AddressSet.t) => promise<nextPageResponse>,
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
@send
|
|
@@ -51,6 +50,7 @@ external classNew: (
|
|
|
51
50
|
cfg,
|
|
52
51
|
array<HyperSyncClient.Registration.input>,
|
|
53
52
|
~checksumAddresses: bool,
|
|
53
|
+
~addressStore: AddressStore.t,
|
|
54
54
|
) => t = "new"
|
|
55
55
|
|
|
56
56
|
// Rust encodes JSON-RPC errors as a JSON payload in the napi error's
|
|
@@ -89,6 +89,7 @@ let make = (
|
|
|
89
89
|
~httpReqTimeoutMillis=?,
|
|
90
90
|
~headers=?,
|
|
91
91
|
~eventRegistrations=[],
|
|
92
|
+
~addressStore,
|
|
92
93
|
) => {
|
|
93
94
|
let client = Core.getAddon().evmRpcClient->classNew(
|
|
94
95
|
{
|
|
@@ -104,9 +105,11 @@ let make = (
|
|
|
104
105
|
},
|
|
105
106
|
eventRegistrations,
|
|
106
107
|
~checksumAddresses,
|
|
108
|
+
~addressStore,
|
|
107
109
|
)
|
|
108
110
|
{
|
|
109
111
|
getHeight: () => client.getHeight()->Promise.catch(coerceErrorOrThrow),
|
|
110
|
-
getNextPage: params =>
|
|
112
|
+
getNextPage: (params, addressSet) =>
|
|
113
|
+
client.getNextPage(params, addressSet)->Promise.catch(coerceErrorOrThrow),
|
|
111
114
|
}
|
|
112
115
|
}
|
|
@@ -46,7 +46,7 @@ function coerceErrorOrThrow(exn) {
|
|
|
46
46
|
throw exn;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers, eventRegistrationsOpt) {
|
|
49
|
+
function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers, eventRegistrationsOpt, addressStore) {
|
|
50
50
|
let eventRegistrations = eventRegistrationsOpt !== undefined ? eventRegistrationsOpt : [];
|
|
51
51
|
let client = Core.getAddon().EvmRpcClient.new({
|
|
52
52
|
url: url,
|
|
@@ -58,10 +58,10 @@ function make(url, checksumAddresses, syncConfig, httpReqTimeoutMillis, headers,
|
|
|
58
58
|
intervalCeiling: syncConfig.intervalCeiling,
|
|
59
59
|
backoffMillis: syncConfig.backoffMillis,
|
|
60
60
|
queryTimeoutMillis: syncConfig.queryTimeoutMillis
|
|
61
|
-
}, eventRegistrations, checksumAddresses);
|
|
61
|
+
}, eventRegistrations, checksumAddresses, addressStore);
|
|
62
62
|
return {
|
|
63
63
|
getHeight: () => Stdlib_Promise.$$catch(client.getHeight(), coerceErrorOrThrow),
|
|
64
|
-
getNextPage: params => Stdlib_Promise.$$catch(client.getNextPage(params), coerceErrorOrThrow)
|
|
64
|
+
getNextPage: (params, addressSet) => Stdlib_Promise.$$catch(client.getNextPage(params, addressSet), coerceErrorOrThrow)
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -43,16 +43,17 @@ module GetLogs = {
|
|
|
43
43
|
~fromBlock,
|
|
44
44
|
~toBlock,
|
|
45
45
|
~registrationIndexes,
|
|
46
|
-
~
|
|
46
|
+
~addressSet,
|
|
47
|
+
~clientFilteredContracts,
|
|
47
48
|
): logsQueryPage => {
|
|
48
49
|
let query: FuelHyperSyncClient.EventItems.query = {
|
|
49
50
|
fromBlock,
|
|
50
51
|
toBlock,
|
|
51
52
|
registrationIndexes,
|
|
52
|
-
|
|
53
|
+
clientFilteredContracts,
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
let res = switch await client->FuelHyperSyncClient.getEventItems(query) {
|
|
56
|
+
let res = switch await client->FuelHyperSyncClient.getEventItems(query, addressSet) {
|
|
56
57
|
| res => res
|
|
57
58
|
| exception exn =>
|
|
58
59
|
switch exn->extractMissingParams {
|
|
@@ -29,16 +29,16 @@ function extractMissingParams(exn) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async function query(client, fromBlock, toBlock, registrationIndexes,
|
|
32
|
+
async function query(client, fromBlock, toBlock, registrationIndexes, addressSet, clientFilteredContracts) {
|
|
33
33
|
let query$1 = {
|
|
34
34
|
fromBlock: fromBlock,
|
|
35
35
|
toBlock: toBlock,
|
|
36
36
|
registrationIndexes: registrationIndexes,
|
|
37
|
-
|
|
37
|
+
clientFilteredContracts: clientFilteredContracts
|
|
38
38
|
};
|
|
39
39
|
let res;
|
|
40
40
|
try {
|
|
41
|
-
res = await client.getEventItems(query$1);
|
|
41
|
+
res = await client.getEventItems(query$1, addressSet);
|
|
42
42
|
} catch (raw_exn) {
|
|
43
43
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
44
44
|
let missingParams = extractMissingParams(exn);
|
|
@@ -17,6 +17,7 @@ module GetLogs: {
|
|
|
17
17
|
~fromBlock: int,
|
|
18
18
|
~toBlock: option<int>,
|
|
19
19
|
~registrationIndexes: array<int>,
|
|
20
|
-
~
|
|
20
|
+
~addressSet: AddressSet.t,
|
|
21
|
+
~clientFilteredContracts: option<array<string>>,
|
|
21
22
|
) => promise<logsQueryPage>
|
|
22
23
|
}
|
|
@@ -52,15 +52,18 @@ module Registration = {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
module EventItems = {
|
|
55
|
-
// The whole per-query input
|
|
56
|
-
// selection (by index)
|
|
57
|
-
//
|
|
55
|
+
// The whole per-query input beside the partition's address set: block range
|
|
56
|
+
// and the registration selection (by index). Receipt selections, field
|
|
57
|
+
// selection, and routing are derived on the Rust side.
|
|
58
58
|
type query = {
|
|
59
59
|
fromBlock: int,
|
|
60
60
|
// Inclusive; None queries to the end of available data.
|
|
61
61
|
toBlock: option<int>,
|
|
62
62
|
registrationIndexes: array<int>,
|
|
63
|
-
|
|
63
|
+
// Contract names to fetch address-free even though their registrations
|
|
64
|
+
// depend on addresses (client-side filtering). None/empty means every
|
|
65
|
+
// address-dependent contract is filtered server-side.
|
|
66
|
+
clientFilteredContracts: option<array<string>>,
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
// One routed receipt with its kind-specific columns flattened: LogData
|
|
@@ -102,19 +105,22 @@ external classNew: (
|
|
|
102
105
|
cfg,
|
|
103
106
|
~userAgent: string,
|
|
104
107
|
array<Registration.input>,
|
|
108
|
+
AddressStore.t,
|
|
105
109
|
) => t = "new"
|
|
106
110
|
|
|
107
|
-
let make = (cfg: cfg, ~eventRegistrations) => {
|
|
111
|
+
let make = (cfg: cfg, ~eventRegistrations, ~addressStore) => {
|
|
108
112
|
let envioVersion = Utils.EnvioPackage.value.version
|
|
109
113
|
Core.getAddon().fuelHyperSyncClient->classNew(
|
|
110
114
|
cfg,
|
|
111
115
|
~userAgent=`hyperindex/${envioVersion}`,
|
|
112
116
|
eventRegistrations,
|
|
117
|
+
addressStore,
|
|
113
118
|
)
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
@send
|
|
117
|
-
external getEventItems: (t, EventItems.query) => promise<EventItems.response> =
|
|
122
|
+
external getEventItems: (t, EventItems.query, AddressSet.t) => promise<EventItems.response> =
|
|
123
|
+
"getEventItems"
|
|
118
124
|
|
|
119
125
|
@send
|
|
120
126
|
external getHeight: t => promise<int> = "getHeight"
|
|
@@ -58,9 +58,9 @@ let Registration = {
|
|
|
58
58
|
|
|
59
59
|
let EventItems = {};
|
|
60
60
|
|
|
61
|
-
function make(cfg, eventRegistrations) {
|
|
61
|
+
function make(cfg, eventRegistrations, addressStore) {
|
|
62
62
|
let envioVersion = Utils.EnvioPackage.value.version;
|
|
63
|
-
return Core.getAddon().FuelHyperSyncClient.new(cfg, `hyperindex/` + envioVersion, eventRegistrations);
|
|
63
|
+
return Core.getAddon().FuelHyperSyncClient.new(cfg, `hyperindex/` + envioVersion, eventRegistrations, addressStore);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
export {
|
|
@@ -8,9 +8,11 @@ type options = {
|
|
|
8
8
|
apiToken: option<string>,
|
|
9
9
|
// The chain's registrations, indexed by their sequential `index`.
|
|
10
10
|
onEventRegistrations: array<Internal.fuelOnEventRegistration>,
|
|
11
|
+
// The chain's address index; the client reads it while routing.
|
|
12
|
+
addressStore: AddressStore.t,
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
let make = ({chain, endpointUrl, apiToken, onEventRegistrations}: options): t => {
|
|
15
|
+
let make = ({chain, endpointUrl, apiToken, onEventRegistrations, addressStore}: options): t => {
|
|
14
16
|
let name = "HyperFuel"
|
|
15
17
|
|
|
16
18
|
let apiToken = switch apiToken {
|
|
@@ -24,6 +26,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
24
26
|
let client = switch FuelHyperSyncClient.make(
|
|
25
27
|
{url: endpointUrl, apiToken},
|
|
26
28
|
~eventRegistrations=FuelHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations),
|
|
29
|
+
~addressStore,
|
|
27
30
|
) {
|
|
28
31
|
| client => client
|
|
29
32
|
| exception exn =>
|
|
@@ -33,8 +36,7 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
33
36
|
let getItemsOrThrow = async (
|
|
34
37
|
~fromBlock,
|
|
35
38
|
~toBlock,
|
|
36
|
-
~
|
|
37
|
-
~contractNameByAddress as _,
|
|
39
|
+
~addressSet,
|
|
38
40
|
~knownHeight,
|
|
39
41
|
~partitionId as _,
|
|
40
42
|
~selection: FetchState.selection,
|
|
@@ -52,7 +54,8 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
52
54
|
~fromBlock,
|
|
53
55
|
~toBlock,
|
|
54
56
|
~registrationIndexes=selection.onEventRegistrations->Array.map(reg => reg.index),
|
|
55
|
-
~
|
|
57
|
+
~addressSet,
|
|
58
|
+
~clientFilteredContracts=selection.clientFilteredContracts,
|
|
56
59
|
) catch {
|
|
57
60
|
| FuelHyperSync.GetLogs.Error(error) =>
|
|
58
61
|
throw(
|
|
@@ -27,17 +27,17 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
|
27
27
|
client = FuelHyperSyncClient.make({
|
|
28
28
|
url: param.endpointUrl,
|
|
29
29
|
apiToken: apiToken$1
|
|
30
|
-
}, FuelHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations));
|
|
30
|
+
}, FuelHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), param.addressStore);
|
|
31
31
|
} catch (raw_exn) {
|
|
32
32
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
33
33
|
client = ErrorHandling.mkLogAndRaise(undefined, "Failed to instantiate the HyperFuel client", exn);
|
|
34
34
|
}
|
|
35
|
-
let getItemsOrThrow = async (fromBlock, toBlock,
|
|
35
|
+
let getItemsOrThrow = async (fromBlock, toBlock, addressSet, knownHeight, param, selection, param$1, retry, logger) => {
|
|
36
36
|
let totalTimeRef = Performance.now();
|
|
37
37
|
let startFetchingBatchTimeRef = Performance.now();
|
|
38
38
|
let pageUnsafe;
|
|
39
39
|
try {
|
|
40
|
-
pageUnsafe = await FuelHyperSync.GetLogs.query(client, fromBlock, toBlock, selection.onEventRegistrations.map(reg => reg.index),
|
|
40
|
+
pageUnsafe = await FuelHyperSync.GetLogs.query(client, fromBlock, toBlock, selection.onEventRegistrations.map(reg => reg.index), addressSet, selection.clientFilteredContracts);
|
|
41
41
|
} catch (raw_error) {
|
|
42
42
|
let error = Primitive_exceptions.internalToException(raw_error);
|
|
43
43
|
if (error.RE_EXN_ID === FuelHyperSync.GetLogs.$$Error) {
|
|
@@ -16,7 +16,7 @@ let reraisIfRateLimited = exn =>
|
|
|
16
16
|
|
|
17
17
|
type logsQueryPage = {
|
|
18
18
|
items: array<HyperSyncClient.EventItems.item>,
|
|
19
|
-
//
|
|
19
|
+
// Headers for every returned block, deduplicated by block number.
|
|
20
20
|
blocks: array<HyperSyncClient.EventItems.blockHeader>,
|
|
21
21
|
nextBlock: int,
|
|
22
22
|
archiveHeight: int,
|
|
@@ -92,7 +92,7 @@ module GetLogs = {
|
|
|
92
92
|
~toBlock,
|
|
93
93
|
~maxNumLogs,
|
|
94
94
|
~registrationIndexes,
|
|
95
|
-
~
|
|
95
|
+
~addressSet,
|
|
96
96
|
~clientFilteredContracts,
|
|
97
97
|
): logsQueryPage => {
|
|
98
98
|
let query: HyperSyncClient.EventItems.query = {
|
|
@@ -100,11 +100,13 @@ module GetLogs = {
|
|
|
100
100
|
toBlock,
|
|
101
101
|
?maxNumLogs,
|
|
102
102
|
registrationIndexes,
|
|
103
|
-
addressesByContractName,
|
|
104
103
|
clientFilteredContracts,
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
let (res, transactionStore, blockStore) = switch await client.getEventItems(
|
|
106
|
+
let (res, transactionStore, blockStore) = switch await client.getEventItems(
|
|
107
|
+
~query,
|
|
108
|
+
~addressSet,
|
|
109
|
+
) {
|
|
108
110
|
| res => res
|
|
109
111
|
| exception exn =>
|
|
110
112
|
reraisIfRateLimited(exn)
|
|
@@ -82,18 +82,17 @@ function extractMissingParams(exn) {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
async function query(client, fromBlock, toBlock, maxNumLogs, registrationIndexes,
|
|
85
|
+
async function query(client, fromBlock, toBlock, maxNumLogs, registrationIndexes, addressSet, clientFilteredContracts) {
|
|
86
86
|
let query$1 = {
|
|
87
87
|
fromBlock: fromBlock,
|
|
88
88
|
toBlock: toBlock,
|
|
89
89
|
maxNumLogs: maxNumLogs,
|
|
90
90
|
registrationIndexes: registrationIndexes,
|
|
91
|
-
addressesByContractName: addressesByContractName,
|
|
92
91
|
clientFilteredContracts: clientFilteredContracts
|
|
93
92
|
};
|
|
94
93
|
let match;
|
|
95
94
|
try {
|
|
96
|
-
match = await client.getEventItems(query$1);
|
|
95
|
+
match = await client.getEventItems(query$1, addressSet);
|
|
97
96
|
} catch (raw_exn) {
|
|
98
97
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
99
98
|
reraisIfRateLimited(exn);
|