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 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'}`);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user