@@ -7,13 +7,87 @@ import type {
77 QueryResponseKind ,
88} from "@near-js/types/lib/provider/response" ;
99import { providers } from "near-api-js" ;
10+ import type { CodeResult } from "near-api-js/lib/providers/provider" ;
1011
1112import { NETWORK , SOCIAL_DB_CONTRACT_ACCOUNT_ID } from "@/common/_config" ;
1213import { FULL_TGAS } from "@/common/constants" ;
1314
14- export const RPC_NODE_URL = `https://${ NETWORK === "mainnet" ? "free.rpc.fastnear.com" : "test.rpc.fastnear.com" } ` ;
15+ const RPC_NODE_URLS =
16+ NETWORK === "mainnet"
17+ ? [
18+ "https://1rpc.io/near" ,
19+ "https://free.rpc.fastnear.com" ,
20+ "https://near.blockpi.network/v1/rpc/public" ,
21+ "https://near.lava.build" ,
22+ "https://rpc.ankr.com/near" ,
23+ ]
24+ : [ "https://rpc.testnet.near.org" , "https://test.rpc.fastnear.com" ] ;
1525
16- const nearRpc = new providers . JsonRpcProvider ( { url : RPC_NODE_URL } ) ;
26+ export const RPC_NODE_URL = RPC_NODE_URLS [ 0 ] ;
27+
28+ const RPC_COOLDOWN_MS = 60_000 ;
29+
30+ const rpcCooldownUntil = new Map < string , number > ( ) ;
31+
32+ const rpcProviders = RPC_NODE_URLS . map ( ( url ) => ( {
33+ url,
34+ provider : new providers . JsonRpcProvider ( { url } ) ,
35+ } ) ) ;
36+
37+ let preferredRpcIndex = 0 ;
38+
39+ const getRpcProviderOrder = ( ) => {
40+ const indexes = rpcProviders . map ( ( _ , index ) => index ) ;
41+
42+ return [ preferredRpcIndex , ...indexes . filter ( ( index ) => index !== preferredRpcIndex ) ] ;
43+ } ;
44+
45+ const markRpcUnavailable = ( url : string ) => {
46+ rpcCooldownUntil . set ( url , Date . now ( ) + RPC_COOLDOWN_MS ) ;
47+ } ;
48+
49+ const isRpcAvailable = ( url : string ) => ( rpcCooldownUntil . get ( url ) ?? 0 ) <= Date . now ( ) ;
50+
51+ type NearRpcProvider = InstanceType < typeof providers . JsonRpcProvider > ;
52+
53+ const queryNearRpc = async < R > ( run : ( provider : NearRpcProvider ) => Promise < R > ) : Promise < R > => {
54+ let lastError : unknown ;
55+
56+ for ( const index of getRpcProviderOrder ( ) ) {
57+ const { provider, url } = rpcProviders [ index ] ;
58+
59+ if ( ! isRpcAvailable ( url ) ) continue ;
60+
61+ try {
62+ const result = await run ( provider ) ;
63+ preferredRpcIndex = index ;
64+ return result ;
65+ } catch ( error ) {
66+ lastError = error ;
67+ markRpcUnavailable ( url ) ;
68+ }
69+ }
70+
71+ const fallbackIndex = preferredRpcIndex === 0 ? 1 : 0 ;
72+ const fallback = rpcProviders [ fallbackIndex ] ?? rpcProviders [ 0 ] ;
73+
74+ try {
75+ const result = await run ( fallback . provider ) ;
76+ preferredRpcIndex = fallbackIndex ;
77+ return result ;
78+ } catch ( error ) {
79+ throw lastError ?? error ;
80+ }
81+ } ;
82+
83+ const nearRpc = {
84+ query : < R extends QueryResponseKind = CodeResult > (
85+ ...args : Parameters < NearRpcProvider [ "query" ] >
86+ ) => queryNearRpc ( ( provider ) => provider . query < R > ( ...args ) ) ,
87+
88+ txStatus : ( ...args : Parameters < NearRpcProvider [ "txStatus" ] > ) =>
89+ queryNearRpc ( ( provider ) => provider . txStatus ( ...args ) ) ,
90+ } ;
1791
1892type CallProps < A extends object = Record < string , unknown > > = {
1993 args ?: A ;
0 commit comments