envio 3.10.0 → 3.11.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/evm.schema.json +21 -4
- package/fuel.schema.json +21 -4
- package/index.d.ts +7 -6
- package/package.json +6 -6
- package/src/ChainState.res +9 -73
- package/src/ChainState.res.mjs +6 -58
- package/src/Config.res +73 -18
- package/src/Config.res.mjs +69 -16
- package/src/EventConfigBuilder.res +14 -10
- package/src/EventConfigBuilder.res.mjs +6 -4
- package/src/HandlerRegister.res +12 -11
- package/src/HandlerRegister.res.mjs +6 -5
- package/src/Internal.res +20 -4
- package/src/Internal.res.mjs +9 -0
- package/src/Main.res +8 -1
- package/src/Main.res.mjs +3 -3
- package/src/Persistence.res +11 -1
- package/src/Persistence.res.mjs +6 -2
- package/src/PgStorage.res +1 -1
- package/src/PgStorage.res.mjs +1 -1
- package/src/SimulateItems.res +13 -6
- package/src/SimulateItems.res.mjs +7 -6
- package/src/TestIndexer.res +6 -5
- package/src/TestIndexer.res.mjs +5 -4
- package/src/db/InternalTable.res +1 -1
- package/src/db/InternalTable.res.mjs +2 -1
- package/src/sources/ChainSources.res +72 -0
- package/src/sources/ChainSources.res.mjs +55 -0
- package/src/sources/EvmHyperSyncSource.res +10 -22
- package/src/sources/EvmHyperSyncSource.res.mjs +9 -41
- package/src/sources/FuelHyperSyncSource.res +10 -18
- package/src/sources/FuelHyperSyncSource.res.mjs +8 -38
- package/src/sources/HyperSync.res +31 -0
- package/src/sources/HyperSync.res.mjs +31 -0
- package/src/sources/HyperSync.resi +16 -0
- package/src/sources/StartBlockResolver.res +173 -0
- package/src/sources/StartBlockResolver.res.mjs +134 -0
- package/src/sources/Svm.res +0 -51
- package/src/sources/Svm.res.mjs +0 -46
- package/src/sources/SvmHyperSyncSource.res +20 -9
- package/src/sources/SvmHyperSyncSource.res.mjs +34 -12
- package/svm.schema.json +80 -66
package/src/TestIndexer.res.mjs
CHANGED
|
@@ -268,16 +268,17 @@ function parseBlockRange(chainIdStr, config, rawChainConfig, progressBlock) {
|
|
|
268
268
|
Stdlib_JsError.throwWithMessage(`Chain ` + chainIdStr + ` is not configured in config.yaml`);
|
|
269
269
|
}
|
|
270
270
|
let configChain = ChainMap.get(config.chainMap, chain);
|
|
271
|
+
let configStartBlock = Config.startBlockOrZero(configChain);
|
|
271
272
|
let sb = rawChainConfig.startBlock;
|
|
272
273
|
let startBlock = sb !== undefined ? sb : (
|
|
273
|
-
progressBlock !== undefined ? progressBlock + 1 | 0 :
|
|
274
|
+
progressBlock !== undefined ? progressBlock + 1 | 0 : configStartBlock
|
|
274
275
|
);
|
|
275
276
|
let eb = rawChainConfig.endBlock;
|
|
276
277
|
let endBlock = eb !== undefined ? eb : (
|
|
277
278
|
Stdlib_Option.isSome(rawChainConfig.simulate) ? getSimulateEndBlock(Stdlib_Option.getOrThrow(rawChainConfig.simulate, undefined), config, startBlock) : undefined
|
|
278
279
|
);
|
|
279
|
-
if (startBlock <
|
|
280
|
-
Stdlib_JsError.throwWithMessage(`Invalid block range for chain ` + chainIdStr + `: startBlock (` + startBlock.toString() + `) is less than config.startBlock (` +
|
|
280
|
+
if (startBlock < configStartBlock) {
|
|
281
|
+
Stdlib_JsError.throwWithMessage(`Invalid block range for chain ` + chainIdStr + `: startBlock (` + startBlock.toString() + `) is less than config.startBlock (` + configStartBlock.toString() + `). ` + (`Either use startBlock >= ` + configStartBlock.toString() + ` or create a new test indexer with createTestIndexer().`));
|
|
281
282
|
}
|
|
282
283
|
let match = configChain.endBlock;
|
|
283
284
|
if (endBlock !== undefined && match !== undefined && endBlock > match) {
|
|
@@ -480,7 +481,7 @@ function createTestIndexer() {
|
|
|
480
481
|
value: chainConfig.id
|
|
481
482
|
}), "startBlock", {
|
|
482
483
|
enumerable: true,
|
|
483
|
-
value: chainConfig
|
|
484
|
+
value: Config.startBlockOrZero(chainConfig)
|
|
484
485
|
}), "endBlock", {
|
|
485
486
|
enumerable: true,
|
|
486
487
|
value: chainConfig.endBlock
|
package/src/db/InternalTable.res
CHANGED
|
@@ -272,7 +272,7 @@ module Chains = {
|
|
|
272
272
|
{
|
|
273
273
|
id: chainConfig.id,
|
|
274
274
|
ecosystem: (chainConfig.ecosystem: Ecosystem.name :> string),
|
|
275
|
-
startBlock: chainConfig.
|
|
275
|
+
startBlock: chainConfig->Config.startBlockOrThrow,
|
|
276
276
|
endBlock: chainConfig.endBlock->Null.fromOption,
|
|
277
277
|
maxReorgDepth: chainConfig.maxReorgDepth,
|
|
278
278
|
blockHeight: 0,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Table from "./Table.res.mjs";
|
|
4
4
|
import * as Utils from "../Utils.res.mjs";
|
|
5
|
+
import * as Config from "../Config.res.mjs";
|
|
5
6
|
import * as Address from "../Address.res.mjs";
|
|
6
7
|
import * as ChainId from "../ChainId.res.mjs";
|
|
7
8
|
import * as Postgres from "../bindings/Postgres.res.mjs";
|
|
@@ -181,7 +182,7 @@ function initialFromConfig(chainConfig) {
|
|
|
181
182
|
return {
|
|
182
183
|
id: chainConfig.id,
|
|
183
184
|
ecosystem: chainConfig.ecosystem,
|
|
184
|
-
start_block: chainConfig
|
|
185
|
+
start_block: Config.startBlockOrThrow(chainConfig),
|
|
185
186
|
end_block: Stdlib_Null.fromOption(chainConfig.endBlock),
|
|
186
187
|
max_reorg_depth: chainConfig.maxReorgDepth,
|
|
187
188
|
source_block: 0,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Sits below `ChainState`/`Persistence` in the module graph so that
|
|
2
|
+
// `StartBlockResolver` (called from `Persistence.init`) can build a chain's
|
|
3
|
+
// sources without a dependency cycle.
|
|
4
|
+
let make = (
|
|
5
|
+
~chainConfig: Config.chain,
|
|
6
|
+
~onEventRegistrations: array<Internal.onEventRegistration>,
|
|
7
|
+
~addressStore: AddressStore.t,
|
|
8
|
+
~lowercaseAddresses: bool,
|
|
9
|
+
): array<Source.t> => {
|
|
10
|
+
let chainId = chainConfig.id
|
|
11
|
+
switch chainConfig.sourceConfig {
|
|
12
|
+
| Config.EvmSourceConfig({hypersync, rpcs}) =>
|
|
13
|
+
let evmRpcs: array<EvmChain.rpc> = rpcs->Array.map((rpc): EvmChain.rpc => {
|
|
14
|
+
let syncConfig = rpc.syncConfig
|
|
15
|
+
let ws = rpc.ws
|
|
16
|
+
let headers = rpc.headers
|
|
17
|
+
{
|
|
18
|
+
url: rpc.url,
|
|
19
|
+
sourceFor: rpc.sourceFor,
|
|
20
|
+
?syncConfig,
|
|
21
|
+
?ws,
|
|
22
|
+
?headers,
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
EvmChain.makeSources(
|
|
26
|
+
~chainId,
|
|
27
|
+
~onEventRegistrations=onEventRegistrations->(
|
|
28
|
+
Utils.magic: array<Internal.onEventRegistration> => array<Internal.evmOnEventRegistration>
|
|
29
|
+
),
|
|
30
|
+
~hyperSync=hypersync,
|
|
31
|
+
~rpcs=evmRpcs,
|
|
32
|
+
~lowercaseAddresses,
|
|
33
|
+
~addressStore,
|
|
34
|
+
)
|
|
35
|
+
| Config.FuelSourceConfig({hypersync}) => [
|
|
36
|
+
FuelHyperSyncSource.make({
|
|
37
|
+
chainId,
|
|
38
|
+
endpointUrl: hypersync,
|
|
39
|
+
apiToken: Env.envioApiToken,
|
|
40
|
+
onEventRegistrations,
|
|
41
|
+
addressStore,
|
|
42
|
+
}),
|
|
43
|
+
]
|
|
44
|
+
| Config.SvmSourceConfig({hypersync}) => [
|
|
45
|
+
SvmHyperSyncSource.make({
|
|
46
|
+
chainId,
|
|
47
|
+
endpointUrl: hypersync,
|
|
48
|
+
apiToken: Env.envioApiToken,
|
|
49
|
+
onEventRegistrations: onEventRegistrations->(
|
|
50
|
+
Utils.magic: array<Internal.onEventRegistration> => array<
|
|
51
|
+
Internal.svmOnEventRegistration,
|
|
52
|
+
>
|
|
53
|
+
),
|
|
54
|
+
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
|
|
55
|
+
addressStore,
|
|
56
|
+
}),
|
|
57
|
+
]
|
|
58
|
+
| Config.SimulateSourceConfig({items, endBlock, ?transactionStore, ?blockStore}) => [
|
|
59
|
+
SimulateSource.make(
|
|
60
|
+
~items,
|
|
61
|
+
~endBlock,
|
|
62
|
+
~chainId,
|
|
63
|
+
~addressStore,
|
|
64
|
+
~ecosystem=chainConfig.ecosystem,
|
|
65
|
+
~transactionStore,
|
|
66
|
+
~blockStore,
|
|
67
|
+
),
|
|
68
|
+
]
|
|
69
|
+
// For tests: use ready-to-use sources directly
|
|
70
|
+
| Config.CustomSources(sources) => sources
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Env from "../Env.res.mjs";
|
|
4
|
+
import * as EvmChain from "./EvmChain.res.mjs";
|
|
5
|
+
import * as SimulateSource from "./SimulateSource.res.mjs";
|
|
6
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
7
|
+
import * as SvmHyperSyncSource from "./SvmHyperSyncSource.res.mjs";
|
|
8
|
+
import * as FuelHyperSyncSource from "./FuelHyperSyncSource.res.mjs";
|
|
9
|
+
|
|
10
|
+
function make(chainConfig, onEventRegistrations, addressStore, lowercaseAddresses) {
|
|
11
|
+
let chainId = chainConfig.id;
|
|
12
|
+
let sources = chainConfig.sourceConfig;
|
|
13
|
+
switch (sources.TAG) {
|
|
14
|
+
case "EvmSourceConfig" :
|
|
15
|
+
let evmRpcs = sources.rpcs.map(rpc => {
|
|
16
|
+
let syncConfig = rpc.syncConfig;
|
|
17
|
+
let ws = rpc.ws;
|
|
18
|
+
let headers = rpc.headers;
|
|
19
|
+
return {
|
|
20
|
+
url: rpc.url,
|
|
21
|
+
sourceFor: rpc.sourceFor,
|
|
22
|
+
syncConfig: syncConfig,
|
|
23
|
+
ws: ws,
|
|
24
|
+
headers: headers
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
return EvmChain.makeSources(chainId, onEventRegistrations, sources.hypersync, evmRpcs, lowercaseAddresses, addressStore);
|
|
28
|
+
case "FuelSourceConfig" :
|
|
29
|
+
return [FuelHyperSyncSource.make({
|
|
30
|
+
chainId: chainId,
|
|
31
|
+
endpointUrl: sources.hypersync,
|
|
32
|
+
apiToken: Env.envioApiToken,
|
|
33
|
+
onEventRegistrations: onEventRegistrations,
|
|
34
|
+
addressStore: addressStore
|
|
35
|
+
})];
|
|
36
|
+
case "SvmSourceConfig" :
|
|
37
|
+
return [SvmHyperSyncSource.make({
|
|
38
|
+
chainId: chainId,
|
|
39
|
+
endpointUrl: sources.hypersync,
|
|
40
|
+
apiToken: Env.envioApiToken,
|
|
41
|
+
onEventRegistrations: onEventRegistrations,
|
|
42
|
+
clientTimeoutMillis: Env.hyperSyncClientTimeoutMillis,
|
|
43
|
+
addressStore: addressStore
|
|
44
|
+
})];
|
|
45
|
+
case "SimulateSourceConfig" :
|
|
46
|
+
return [SimulateSource.make(sources.items, sources.endBlock, chainId, addressStore, chainConfig.ecosystem, Primitive_option.some(sources.transactionStore), Primitive_option.some(sources.blockStore))];
|
|
47
|
+
case "CustomSources" :
|
|
48
|
+
return sources._0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
make,
|
|
54
|
+
}
|
|
55
|
+
/* Env Not a pure module */
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
open Source
|
|
2
2
|
|
|
3
|
-
// Surfaced by the HyperSync client (Rust) when HyperSync rejects the API
|
|
4
|
-
// token. The corrupted-token test feeds the real server error (from the query
|
|
5
|
-
// endpoint; the edge no longer 401s malformed tokens on /height) through this
|
|
6
|
-
// check so it can't silently drift away from what getHeightOrThrow guards on.
|
|
7
|
-
let isUnauthorizedError = (message: string) => message->String.includes("401 Unauthorized")
|
|
8
|
-
|
|
9
3
|
type options = {
|
|
10
4
|
chainId: ChainId.t,
|
|
11
5
|
endpointUrl: string,
|
|
@@ -37,13 +31,11 @@ let make = (
|
|
|
37
31
|
): t => {
|
|
38
32
|
let name = "HyperSync"
|
|
39
33
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
46
|
-
}
|
|
34
|
+
// Per source, so one rejected token is reported once rather than on every
|
|
35
|
+
// height retry for the life of the process.
|
|
36
|
+
let unauthorizedWarned = ref(false)
|
|
37
|
+
|
|
38
|
+
let apiToken = apiToken->HyperSync.requireApiToken
|
|
47
39
|
|
|
48
40
|
let client = switch HyperSyncClient.make(
|
|
49
41
|
~url=endpointUrl,
|
|
@@ -213,15 +205,11 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
213
205
|
let height = try {
|
|
214
206
|
await client.getHeight()
|
|
215
207
|
} catch {
|
|
216
|
-
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
let _ = await Promise.make((_, _) => ())
|
|
222
|
-
0
|
|
223
|
-
| _ => throw(JsExn(e))
|
|
224
|
-
}
|
|
208
|
+
| exn =>
|
|
209
|
+
exn->HyperSync.rethrowLoggingUnauthorized(
|
|
210
|
+
~warned=unauthorizedWarned,
|
|
211
|
+
~product="HyperSync",
|
|
212
|
+
)
|
|
225
213
|
}
|
|
226
214
|
let seconds = timerRef->Performance.secondsSince
|
|
227
215
|
{height, requestStats: [{method: "getHeight", seconds}]}
|
|
@@ -1,33 +1,26 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Source from "./Source.res.mjs";
|
|
4
|
-
import * as Logging from "../Logging.res.mjs";
|
|
5
4
|
import * as HyperSync from "./HyperSync.res.mjs";
|
|
6
5
|
import * as Performance from "../bindings/Performance.res.mjs";
|
|
7
6
|
import * as HyperSyncSSE from "./HyperSyncSSE.res.mjs";
|
|
8
|
-
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
9
7
|
import * as ErrorHandling from "../ErrorHandling.res.mjs";
|
|
10
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
11
|
-
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
12
9
|
import * as HyperSyncClient from "./HyperSyncClient.res.mjs";
|
|
13
10
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
14
11
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
15
12
|
|
|
16
|
-
function isUnauthorizedError(message) {
|
|
17
|
-
return message.includes("401 Unauthorized");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
13
|
function make(param) {
|
|
21
|
-
let apiToken = param.apiToken;
|
|
22
14
|
let onEventRegistrations = param.onEventRegistrations;
|
|
23
15
|
let endpointUrl = param.endpointUrl;
|
|
24
16
|
let chainId = param.chainId;
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
let unauthorizedWarned = {
|
|
18
|
+
contents: false
|
|
19
|
+
};
|
|
20
|
+
let apiToken = HyperSync.requireApiToken(param.apiToken);
|
|
28
21
|
let client;
|
|
29
22
|
try {
|
|
30
|
-
client = HyperSyncClient.make(endpointUrl, apiToken
|
|
23
|
+
client = HyperSyncClient.make(endpointUrl, apiToken, param.clientTimeoutMillis, HyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), !param.lowercaseAddresses, param.serializationFormat, param.enableQueryCaching, undefined, undefined, undefined, param.logLevel, param.addressStore);
|
|
31
24
|
} catch (raw_exn) {
|
|
32
25
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
33
26
|
client = ErrorHandling.mkLogAndRaise(undefined, "Failed to instantiate the hypersync client, please double check your ABI", exn);
|
|
@@ -150,33 +143,9 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
|
150
143
|
let height;
|
|
151
144
|
try {
|
|
152
145
|
height = await client.getHeight();
|
|
153
|
-
} catch (
|
|
154
|
-
let
|
|
155
|
-
|
|
156
|
-
let e$1 = e._1;
|
|
157
|
-
let message = Stdlib_JsExn.message(e$1);
|
|
158
|
-
if (message !== undefined) {
|
|
159
|
-
if (message.includes("401 Unauthorized")) {
|
|
160
|
-
Logging.error(`Your ENVIO_API_TOKEN was rejected by HyperSync (401 Unauthorized). The indexer will not be able to fetch events. Update the token and try again using 'envio start' or 'envio dev'. For more info: https://docs.envio.dev/docs/HyperSync/api-tokens`);
|
|
161
|
-
await new Promise((param, param$1) => {});
|
|
162
|
-
height = 0;
|
|
163
|
-
} else {
|
|
164
|
-
throw {
|
|
165
|
-
RE_EXN_ID: "JsExn",
|
|
166
|
-
_1: e$1,
|
|
167
|
-
Error: new Error()
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
171
|
-
throw {
|
|
172
|
-
RE_EXN_ID: "JsExn",
|
|
173
|
-
_1: e$1,
|
|
174
|
-
Error: new Error()
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
throw e;
|
|
179
|
-
}
|
|
146
|
+
} catch (raw_exn) {
|
|
147
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
148
|
+
height = HyperSync.rethrowLoggingUnauthorized(exn, unauthorizedWarned, "HyperSync");
|
|
180
149
|
}
|
|
181
150
|
let seconds = Performance.secondsSince(timerRef);
|
|
182
151
|
return {
|
|
@@ -188,12 +157,11 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
|
188
157
|
};
|
|
189
158
|
},
|
|
190
159
|
getItemsOrThrow: getItemsOrThrow,
|
|
191
|
-
createHeightSubscription: (onHeight, onStatus) => HyperSyncSSE.subscribe(endpointUrl, apiToken
|
|
160
|
+
createHeightSubscription: (onHeight, onStatus) => HyperSyncSSE.subscribe(endpointUrl, apiToken, onHeight, onStatus)
|
|
192
161
|
};
|
|
193
162
|
}
|
|
194
163
|
|
|
195
164
|
export {
|
|
196
|
-
isUnauthorizedError,
|
|
197
165
|
make,
|
|
198
166
|
}
|
|
199
167
|
/* Source Not a pure module */
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
open Source
|
|
2
2
|
|
|
3
|
-
let isUnauthorizedError = (message: string) => message->String.includes("401 Unauthorized")
|
|
4
|
-
|
|
5
3
|
type options = {
|
|
6
4
|
chainId: ChainId.t,
|
|
7
5
|
endpointUrl: string,
|
|
@@ -15,13 +13,11 @@ type options = {
|
|
|
15
13
|
let make = ({chainId, endpointUrl, apiToken, onEventRegistrations, addressStore}: options): t => {
|
|
16
14
|
let name = "HyperFuel"
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
24
|
-
}
|
|
16
|
+
// Per source, so one rejected token is reported once rather than on every
|
|
17
|
+
// height retry for the life of the process.
|
|
18
|
+
let unauthorizedWarned = ref(false)
|
|
19
|
+
|
|
20
|
+
let apiToken = apiToken->HyperSync.requireApiToken
|
|
25
21
|
|
|
26
22
|
let client = switch FuelHyperSyncClient.make(
|
|
27
23
|
{url: endpointUrl, apiToken},
|
|
@@ -213,15 +209,11 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
|
213
209
|
getHeightOrThrow: async () => {
|
|
214
210
|
let timerRef = Performance.now()
|
|
215
211
|
let height = try await client->FuelHyperSyncClient.getHeight catch {
|
|
216
|
-
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
let _ = await Promise.make((_, _) => ())
|
|
222
|
-
0
|
|
223
|
-
| _ => throw(JsExn(e))
|
|
224
|
-
}
|
|
212
|
+
| exn =>
|
|
213
|
+
exn->HyperSync.rethrowLoggingUnauthorized(
|
|
214
|
+
~warned=unauthorizedWarned,
|
|
215
|
+
~product="HyperFuel",
|
|
216
|
+
)
|
|
225
217
|
}
|
|
226
218
|
let seconds = timerRef->Performance.secondsSince
|
|
227
219
|
{height, requestStats: [{method: "getHeight", seconds}]}
|
|
@@ -4,7 +4,6 @@ import * as Source from "./Source.res.mjs";
|
|
|
4
4
|
import * as Logging from "../Logging.res.mjs";
|
|
5
5
|
import * as HyperSync from "./HyperSync.res.mjs";
|
|
6
6
|
import * as Performance from "../bindings/Performance.res.mjs";
|
|
7
|
-
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
8
7
|
import * as ErrorHandling from "../ErrorHandling.res.mjs";
|
|
9
8
|
import * as FuelHyperSync from "./FuelHyperSync.res.mjs";
|
|
10
9
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
@@ -12,22 +11,18 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
|
12
11
|
import * as FuelHyperSyncClient from "./FuelHyperSyncClient.res.mjs";
|
|
13
12
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
14
13
|
|
|
15
|
-
function isUnauthorizedError(message) {
|
|
16
|
-
return message.includes("401 Unauthorized");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
function make(param) {
|
|
20
15
|
let onEventRegistrations = param.onEventRegistrations;
|
|
21
|
-
let apiToken = param.apiToken;
|
|
22
16
|
let chainId = param.chainId;
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
let unauthorizedWarned = {
|
|
18
|
+
contents: false
|
|
19
|
+
};
|
|
20
|
+
let apiToken = HyperSync.requireApiToken(param.apiToken);
|
|
26
21
|
let client;
|
|
27
22
|
try {
|
|
28
23
|
client = FuelHyperSyncClient.make({
|
|
29
24
|
url: param.endpointUrl,
|
|
30
|
-
apiToken: apiToken
|
|
25
|
+
apiToken: apiToken
|
|
31
26
|
}, FuelHyperSyncClient.Registration.fromOnEventRegistrations(onEventRegistrations), param.addressStore);
|
|
32
27
|
} catch (raw_exn) {
|
|
33
28
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
@@ -188,33 +183,9 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
|
188
183
|
let height;
|
|
189
184
|
try {
|
|
190
185
|
height = await client.getHeight();
|
|
191
|
-
} catch (
|
|
192
|
-
let
|
|
193
|
-
|
|
194
|
-
let e$1 = e._1;
|
|
195
|
-
let message = Stdlib_JsExn.message(e$1);
|
|
196
|
-
if (message !== undefined) {
|
|
197
|
-
if (message.includes("401 Unauthorized")) {
|
|
198
|
-
Logging.error(`Your ENVIO_API_TOKEN was rejected by HyperFuel (401 Unauthorized). The indexer will not be able to fetch events. Update the token and try again using 'envio start' or 'envio dev'. For more info: https://docs.envio.dev/docs/HyperSync/api-tokens`);
|
|
199
|
-
await new Promise((param, param$1) => {});
|
|
200
|
-
height = 0;
|
|
201
|
-
} else {
|
|
202
|
-
throw {
|
|
203
|
-
RE_EXN_ID: "JsExn",
|
|
204
|
-
_1: e$1,
|
|
205
|
-
Error: new Error()
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
} else {
|
|
209
|
-
throw {
|
|
210
|
-
RE_EXN_ID: "JsExn",
|
|
211
|
-
_1: e$1,
|
|
212
|
-
Error: new Error()
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
} else {
|
|
216
|
-
throw e;
|
|
217
|
-
}
|
|
186
|
+
} catch (raw_exn) {
|
|
187
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
188
|
+
height = HyperSync.rethrowLoggingUnauthorized(exn, unauthorizedWarned, "HyperFuel");
|
|
218
189
|
}
|
|
219
190
|
let seconds = Performance.secondsSince(timerRef);
|
|
220
191
|
return {
|
|
@@ -230,7 +201,6 @@ Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
|
230
201
|
}
|
|
231
202
|
|
|
232
203
|
export {
|
|
233
|
-
isUnauthorizedError,
|
|
234
204
|
make,
|
|
235
205
|
}
|
|
236
206
|
/* Source Not a pure module */
|
|
@@ -9,6 +9,37 @@ let pollingInterval = 400
|
|
|
9
9
|
let rateLimitedPrefix = "RATE_LIMITED:"
|
|
10
10
|
let behindHeadPrefix = "SOURCE_BEHIND_HEAD:"
|
|
11
11
|
|
|
12
|
+
let requireApiToken = apiToken =>
|
|
13
|
+
switch apiToken {
|
|
14
|
+
| Some(token) => token
|
|
15
|
+
| None =>
|
|
16
|
+
JsError.throwWithMessage(`An Envio API token is required for using HyperSync as a data-source.
|
|
17
|
+
Set the ENVIO_API_TOKEN environment variable in your .env file.
|
|
18
|
+
Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let isUnauthorizedError = (message: string) => message->String.includes("401 Unauthorized")
|
|
22
|
+
|
|
23
|
+
// Never swallows the failure: the caller's retry ramp has to keep asking,
|
|
24
|
+
// because a token can be replaced without restarting the indexer. All this adds
|
|
25
|
+
// is one loud line the first time a source sees a 401 - saying it on every retry
|
|
26
|
+
// would bury everything else in the log.
|
|
27
|
+
let rethrowLoggingUnauthorized = (exn: exn, ~warned: ref<bool>, ~product: string): 'a => {
|
|
28
|
+
switch exn {
|
|
29
|
+
| JsExn(jsExn) =>
|
|
30
|
+
switch jsExn->JsExn.message {
|
|
31
|
+
| Some(message) if message->isUnauthorizedError =>
|
|
32
|
+
if !warned.contents {
|
|
33
|
+
warned := true
|
|
34
|
+
Logging.error(`Your ENVIO_API_TOKEN was rejected by ${product} (401 Unauthorized). The indexer will not be able to fetch events. Update the token and try again using 'envio start' or 'envio dev'. For more info: https://docs.envio.dev/docs/HyperSync/api-tokens`)
|
|
35
|
+
}
|
|
36
|
+
| _ => ()
|
|
37
|
+
}
|
|
38
|
+
| _ => ()
|
|
39
|
+
}
|
|
40
|
+
throw(exn)
|
|
41
|
+
}
|
|
42
|
+
|
|
12
43
|
let markerValue = (msg, ~prefix) =>
|
|
13
44
|
msg->String.slice(~start=prefix->String.length, ~end=msg->String.length)->Int.fromString
|
|
14
45
|
|
|
@@ -1,17 +1,45 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Source from "./Source.res.mjs";
|
|
4
|
+
import * as Logging from "../Logging.res.mjs";
|
|
4
5
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
5
6
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
6
7
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
7
8
|
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
8
9
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
10
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
11
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
9
12
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
10
13
|
|
|
11
14
|
let rateLimitedPrefix = "RATE_LIMITED:";
|
|
12
15
|
|
|
13
16
|
let behindHeadPrefix = "SOURCE_BEHIND_HEAD:";
|
|
14
17
|
|
|
18
|
+
function requireApiToken(apiToken) {
|
|
19
|
+
if (apiToken !== undefined) {
|
|
20
|
+
return Primitive_option.valFromOption(apiToken);
|
|
21
|
+
} else {
|
|
22
|
+
return Stdlib_JsError.throwWithMessage(`An Envio API token is required for using HyperSync as a data-source.
|
|
23
|
+
Set the ENVIO_API_TOKEN environment variable in your .env file.
|
|
24
|
+
Learn more or get a free Envio API token at: https://envio.dev/app/api-tokens`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isUnauthorizedError(message) {
|
|
29
|
+
return message.includes("401 Unauthorized");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function rethrowLoggingUnauthorized(exn, warned, product) {
|
|
33
|
+
if (exn.RE_EXN_ID === "JsExn") {
|
|
34
|
+
let message = Stdlib_JsExn.message(exn._1);
|
|
35
|
+
if (message !== undefined && message.includes("401 Unauthorized") && !warned.contents) {
|
|
36
|
+
warned.contents = true;
|
|
37
|
+
Logging.error(`Your ENVIO_API_TOKEN was rejected by ` + product + ` (401 Unauthorized). The indexer will not be able to fetch events. Update the token and try again using 'envio start' or 'envio dev'. For more info: https://docs.envio.dev/docs/HyperSync/api-tokens`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
throw exn;
|
|
41
|
+
}
|
|
42
|
+
|
|
15
43
|
function markerValue(msg, prefix) {
|
|
16
44
|
return Stdlib_Int.fromString(msg.slice(prefix.length, msg.length), undefined);
|
|
17
45
|
}
|
|
@@ -163,6 +191,9 @@ let pollingInterval = 400;
|
|
|
163
191
|
|
|
164
192
|
export {
|
|
165
193
|
pollingInterval,
|
|
194
|
+
requireApiToken,
|
|
195
|
+
isUnauthorizedError,
|
|
196
|
+
rethrowLoggingUnauthorized,
|
|
166
197
|
mapNativeFailure,
|
|
167
198
|
mapNativeFailureExn,
|
|
168
199
|
reraiseIfRecoverable,
|
|
@@ -10,6 +10,22 @@ type logsQueryPage = {
|
|
|
10
10
|
// one. Every ecosystem's client talks to the same service, so they wait alike.
|
|
11
11
|
let pollingInterval: int
|
|
12
12
|
|
|
13
|
+
// Every HyperSync-backed source needs a token to reach the service, so a
|
|
14
|
+
// missing one fails at construction with the message that says how to get one,
|
|
15
|
+
// rather than as a 401 per request.
|
|
16
|
+
let requireApiToken: option<string> => string
|
|
17
|
+
|
|
18
|
+
// Surfaced by the native clients when the edge rejects the API token. The
|
|
19
|
+
// corrupted-token test feeds the real server error (from the query endpoint;
|
|
20
|
+
// the edge no longer 401s malformed tokens on /height) through this so it can't
|
|
21
|
+
// silently drift away from the message shape a 401 actually produces.
|
|
22
|
+
let isUnauthorizedError: string => bool
|
|
23
|
+
|
|
24
|
+
// Rethrows every failure unchanged, adding one loud line the first time a source
|
|
25
|
+
// sees a 401. Never swallows it: the caller's retry ramp has to keep asking,
|
|
26
|
+
// because a token can be replaced without restarting the indexer.
|
|
27
|
+
let rethrowLoggingUnauthorized: (exn, ~warned: ref<bool>, ~product: string) => 'a
|
|
28
|
+
|
|
13
29
|
// Map a native client's `PREFIX:<int>` failure marker onto the exception
|
|
14
30
|
// SourceManager retries on (`RateLimited` / `SourceBehindHead`), falling back to
|
|
15
31
|
// the original cause.
|