Add rpc url fallback to all gmx methods
This commit is contained in:
@@ -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 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
|
||||
* @param sdk The GMX SDK client
|
||||
@@ -297,7 +349,8 @@ export const openGmxPositionImpl = async (
|
||||
stopLossPrice?: number,
|
||||
takeProfitPrice?: number
|
||||
): Promise<string> => {
|
||||
try {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
// Get markets and tokens data from GMX SDK with cache
|
||||
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
|
||||
|
||||
@@ -360,10 +413,8 @@ export const openGmxPositionImpl = async (
|
||||
console.log('✅ Position order executed successfully');
|
||||
|
||||
return "";
|
||||
} catch (error) {
|
||||
console.error('Error opening GMX position:', error);
|
||||
throw new Error(`Failed to open position: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}, sdk, 0
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -445,7 +496,8 @@ export const cancelGmxOrdersImpl = async (
|
||||
sdk: GmxSdk,
|
||||
ticker: string
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
// Get markets and tokens data first
|
||||
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
|
||||
|
||||
@@ -478,10 +530,8 @@ export const cancelGmxOrdersImpl = async (
|
||||
await sdk.orders.cancelOrders(orderKeys);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error canceling GMX orders:', error);
|
||||
throw new Error(`Failed to cancel orders: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}, sdk, 0
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -580,7 +630,8 @@ export const closeGmxPositionImpl = async (
|
||||
ticker: string,
|
||||
direction: TradeDirection
|
||||
): Promise<string> => {
|
||||
try {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
// Get markets and tokens data from GMX SDK with cache
|
||||
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
|
||||
|
||||
@@ -660,10 +711,8 @@ export const closeGmxPositionImpl = async (
|
||||
await sdk.orders.createDecreaseOrder(params2);
|
||||
|
||||
return "hash";
|
||||
} catch (error) {
|
||||
console.error('Error closing GMX position:', error);
|
||||
throw new Error(`Failed to close position: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}, sdk, 0
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -712,7 +761,8 @@ export const getGmxTradeImpl = async (
|
||||
sdk: GmxSdk,
|
||||
ticker: string
|
||||
): Promise<Trade[]> => {
|
||||
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
|
||||
|
||||
const orders = await sdk.orders.getOrders({
|
||||
@@ -721,7 +771,6 @@ export const getGmxTradeImpl = async (
|
||||
tokensData
|
||||
});
|
||||
|
||||
|
||||
// Filter orders for the specific ticker and transform them to trades
|
||||
const trades = Object.values(orders.ordersInfoData)
|
||||
.filter(order => {
|
||||
@@ -742,7 +791,6 @@ export const getGmxTradeImpl = async (
|
||||
const sizeDelta = order.sizeDeltaUsd ?? 0n;
|
||||
const initialCollateral = order.initialCollateralDeltaAmount ?? 0n;
|
||||
|
||||
|
||||
// Calculate leverage directly from the division of bigint values
|
||||
// and convert to standard leverage number (like 2 for 2x)
|
||||
let leverage = 2; // Default to 2x leverage
|
||||
@@ -790,7 +838,9 @@ export const getGmxTradeImpl = async (
|
||||
});
|
||||
|
||||
return trades;
|
||||
}
|
||||
}, sdk, 0
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets trades on GMX
|
||||
@@ -832,7 +882,8 @@ export const getGmxPositionsImpl = async (
|
||||
sdk: GmxSdk,
|
||||
retryCount: number = 0
|
||||
): Promise<Position[]> => {
|
||||
try {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
|
||||
|
||||
const positionsInfo = await sdk.positions.getPositionsInfo({
|
||||
@@ -954,41 +1005,8 @@ export const getGmxPositionsImpl = async (
|
||||
});
|
||||
|
||||
return Promise.all(positions);
|
||||
} 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 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;
|
||||
}
|
||||
}, sdk, retryCount
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1086,7 +1104,8 @@ export const getGmxRebateStatsImpl = async (
|
||||
rebateFactor: number;
|
||||
discountFactor: number;
|
||||
} | null> => {
|
||||
try {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
// Get the referral storage contract address
|
||||
|
||||
const referralStorageAddress = getContract(sdk.chainId, "ReferralStorage");
|
||||
@@ -1203,10 +1222,8 @@ export const getGmxRebateStatsImpl = async (
|
||||
rebateFactor: Number(totalRebateFactor),
|
||||
discountFactor: Number(discountFactor)
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error getting GMX rebate stats:', error);
|
||||
throw error;
|
||||
}
|
||||
}, sdk, 0
|
||||
);
|
||||
};
|
||||
|
||||
export async function getGmxRebateStats(
|
||||
@@ -1253,7 +1270,8 @@ interface FundingFeesClaimData {
|
||||
export const getClaimableFundingFeesImpl = async (
|
||||
sdk: GmxSdk
|
||||
): Promise<ClaimableFundingData> => {
|
||||
try {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
const { marketsInfoData, tokensData } = await getMarketsInfoWithCache(sdk);
|
||||
|
||||
if (!marketsInfoData || !tokensData) {
|
||||
@@ -1341,11 +1359,8 @@ export const getClaimableFundingFeesImpl = async (
|
||||
|
||||
return claimableFundingData;
|
||||
}, {} as ClaimableFundingData);
|
||||
|
||||
} 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'}`);
|
||||
}
|
||||
}, sdk, 0
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1524,7 +1539,8 @@ interface ClaimableUiFeeData {
|
||||
export const getClaimableUiFeesImpl = async (
|
||||
sdk: GmxSdk
|
||||
): Promise<ClaimableUiFeeData> => {
|
||||
try {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
const { marketsInfoData } = await getMarketsInfoWithCache(sdk);
|
||||
|
||||
if (!marketsInfoData) {
|
||||
@@ -1590,11 +1606,8 @@ export const getClaimableUiFeesImpl = async (
|
||||
|
||||
return claimableUiFeeData;
|
||||
}, {} as ClaimableUiFeeData);
|
||||
|
||||
} 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'}`);
|
||||
}
|
||||
}, sdk, 0
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1733,7 +1746,8 @@ export const swapGmxTokensImpl = async (
|
||||
triggerRatio?: number,
|
||||
allowedSlippage: number = 0.5
|
||||
): Promise<string> => {
|
||||
try {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
// Get markets and tokens data from GMX SDK with cache
|
||||
const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
|
||||
|
||||
@@ -1818,15 +1832,12 @@ export const swapGmxTokensImpl = async (
|
||||
swapParams.marketsInfoData = marketsInfoData;
|
||||
swapParams.tokensData = tokensData;
|
||||
|
||||
|
||||
// Use the SDK's built-in swap method
|
||||
await sdk.orders.swap(swapParams);
|
||||
|
||||
return "swap_order_created";
|
||||
} catch (error) {
|
||||
console.error('Error swapping GMX tokens:', error);
|
||||
throw new Error(`Failed to swap tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}, sdk, 0
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user