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