fix: wallet x-ray RPC failover with 5 endpoints (Alchemy, PublicNode, Ankr, Solana, Helius)
This commit is contained in:
parent
660bb842ec
commit
f484cf94b1
1 changed files with 28 additions and 12 deletions
|
|
@ -6,8 +6,13 @@
|
|||
'use strict';
|
||||
|
||||
// ── Config ──
|
||||
const RPC = 'https://api.mainnet-beta.solana.com';
|
||||
const RPC_BACKUP = 'https://rpc.ankr.com/solana';
|
||||
const RPC_ENDPOINTS = [
|
||||
'https://solana-mainnet.g.alchemy.com/v2/demo',
|
||||
'https://solana.publicnode.com',
|
||||
'https://rpc.ankr.com/solana',
|
||||
'https://api.mainnet-beta.solana.com',
|
||||
'https://mainnet.helius-rpc.com/?api-key=1d8740dc-e5f4-421c-b823-e1bad1889eff'
|
||||
];
|
||||
const JUP_PRICE = 'https://price.jup.ag/v6/price';
|
||||
const JUP_TOKEN_LIST = 'https://token.jup.ag/all';
|
||||
const SOLSCAN_TX = 'https://solscan.io/tx/';
|
||||
|
|
@ -30,7 +35,8 @@
|
|||
// ── State ──
|
||||
let tokenListCache = null;
|
||||
let tokenMapCache = null; // mint → {name, symbol, logoURI, decimals}
|
||||
let activeRpc = RPC;
|
||||
let activeRpc = RPC_ENDPOINTS[0];
|
||||
let rpcIndex = 0;
|
||||
let scanning = false;
|
||||
|
||||
// ── Helpers ──
|
||||
|
|
@ -104,34 +110,44 @@
|
|||
}
|
||||
|
||||
// ── RPC Call ──
|
||||
async function rpc(method, params, retries = 2) {
|
||||
async function rpc(method, params, retries = 4) {
|
||||
let lastError;
|
||||
for (let i = 0; i <= retries; i++) {
|
||||
const url = RPC_ENDPOINTS[(rpcIndex + i) % RPC_ENDPOINTS.length];
|
||||
try {
|
||||
const url = i === 0 ? activeRpc : RPC_BACKUP;
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
|
||||
});
|
||||
if (res.status === 429) {
|
||||
await sleep(1000 * (i + 1));
|
||||
if (res.status === 429 || res.status === 403) {
|
||||
lastError = new Error('Rate limited (HTTP ' + res.status + ')');
|
||||
await sleep(800 * (i + 1));
|
||||
continue;
|
||||
}
|
||||
if (!res.ok) {
|
||||
lastError = new Error('HTTP ' + res.status);
|
||||
continue;
|
||||
}
|
||||
const json = await res.json();
|
||||
if (json.error) {
|
||||
if (json.error.code === -32429 || json.error.message?.includes('rate')) {
|
||||
await sleep(1000 * (i + 1));
|
||||
if (json.error.code === -32429 || (json.error.message && json.error.message.includes('rate'))) {
|
||||
lastError = new Error(json.error.message);
|
||||
await sleep(800 * (i + 1));
|
||||
continue;
|
||||
}
|
||||
throw new Error(json.error.message || 'RPC error');
|
||||
}
|
||||
// Success — remember this working RPC
|
||||
rpcIndex = (rpcIndex + i) % RPC_ENDPOINTS.length;
|
||||
activeRpc = RPC_ENDPOINTS[rpcIndex];
|
||||
return json.result;
|
||||
} catch (e) {
|
||||
if (i === retries) throw e;
|
||||
activeRpc = RPC_BACKUP;
|
||||
await sleep(500);
|
||||
lastError = e;
|
||||
if (i < retries) await sleep(500);
|
||||
}
|
||||
}
|
||||
throw lastError || new Error('All RPC endpoints failed');
|
||||
}
|
||||
|
||||
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue