Add rpc url fallback to all gmx methods

This commit is contained in:
2025-08-17 01:18:12 +07:00
parent 09a217ca63
commit 0fb8178cea

View File

@@ -56,6 +56,58 @@ const FALLBACK_RPC_URL = "https://radial-shy-cherry.arbitrum-mainnet.quiknode.pr
const PRIMARY_RPC_URL = "https://arb1.arbitrum.io/rpc"; const PRIMARY_RPC_URL = "https://arb1.arbitrum.io/rpc";
const MAX_RETRY_ATTEMPTS = 2; // Only allow one retry to prevent infinite loops const MAX_RETRY_ATTEMPTS = 2; // Only allow one retry to prevent infinite loops
/**
* Generic fallback method for GMX operations that can retry with a fallback RPC
* @param operation The operation function to execute
* @param sdk The GMX SDK client
* @param retryCount Current retry attempt count
* @returns The result of the operation
*/
async function executeWithFallback<T>(
operation: (sdk: GmxSdk, retryCount: number) => Promise<T>,
sdk: GmxSdk,
retryCount: number = 0
): Promise<T> {
try {
console.log(`🔄 Executing operation with retry count: ${retryCount}, RPC: ${retryCount === 0 ? 'Primary' : 'Fallback'}`);
return await operation(sdk, retryCount);
} catch (error) {
// Check if this is a multicall timeout error and we haven't exceeded retry limit
const errorMessage = error instanceof Error ? error.message : String(error);
const isMulticallTimeout = errorMessage.toLowerCase().includes('multicall timeout') ||
errorMessage.toLowerCase().includes('timeout') ||
errorMessage.toLowerCase().includes('rpc error');
if (isMulticallTimeout && retryCount < MAX_RETRY_ATTEMPTS) {
console.log(`🔄 Multicall timeout detected (attempt ${retryCount + 1}/${MAX_RETRY_ATTEMPTS + 1}), retrying with fallback RPC...`);
try {
// Create a new SDK client with the fallback RPC
const fallbackSdk = await createGmxClientWithRpc(sdk.account, FALLBACK_RPC_URL);
console.log('✅ Fallback RPC client created, retrying operation...');
// Recursively call the same function with the fallback SDK and incremented retry count
const result = await executeWithFallback(operation, fallbackSdk, retryCount + 1);
console.log('✅ Fallback RPC request successful');
return result;
} catch (fallbackError) {
console.error('❌ Fallback RPC also failed:', fallbackError);
// If fallback also fails, throw the original error
throw error;
}
}
// If it's not a multicall timeout or we've exceeded retry limit, re-throw the error
if (isMulticallTimeout && retryCount >= MAX_RETRY_ATTEMPTS) {
console.error(`❌ Maximum retry attempts (${MAX_RETRY_ATTEMPTS}) exceeded. Both primary and fallback RPCs failed.`);
}
throw error;
}
}
/** /**
* Gets markets info data from cache or fetches it from GMX SDK * Gets markets info data from cache or fetches it from GMX SDK
* @param sdk The GMX SDK client * @param sdk The GMX SDK client
@@ -297,7 +349,8 @@ export const openGmxPositionImpl = async (
stopLossPrice?: number, stopLossPrice?: number,
takeProfitPrice?: number takeProfitPrice?: number
): Promise<string> => { ): Promise<string> => {
try { return executeWithFallback(
async (sdk, retryCount) => {
// Get markets and tokens data from GMX SDK with cache // Get markets and tokens data from GMX SDK with cache
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
@@ -360,10 +413,8 @@ export const openGmxPositionImpl = async (
console.log('✅ Position order executed successfully'); console.log('✅ Position order executed successfully');
return ""; return "";
} catch (error) { }, sdk, 0
console.error('Error opening GMX position:', error); );
throw new Error(`Failed to open position: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}; };
@@ -445,7 +496,8 @@ export const cancelGmxOrdersImpl = async (
sdk: GmxSdk, sdk: GmxSdk,
ticker: string ticker: string
): Promise<boolean> => { ): Promise<boolean> => {
try { return executeWithFallback(
async (sdk, retryCount) => {
// Get markets and tokens data first // Get markets and tokens data first
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
@@ -478,10 +530,8 @@ export const cancelGmxOrdersImpl = async (
await sdk.orders.cancelOrders(orderKeys); await sdk.orders.cancelOrders(orderKeys);
return true; return true;
} catch (error) { }, sdk, 0
console.error('Error canceling GMX orders:', error); );
throw new Error(`Failed to cancel orders: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}; };
@@ -580,7 +630,8 @@ export const closeGmxPositionImpl = async (
ticker: string, ticker: string,
direction: TradeDirection direction: TradeDirection
): Promise<string> => { ): Promise<string> => {
try { return executeWithFallback(
async (sdk, retryCount) => {
// Get markets and tokens data from GMX SDK with cache // Get markets and tokens data from GMX SDK with cache
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
@@ -660,10 +711,8 @@ export const closeGmxPositionImpl = async (
await sdk.orders.createDecreaseOrder(params2); await sdk.orders.createDecreaseOrder(params2);
return "hash"; return "hash";
} catch (error) { }, sdk, 0
console.error('Error closing GMX position:', error); );
throw new Error(`Failed to close position: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}; };
/** /**
@@ -712,7 +761,8 @@ export const getGmxTradeImpl = async (
sdk: GmxSdk, sdk: GmxSdk,
ticker: string ticker: string
): Promise<Trade[]> => { ): Promise<Trade[]> => {
return executeWithFallback(
async (sdk, retryCount) => {
const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk); const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
const orders = await sdk.orders.getOrders({ const orders = await sdk.orders.getOrders({
@@ -721,7 +771,6 @@ export const getGmxTradeImpl = async (
tokensData tokensData
}); });
// Filter orders for the specific ticker and transform them to trades // Filter orders for the specific ticker and transform them to trades
const trades = Object.values(orders.ordersInfoData) const trades = Object.values(orders.ordersInfoData)
.filter(order => { .filter(order => {
@@ -742,7 +791,6 @@ export const getGmxTradeImpl = async (
const sizeDelta = order.sizeDeltaUsd ?? 0n; const sizeDelta = order.sizeDeltaUsd ?? 0n;
const initialCollateral = order.initialCollateralDeltaAmount ?? 0n; const initialCollateral = order.initialCollateralDeltaAmount ?? 0n;
// Calculate leverage directly from the division of bigint values // Calculate leverage directly from the division of bigint values
// and convert to standard leverage number (like 2 for 2x) // and convert to standard leverage number (like 2 for 2x)
let leverage = 2; // Default to 2x leverage let leverage = 2; // Default to 2x leverage
@@ -790,7 +838,9 @@ export const getGmxTradeImpl = async (
}); });
return trades; return trades;
} }, sdk, 0
);
};
/** /**
* Gets trades on GMX * Gets trades on GMX
@@ -832,7 +882,8 @@ export const getGmxPositionsImpl = async (
sdk: GmxSdk, sdk: GmxSdk,
retryCount: number = 0 retryCount: number = 0
): Promise<Position[]> => { ): Promise<Position[]> => {
try { return executeWithFallback(
async (sdk, retryCount) => {
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
const positionsInfo = await sdk.positions.getPositionsInfo({ const positionsInfo = await sdk.positions.getPositionsInfo({
@@ -954,41 +1005,8 @@ export const getGmxPositionsImpl = async (
}); });
return Promise.all(positions); return Promise.all(positions);
} catch (error) { }, sdk, retryCount
// Check if this is a multicall timeout error and we haven't exceeded retry limit );
const errorMessage = error instanceof Error ? error.message : String(error);
const isMulticallTimeout = errorMessage.toLowerCase().includes('multicall timeout') ||
errorMessage.toLowerCase().includes('timeout') ||
errorMessage.toLowerCase().includes('rpc error');
if (isMulticallTimeout && retryCount < MAX_RETRY_ATTEMPTS) {
console.log(`🔄 Multicall timeout detected (attempt ${retryCount + 1}/${MAX_RETRY_ATTEMPTS + 1}), retrying with fallback RPC...`);
try {
// Create a new SDK client with the fallback RPC
const fallbackSdk = await createGmxClientWithRpc(sdk.account, FALLBACK_RPC_URL);
console.log('✅ Fallback RPC client created, retrying positions request...');
// Recursively call the same function with the fallback SDK and incremented retry count
const result = await getGmxPositionsImpl(fallbackSdk, retryCount + 1);
console.log('✅ Fallback RPC request successful');
return result;
} catch (fallbackError) {
console.error('❌ Fallback RPC also failed:', fallbackError);
// If fallback also fails, throw the original error
throw error;
}
}
// If it's not a multicall timeout or we've exceeded retry limit, re-throw the error
if (isMulticallTimeout && retryCount >= MAX_RETRY_ATTEMPTS) {
console.error(`❌ Maximum retry attempts (${MAX_RETRY_ATTEMPTS}) exceeded. Both primary and fallback RPCs failed.`);
}
throw error;
}
}; };
/** /**
@@ -1086,7 +1104,8 @@ export const getGmxRebateStatsImpl = async (
rebateFactor: number; rebateFactor: number;
discountFactor: number; discountFactor: number;
} | null> => { } | null> => {
try { return executeWithFallback(
async (sdk, retryCount) => {
// Get the referral storage contract address // Get the referral storage contract address
const referralStorageAddress = getContract(sdk.chainId, "ReferralStorage"); const referralStorageAddress = getContract(sdk.chainId, "ReferralStorage");
@@ -1203,10 +1222,8 @@ export const getGmxRebateStatsImpl = async (
rebateFactor: Number(totalRebateFactor), rebateFactor: Number(totalRebateFactor),
discountFactor: Number(discountFactor) discountFactor: Number(discountFactor)
}; };
} catch (error) { }, sdk, 0
console.error('Error getting GMX rebate stats:', error); );
throw error;
}
}; };
export async function getGmxRebateStats( export async function getGmxRebateStats(
@@ -1253,7 +1270,8 @@ interface FundingFeesClaimData {
export const getClaimableFundingFeesImpl = async ( export const getClaimableFundingFeesImpl = async (
sdk: GmxSdk sdk: GmxSdk
): Promise<ClaimableFundingData> => { ): Promise<ClaimableFundingData> => {
try { return executeWithFallback(
async (sdk, retryCount) => {
const { marketsInfoData, tokensData } = await getMarketsInfoWithCache(sdk); const { marketsInfoData, tokensData } = await getMarketsInfoWithCache(sdk);
if (!marketsInfoData || !tokensData) { if (!marketsInfoData || !tokensData) {
@@ -1341,11 +1359,8 @@ export const getClaimableFundingFeesImpl = async (
return claimableFundingData; return claimableFundingData;
}, {} as ClaimableFundingData); }, {} as ClaimableFundingData);
}, sdk, 0
} catch (error) { );
console.error('Error getting claimable funding fees:', error);
throw new Error(`Failed to get claimable funding fees: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}; };
/** /**
@@ -1524,7 +1539,8 @@ interface ClaimableUiFeeData {
export const getClaimableUiFeesImpl = async ( export const getClaimableUiFeesImpl = async (
sdk: GmxSdk sdk: GmxSdk
): Promise<ClaimableUiFeeData> => { ): Promise<ClaimableUiFeeData> => {
try { return executeWithFallback(
async (sdk, retryCount) => {
const { marketsInfoData } = await getMarketsInfoWithCache(sdk); const { marketsInfoData } = await getMarketsInfoWithCache(sdk);
if (!marketsInfoData) { if (!marketsInfoData) {
@@ -1590,11 +1606,8 @@ export const getClaimableUiFeesImpl = async (
return claimableUiFeeData; return claimableUiFeeData;
}, {} as ClaimableUiFeeData); }, {} as ClaimableUiFeeData);
}, sdk, 0
} catch (error) { );
console.error('Error getting claimable UI fees:', error);
throw new Error(`Failed to get claimable UI fees: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}; };
/** /**
@@ -1733,7 +1746,8 @@ export const swapGmxTokensImpl = async (
triggerRatio?: number, triggerRatio?: number,
allowedSlippage: number = 0.5 allowedSlippage: number = 0.5
): Promise<string> => { ): Promise<string> => {
try { return executeWithFallback(
async (sdk, retryCount) => {
// Get markets and tokens data from GMX SDK with cache // Get markets and tokens data from GMX SDK with cache
const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk); const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
@@ -1818,15 +1832,12 @@ export const swapGmxTokensImpl = async (
swapParams.marketsInfoData = marketsInfoData; swapParams.marketsInfoData = marketsInfoData;
swapParams.tokensData = tokensData; swapParams.tokensData = tokensData;
// Use the SDK's built-in swap method // Use the SDK's built-in swap method
await sdk.orders.swap(swapParams); await sdk.orders.swap(swapParams);
return "swap_order_created"; return "swap_order_created";
} catch (error) { }, sdk, 0
console.error('Error swapping GMX tokens:', error); );
throw new Error(`Failed to swap tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}; };
/** /**