ton-provider-system 0.1.0 → 0.1.1
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/dist/index.cjs +239 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -84
- package/dist/index.d.ts +102 -84
- package/dist/index.js +238 -44
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/rpc.json +7 -22
package/dist/index.js
CHANGED
|
@@ -197,7 +197,10 @@ function resolveProvider(id, config) {
|
|
|
197
197
|
console.warn(`[ConfigParser] Provider ${id} has no valid endpoints after resolution`);
|
|
198
198
|
return null;
|
|
199
199
|
}
|
|
200
|
-
|
|
200
|
+
let apiKey = config.apiKeyEnvVar ? getEnvVar(config.apiKeyEnvVar) : void 0;
|
|
201
|
+
if (!apiKey && config.keyEnvVar && config.type === "onfinality") {
|
|
202
|
+
apiKey = getEnvVar(config.keyEnvVar);
|
|
203
|
+
}
|
|
201
204
|
return {
|
|
202
205
|
id,
|
|
203
206
|
name: config.name,
|
|
@@ -248,13 +251,24 @@ function getDefaultProvidersForNetwork(config, network) {
|
|
|
248
251
|
async function loadBuiltinConfig() {
|
|
249
252
|
const fs = await import('fs').then((m) => m.promises);
|
|
250
253
|
const path = await import('path');
|
|
254
|
+
const { fileURLToPath } = await import('url');
|
|
255
|
+
const getDirname = () => {
|
|
256
|
+
try {
|
|
257
|
+
if (import.meta.url) {
|
|
258
|
+
return path.dirname(fileURLToPath(import.meta.url));
|
|
259
|
+
}
|
|
260
|
+
} catch {
|
|
261
|
+
}
|
|
262
|
+
return process.cwd();
|
|
263
|
+
};
|
|
264
|
+
const dirname = getDirname();
|
|
251
265
|
const possiblePaths = [
|
|
252
266
|
// When running from project root (e.g., ts-node scripts/...)
|
|
253
267
|
path.resolve(process.cwd(), "provider_system", RPC_CONFIG_FILENAME),
|
|
254
268
|
// When running from provider_system folder
|
|
255
269
|
path.resolve(process.cwd(), RPC_CONFIG_FILENAME),
|
|
256
|
-
// Relative to this file (
|
|
257
|
-
path.resolve(
|
|
270
|
+
// Relative to this file (ESM style)
|
|
271
|
+
path.resolve(dirname, "..", RPC_CONFIG_FILENAME)
|
|
258
272
|
];
|
|
259
273
|
for (const configPath of possiblePaths) {
|
|
260
274
|
try {
|
|
@@ -547,12 +561,63 @@ function normalizeV2Endpoint(endpoint) {
|
|
|
547
561
|
if (normalized.toLowerCase().endsWith("/jsonrpc")) {
|
|
548
562
|
return normalized;
|
|
549
563
|
}
|
|
564
|
+
if (normalized.includes("gateway.tatum.io")) {
|
|
565
|
+
try {
|
|
566
|
+
const url = new URL(normalized);
|
|
567
|
+
if (!url.pathname || url.pathname === "/") {
|
|
568
|
+
return normalized + "/jsonRPC";
|
|
569
|
+
}
|
|
570
|
+
if (!url.pathname.toLowerCase().endsWith("/jsonrpc")) {
|
|
571
|
+
return normalized + "/jsonRPC";
|
|
572
|
+
}
|
|
573
|
+
} catch {
|
|
574
|
+
return normalized + "/jsonRPC";
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (normalized.includes("onfinality.io")) {
|
|
578
|
+
try {
|
|
579
|
+
const url = new URL(normalized);
|
|
580
|
+
const baseUrl = normalized.split("?")[0];
|
|
581
|
+
if (!url.pathname || url.pathname === "/") {
|
|
582
|
+
const apikey = url.searchParams.get("apikey");
|
|
583
|
+
if (apikey && apikey !== "{key}" && apikey.length > 0) {
|
|
584
|
+
return baseUrl.replace(/\/?$/, "/rpc");
|
|
585
|
+
}
|
|
586
|
+
return baseUrl.replace(/\/?$/, "/public");
|
|
587
|
+
}
|
|
588
|
+
if (url.pathname === "/rpc" || url.pathname === "/public") {
|
|
589
|
+
return baseUrl;
|
|
590
|
+
}
|
|
591
|
+
return baseUrl;
|
|
592
|
+
} catch {
|
|
593
|
+
if (normalized.includes("{key}")) {
|
|
594
|
+
return normalized.split("?")[0].replace(/\/?$/, "/public");
|
|
595
|
+
}
|
|
596
|
+
if (!normalized.includes("/rpc") && !normalized.includes("/public")) {
|
|
597
|
+
return normalized.split("?")[0] + "/public";
|
|
598
|
+
}
|
|
599
|
+
return normalized.split("?")[0];
|
|
600
|
+
}
|
|
601
|
+
}
|
|
550
602
|
if (normalized.endsWith("/api/v2")) {
|
|
551
603
|
return normalized + "/jsonRPC";
|
|
552
604
|
}
|
|
553
605
|
if (normalized.endsWith("/api/v3")) {
|
|
554
606
|
return normalized.replace("/api/v3", "/api/v2/jsonRPC");
|
|
555
607
|
}
|
|
608
|
+
if (normalized.includes("quiknode.pro") || normalized.includes("getblock.io")) {
|
|
609
|
+
try {
|
|
610
|
+
const url = new URL(normalized);
|
|
611
|
+
if (!url.pathname || url.pathname === "/") {
|
|
612
|
+
return normalized + "/jsonRPC";
|
|
613
|
+
}
|
|
614
|
+
if (!url.pathname.toLowerCase().endsWith("/jsonrpc")) {
|
|
615
|
+
return normalized + "/jsonRPC";
|
|
616
|
+
}
|
|
617
|
+
} catch {
|
|
618
|
+
return normalized + "/jsonRPC";
|
|
619
|
+
}
|
|
620
|
+
}
|
|
556
621
|
try {
|
|
557
622
|
const url = new URL(normalized);
|
|
558
623
|
if (!url.pathname || url.pathname === "/") {
|
|
@@ -580,7 +645,7 @@ function toV2Base(endpoint) {
|
|
|
580
645
|
return normalized;
|
|
581
646
|
}
|
|
582
647
|
function toV3Base(endpoint) {
|
|
583
|
-
|
|
648
|
+
const normalized = toV2Base(endpoint);
|
|
584
649
|
return normalized.replace("/api/v2", "/api/v3");
|
|
585
650
|
}
|
|
586
651
|
function getBaseUrl(endpoint) {
|
|
@@ -680,11 +745,19 @@ var DEFAULT_CONFIG = {
|
|
|
680
745
|
degradedLatencyMs: 3e3
|
|
681
746
|
};
|
|
682
747
|
var HealthChecker = class {
|
|
683
|
-
constructor(config, logger) {
|
|
748
|
+
constructor(config, logger, rateLimiter) {
|
|
684
749
|
this.results = /* @__PURE__ */ new Map();
|
|
685
750
|
this.highestSeqno = /* @__PURE__ */ new Map();
|
|
751
|
+
this.rateLimiter = null;
|
|
686
752
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
687
753
|
this.logger = logger || consoleLogger2;
|
|
754
|
+
this.rateLimiter = rateLimiter || null;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Set rate limiter (can be set after construction)
|
|
758
|
+
*/
|
|
759
|
+
setRateLimiter(rateLimiter) {
|
|
760
|
+
this.rateLimiter = rateLimiter;
|
|
688
761
|
}
|
|
689
762
|
/**
|
|
690
763
|
* Test a single provider's health
|
|
@@ -704,15 +777,42 @@ var HealthChecker = class {
|
|
|
704
777
|
};
|
|
705
778
|
this.results.set(key, testingResult);
|
|
706
779
|
try {
|
|
780
|
+
if (this.rateLimiter) {
|
|
781
|
+
const acquired = await this.rateLimiter.acquire(provider.id, this.config.timeoutMs);
|
|
782
|
+
if (!acquired) {
|
|
783
|
+
throw new Error("Rate limit timeout - unable to acquire token for health check");
|
|
784
|
+
}
|
|
785
|
+
}
|
|
707
786
|
const endpoint = await this.getEndpoint(provider);
|
|
708
787
|
if (!endpoint) {
|
|
709
788
|
throw new Error("No valid endpoint available");
|
|
710
789
|
}
|
|
711
|
-
|
|
712
|
-
|
|
790
|
+
if (provider.type === "tatum" && !provider.apiKey) {
|
|
791
|
+
throw new Error("Tatum provider requires API key (set TATUM_API_KEY_TESTNET or TATUM_API_KEY_MAINNET)");
|
|
792
|
+
}
|
|
793
|
+
let normalizedEndpoint = this.normalizeEndpointForProvider(provider, endpoint);
|
|
794
|
+
if (provider.type === "onfinality") {
|
|
795
|
+
this.logger.debug(`OnFinality endpoint: ${endpoint} -> ${normalizedEndpoint}, API key: ${provider.apiKey ? "set" : "not set"}`);
|
|
796
|
+
}
|
|
797
|
+
let info;
|
|
798
|
+
try {
|
|
799
|
+
info = await this.callGetMasterchainInfo(normalizedEndpoint, provider);
|
|
800
|
+
} catch (error) {
|
|
801
|
+
if (provider.type === "onfinality" && normalizedEndpoint.includes("/rpc") && provider.apiKey && error.message?.includes("backend error")) {
|
|
802
|
+
this.logger.debug(`OnFinality /rpc failed, retrying with /public endpoint`);
|
|
803
|
+
const publicEndpoint = normalizedEndpoint.replace("/rpc", "/public");
|
|
804
|
+
info = await this.callGetMasterchainInfo(publicEndpoint, { ...provider, apiKey: void 0 });
|
|
805
|
+
} else {
|
|
806
|
+
throw error;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
713
809
|
const endTime = performance.now();
|
|
714
810
|
const latencyMs = Math.round(endTime - startTime);
|
|
715
|
-
const
|
|
811
|
+
const infoWithLast = info;
|
|
812
|
+
const seqno = infoWithLast.last?.seqno;
|
|
813
|
+
if (!seqno || seqno <= 0 || !Number.isInteger(seqno)) {
|
|
814
|
+
throw new Error("Invalid seqno in response (must be positive integer)");
|
|
815
|
+
}
|
|
716
816
|
const currentHighest = this.highestSeqno.get(provider.network) || 0;
|
|
717
817
|
if (seqno > currentHighest) {
|
|
718
818
|
this.highestSeqno.set(provider.network, seqno);
|
|
@@ -746,11 +846,14 @@ var HealthChecker = class {
|
|
|
746
846
|
const errorMsg = error.message || String(error) || "Unknown error";
|
|
747
847
|
const is429 = errorMsg.includes("429") || errorMsg.toLowerCase().includes("rate limit");
|
|
748
848
|
const is404 = errorMsg.includes("404") || errorMsg.toLowerCase().includes("not found");
|
|
849
|
+
const is503 = errorMsg.includes("503") || errorMsg.toLowerCase().includes("service unavailable");
|
|
850
|
+
const is502 = errorMsg.includes("502") || errorMsg.toLowerCase().includes("bad gateway");
|
|
749
851
|
const isTimeout = error.name === "AbortError" || errorMsg.includes("timeout");
|
|
852
|
+
const isOnFinalityBackendError = provider.type === "onfinality" && (errorMsg.includes("Backend error") || errorMsg.includes("backend error"));
|
|
750
853
|
let status = "offline";
|
|
751
854
|
if (is429) {
|
|
752
855
|
status = "degraded";
|
|
753
|
-
} else if (is404) {
|
|
856
|
+
} else if (is404 || is503 || is502 || isOnFinalityBackendError) {
|
|
754
857
|
status = "offline";
|
|
755
858
|
} else if (isTimeout) {
|
|
756
859
|
status = "offline";
|
|
@@ -773,8 +876,11 @@ var HealthChecker = class {
|
|
|
773
876
|
}
|
|
774
877
|
/**
|
|
775
878
|
* Test multiple providers in parallel with staggered batches
|
|
879
|
+
*
|
|
880
|
+
* @param batchSize - Number of providers to test in parallel (default: 2)
|
|
881
|
+
* @param batchDelayMs - Delay between batches in milliseconds (default: 500 to avoid rate limits)
|
|
776
882
|
*/
|
|
777
|
-
async testProviders(providers, batchSize = 2, batchDelayMs =
|
|
883
|
+
async testProviders(providers, batchSize = 2, batchDelayMs = 500) {
|
|
778
884
|
const results = [];
|
|
779
885
|
for (let i = 0; i < providers.length; i += batchSize) {
|
|
780
886
|
const batch = providers.slice(i, i + batchSize);
|
|
@@ -888,15 +994,37 @@ var HealthChecker = class {
|
|
|
888
994
|
return provider.endpointV2 || provider.endpointV3 || null;
|
|
889
995
|
}
|
|
890
996
|
/**
|
|
891
|
-
*
|
|
997
|
+
* Normalize endpoint for provider-specific requirements
|
|
998
|
+
*
|
|
999
|
+
* Note: normalizeV2Endpoint now handles all provider-specific cases correctly,
|
|
1000
|
+
* including Tatum (/jsonRPC), OnFinality (/public or /rpc), QuickNode, and GetBlock.
|
|
1001
|
+
*/
|
|
1002
|
+
normalizeEndpointForProvider(provider, endpoint) {
|
|
1003
|
+
if (provider.type === "tatum" && endpoint.includes("api.tatum.io/v3/blockchain/node")) {
|
|
1004
|
+
const network = provider.network === "testnet" ? "testnet" : "mainnet";
|
|
1005
|
+
endpoint = `https://ton-${network}.gateway.tatum.io`;
|
|
1006
|
+
}
|
|
1007
|
+
return normalizeV2Endpoint(endpoint);
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Call getMasterchainInfo API with provider-specific handling
|
|
892
1011
|
*/
|
|
893
|
-
async callGetMasterchainInfo(endpoint) {
|
|
1012
|
+
async callGetMasterchainInfo(endpoint, provider) {
|
|
894
1013
|
const controller = new AbortController();
|
|
895
1014
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
1015
|
+
const headers = {
|
|
1016
|
+
"Content-Type": "application/json"
|
|
1017
|
+
};
|
|
1018
|
+
if (provider.type === "tatum" && provider.apiKey) {
|
|
1019
|
+
headers["x-api-key"] = provider.apiKey;
|
|
1020
|
+
}
|
|
1021
|
+
if (provider.type === "onfinality" && provider.apiKey) {
|
|
1022
|
+
headers["apikey"] = provider.apiKey;
|
|
1023
|
+
}
|
|
896
1024
|
try {
|
|
897
1025
|
const response = await fetch(endpoint, {
|
|
898
1026
|
method: "POST",
|
|
899
|
-
headers
|
|
1027
|
+
headers,
|
|
900
1028
|
body: JSON.stringify({
|
|
901
1029
|
id: "1",
|
|
902
1030
|
jsonrpc: "2.0",
|
|
@@ -906,20 +1034,63 @@ var HealthChecker = class {
|
|
|
906
1034
|
signal: controller.signal
|
|
907
1035
|
});
|
|
908
1036
|
clearTimeout(timeoutId);
|
|
1037
|
+
const contentType = response.headers.get("content-type") || "";
|
|
1038
|
+
let text = null;
|
|
1039
|
+
let data;
|
|
1040
|
+
if (!contentType.includes("application/json")) {
|
|
1041
|
+
text = await response.text();
|
|
1042
|
+
this.logger.debug(`${provider.type} non-JSON response (${contentType}): ${text.substring(0, 200)}`);
|
|
1043
|
+
if (provider.type === "onfinality" && text.includes("Backend error")) {
|
|
1044
|
+
throw new Error(`OnFinality backend error: ${text}`);
|
|
1045
|
+
}
|
|
1046
|
+
throw new Error(`Invalid response type: expected JSON, got ${contentType}. Response: ${text.substring(0, 100)}`);
|
|
1047
|
+
}
|
|
909
1048
|
if (!response.ok) {
|
|
910
|
-
|
|
1049
|
+
try {
|
|
1050
|
+
data = await response.json();
|
|
1051
|
+
const errorObj = data;
|
|
1052
|
+
const errorMsg = typeof errorObj.error === "string" ? errorObj.error : errorObj.error?.message || `HTTP ${response.status}`;
|
|
1053
|
+
throw new Error(errorMsg);
|
|
1054
|
+
} catch {
|
|
1055
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1056
|
+
}
|
|
911
1057
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1058
|
+
data = await response.json();
|
|
1059
|
+
let info;
|
|
1060
|
+
if (data && typeof data === "object") {
|
|
1061
|
+
const dataObj = data;
|
|
1062
|
+
if ("ok" in dataObj) {
|
|
1063
|
+
if (!dataObj.ok) {
|
|
1064
|
+
const error = dataObj.error;
|
|
1065
|
+
throw new Error(error || "API returned ok=false");
|
|
1066
|
+
}
|
|
1067
|
+
const result = dataObj.result;
|
|
1068
|
+
info = result || dataObj;
|
|
1069
|
+
} else if ("result" in dataObj) {
|
|
1070
|
+
info = dataObj.result;
|
|
1071
|
+
} else if ("last" in dataObj || "@type" in dataObj) {
|
|
1072
|
+
info = dataObj;
|
|
1073
|
+
} else if ("error" in dataObj) {
|
|
1074
|
+
const errorObj = dataObj.error;
|
|
1075
|
+
const errorMsg = typeof errorObj === "string" ? errorObj : errorObj?.message || errorObj?.code || String(errorObj);
|
|
1076
|
+
throw new Error(`API error: ${errorMsg}`);
|
|
1077
|
+
} else {
|
|
1078
|
+
throw new Error(`Unknown response format from ${provider.type}`);
|
|
916
1079
|
}
|
|
917
|
-
|
|
1080
|
+
} else {
|
|
1081
|
+
throw new Error(`Invalid response type: ${typeof data}`);
|
|
918
1082
|
}
|
|
919
|
-
if (
|
|
920
|
-
|
|
1083
|
+
if (!info || typeof info !== "object") {
|
|
1084
|
+
this.logger.debug(`Invalid response structure from ${provider.type}: ${JSON.stringify(data)}`);
|
|
1085
|
+
throw new Error("Invalid response structure");
|
|
921
1086
|
}
|
|
922
|
-
|
|
1087
|
+
const infoObj = info;
|
|
1088
|
+
const seqno = infoObj.last?.seqno;
|
|
1089
|
+
if (seqno === void 0 || seqno === null || seqno <= 0 || !Number.isInteger(seqno)) {
|
|
1090
|
+
this.logger.debug(`Invalid seqno from ${provider.type}:`, { seqno, info });
|
|
1091
|
+
throw new Error(`Invalid seqno: ${seqno} (must be positive integer)`);
|
|
1092
|
+
}
|
|
1093
|
+
return info;
|
|
923
1094
|
} catch (error) {
|
|
924
1095
|
clearTimeout(timeoutId);
|
|
925
1096
|
throw error;
|
|
@@ -929,8 +1100,8 @@ var HealthChecker = class {
|
|
|
929
1100
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
930
1101
|
}
|
|
931
1102
|
};
|
|
932
|
-
function createHealthChecker(config, logger) {
|
|
933
|
-
return new HealthChecker(config, logger);
|
|
1103
|
+
function createHealthChecker(config, logger, rateLimiter) {
|
|
1104
|
+
return new HealthChecker(config, logger, rateLimiter);
|
|
934
1105
|
}
|
|
935
1106
|
|
|
936
1107
|
// src/utils/timeout.ts
|
|
@@ -1402,7 +1573,7 @@ var ProviderSelector = class {
|
|
|
1402
1573
|
if (cachedBestId) {
|
|
1403
1574
|
const cached = this.registry.getProvider(cachedBestId);
|
|
1404
1575
|
const health = this.healthChecker.getResult(cachedBestId, network);
|
|
1405
|
-
if (cached && health && this.config.minStatus.includes(health.status)) {
|
|
1576
|
+
if (cached && health && health.success !== false && this.config.minStatus.includes(health.status)) {
|
|
1406
1577
|
return cached;
|
|
1407
1578
|
}
|
|
1408
1579
|
}
|
|
@@ -1423,17 +1594,39 @@ var ProviderSelector = class {
|
|
|
1423
1594
|
})).filter((item) => item.score > 0).sort((a, b) => b.score - a.score);
|
|
1424
1595
|
if (scored.length === 0) {
|
|
1425
1596
|
const defaults = this.registry.getDefaultOrderForNetwork(network);
|
|
1426
|
-
|
|
1427
|
-
this.
|
|
1428
|
-
|
|
1597
|
+
for (const defaultProvider of defaults) {
|
|
1598
|
+
const health = this.healthChecker.getResult(defaultProvider.id, network);
|
|
1599
|
+
if (!health || health.status === "untested" || health.success === true) {
|
|
1600
|
+
this.logger.warn(
|
|
1601
|
+
`No healthy providers for ${network}, using default: ${defaultProvider.id}`
|
|
1602
|
+
);
|
|
1603
|
+
return defaultProvider;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
for (const provider of providers) {
|
|
1607
|
+
const health = this.healthChecker.getResult(provider.id, network);
|
|
1608
|
+
if (!health || health.status === "untested") {
|
|
1609
|
+
this.logger.warn(
|
|
1610
|
+
`No tested healthy providers for ${network}, using untested: ${provider.id}`
|
|
1611
|
+
);
|
|
1612
|
+
return provider;
|
|
1613
|
+
}
|
|
1429
1614
|
}
|
|
1430
|
-
|
|
1615
|
+
this.logger.error(`No available providers for ${network} (all tested and failed)`);
|
|
1616
|
+
return null;
|
|
1431
1617
|
}
|
|
1432
1618
|
const best = scored[0].provider;
|
|
1433
|
-
this.
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1619
|
+
const bestHealth = this.healthChecker.getResult(best.id, network);
|
|
1620
|
+
if (bestHealth && bestHealth.success === true) {
|
|
1621
|
+
this.bestProviderByNetwork.set(network, best.id);
|
|
1622
|
+
this.logger.debug(
|
|
1623
|
+
`Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)})`
|
|
1624
|
+
);
|
|
1625
|
+
} else {
|
|
1626
|
+
this.logger.debug(
|
|
1627
|
+
`Best provider for ${network}: ${best.id} (score: ${scored[0].score.toFixed(2)}, untested)`
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1437
1630
|
return best;
|
|
1438
1631
|
}
|
|
1439
1632
|
/**
|
|
@@ -1469,7 +1662,7 @@ var ProviderSelector = class {
|
|
|
1469
1662
|
scoreProvider(provider, network) {
|
|
1470
1663
|
const health = this.healthChecker.getResult(provider.id, network);
|
|
1471
1664
|
if (!health || health.status === "untested") {
|
|
1472
|
-
return 0.
|
|
1665
|
+
return 0.01 * (1 / (provider.priority + 1));
|
|
1473
1666
|
}
|
|
1474
1667
|
if (health.success === false) {
|
|
1475
1668
|
return 0;
|
|
@@ -1699,28 +1892,29 @@ var _ProviderManager = class _ProviderManager {
|
|
|
1699
1892
|
const config = await loadConfig();
|
|
1700
1893
|
const mergedConfig = mergeWithDefaults(config);
|
|
1701
1894
|
this.registry = new ProviderRegistry(mergedConfig, this.options.logger);
|
|
1895
|
+
this.rateLimiter = createRateLimiterManager(this.options.logger);
|
|
1896
|
+
for (const provider of this.registry.getAllProviders()) {
|
|
1897
|
+
const config2 = getRateLimitForType(provider.type);
|
|
1898
|
+
this.rateLimiter.setConfig(provider.id, {
|
|
1899
|
+
...config2,
|
|
1900
|
+
rps: provider.rps,
|
|
1901
|
+
minDelayMs: Math.ceil(1e3 / provider.rps)
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1702
1904
|
this.healthChecker = createHealthChecker(
|
|
1703
1905
|
{
|
|
1704
1906
|
timeoutMs: this.options.requestTimeoutMs,
|
|
1705
1907
|
maxBlocksBehind: this.options.maxBlocksBehind
|
|
1706
1908
|
},
|
|
1707
|
-
this.options.logger
|
|
1909
|
+
this.options.logger,
|
|
1910
|
+
this.rateLimiter
|
|
1708
1911
|
);
|
|
1709
|
-
this.rateLimiter = createRateLimiterManager(this.options.logger);
|
|
1710
1912
|
this.selector = createSelector(
|
|
1711
1913
|
this.registry,
|
|
1712
1914
|
this.healthChecker,
|
|
1713
1915
|
void 0,
|
|
1714
1916
|
this.options.logger
|
|
1715
1917
|
);
|
|
1716
|
-
for (const provider of this.registry.getAllProviders()) {
|
|
1717
|
-
const config2 = getRateLimitForType(provider.type);
|
|
1718
|
-
this.rateLimiter.setConfig(provider.id, {
|
|
1719
|
-
...config2,
|
|
1720
|
-
rps: provider.rps,
|
|
1721
|
-
minDelayMs: Math.ceil(1e3 / provider.rps)
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1724
1918
|
this.initialized = true;
|
|
1725
1919
|
this.notifyListeners();
|
|
1726
1920
|
if (testProviders) {
|