Add ETH and USDC balance check before start/restart bot and autoswap
This commit is contained in:
@@ -26,7 +26,7 @@ import {decodeReferralCode, encodeReferralCode} from '../../generated/gmxsdk/uti
|
||||
import {formatUsd} from '../../generated/gmxsdk/utils/numbers/formatting.js';
|
||||
import {calculateDisplayDecimals} from '../../generated/gmxsdk/utils/numbers/index.js';
|
||||
import {handleError} from '../../utils/errorHandler.js';
|
||||
import {Abi, zeroHash} from 'viem';
|
||||
import {Abi, formatEther, parseEther, zeroHash} from 'viem';
|
||||
import {CLAIMABLE_FUNDING_AMOUNT} from '../../generated/gmxsdk/configs/dataStore.js';
|
||||
import {hashDataMap, hashString} from '../../generated/gmxsdk/utils/hash.js';
|
||||
import {ContractName, getContract} from '../../generated/gmxsdk/configs/contracts.js';
|
||||
@@ -56,6 +56,7 @@ const MAX_CACHE_SIZE = 5; // Limit cache size to prevent memory issues
|
||||
const OPERATION_TIMEOUT = 30000; // 30 seconds timeout for operations
|
||||
|
||||
const MEMORY_WARNING_THRESHOLD = 0.8; // Warn when memory usage exceeds 80%
|
||||
const MAX_GAS_FEE_USD = 1; // Maximum gas fee in USD (1 USDC)
|
||||
|
||||
// Memory monitoring function
|
||||
function checkMemoryUsage() {
|
||||
@@ -75,6 +76,118 @@ function checkMemoryUsage() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has sufficient ETH balance for gas fees
|
||||
* @param sdk The GMX SDK client
|
||||
* @param estimatedGasFee The estimated gas fee in wei
|
||||
* @returns Object with balance check result and details
|
||||
*/
|
||||
async function checkGasFeeBalance(
|
||||
sdk: GmxSdk,
|
||||
estimatedGasFee: bigint
|
||||
): Promise<{
|
||||
hasSufficientBalance: boolean;
|
||||
ethBalance: string;
|
||||
estimatedGasFeeUsd: number;
|
||||
ethPrice: number;
|
||||
errorMessage?: string;
|
||||
}> {
|
||||
try {
|
||||
// Get ETH balance using the public client
|
||||
const ethBalance = await sdk.publicClient.getBalance({ address: sdk.account });
|
||||
const ethBalanceFormatted = formatEther(ethBalance);
|
||||
|
||||
// Get ETH price from the market data
|
||||
const {tokensData} = await getMarketsInfoWithCache(sdk);
|
||||
const ethTokenData = getTokenDataFromTicker("ETH", tokensData);
|
||||
|
||||
if (!ethTokenData || !ethTokenData.prices?.minPrice) {
|
||||
throw new Error("Unable to get ETH price for gas fee calculation");
|
||||
}
|
||||
|
||||
// Convert ETH price from 30 decimals to regular number
|
||||
const ethPrice = Number(ethTokenData.prices.minPrice) / 1e30;
|
||||
|
||||
// Calculate estimated gas fee in USD
|
||||
const estimatedGasFeeEth = Number(formatEther(estimatedGasFee));
|
||||
const estimatedGasFeeUsd = estimatedGasFeeEth * ethPrice;
|
||||
|
||||
// Check if gas fee exceeds maximum allowed (1 USDC)
|
||||
const hasSufficientBalance = estimatedGasFeeUsd <= MAX_GAS_FEE_USD;
|
||||
|
||||
console.log(`⛽ Gas fee check:`, {
|
||||
ethBalance: ethBalanceFormatted,
|
||||
estimatedGasFeeEth: estimatedGasFeeEth.toFixed(6),
|
||||
estimatedGasFeeUsd: estimatedGasFeeUsd.toFixed(2),
|
||||
ethPrice: ethPrice.toFixed(2),
|
||||
maxAllowedUsd: MAX_GAS_FEE_USD,
|
||||
hasSufficientBalance
|
||||
});
|
||||
|
||||
return {
|
||||
hasSufficientBalance,
|
||||
ethBalance: ethBalanceFormatted,
|
||||
estimatedGasFeeUsd,
|
||||
ethPrice,
|
||||
errorMessage: hasSufficientBalance ? undefined :
|
||||
`Gas fee too high: $${estimatedGasFeeUsd.toFixed(2)} exceeds maximum of $${MAX_GAS_FEE_USD}. Please wait for lower gas fees or add more ETH.`
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error checking gas fee balance:', error);
|
||||
return {
|
||||
hasSufficientBalance: false,
|
||||
ethBalance: "0",
|
||||
estimatedGasFeeUsd: 0,
|
||||
ethPrice: 0,
|
||||
errorMessage: `Failed to check gas fee balance: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates gas fee for a position opening transaction
|
||||
* @param sdk The GMX SDK client
|
||||
* @param params The position increase parameters
|
||||
* @returns Estimated gas fee in wei
|
||||
*/
|
||||
async function estimatePositionGasFee(
|
||||
sdk: GmxSdk,
|
||||
params: PositionIncreaseParams
|
||||
): Promise<bigint> {
|
||||
try {
|
||||
// Estimate gas for the position opening transaction
|
||||
// This is a simplified estimation - in practice, you might want to use
|
||||
// the actual transaction simulation or a more sophisticated gas estimation
|
||||
const baseGasLimit = 500000n; // Base gas limit for position opening
|
||||
|
||||
// Get gas price using the public client
|
||||
const feeData = await sdk.publicClient.estimateFeesPerGas({
|
||||
type: "legacy",
|
||||
chain: sdk.chain,
|
||||
});
|
||||
const gasPrice = feeData.gasPrice || 0n;
|
||||
|
||||
// Add some buffer for safety (20% more)
|
||||
const estimatedGas = (baseGasLimit * 120n) / 100n;
|
||||
const estimatedGasFee = estimatedGas * gasPrice;
|
||||
|
||||
console.log(`⛽ Gas estimation:`, {
|
||||
baseGasLimit: baseGasLimit.toString(),
|
||||
gasPrice: gasPrice.toString(),
|
||||
estimatedGas: estimatedGas.toString(),
|
||||
estimatedGasFee: estimatedGasFee.toString()
|
||||
});
|
||||
|
||||
return estimatedGasFee;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error estimating gas fee:', error);
|
||||
// Return a conservative estimate if estimation fails
|
||||
return parseEther("0.01"); // 0.01 ETH as fallback
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback RPC configuration
|
||||
const FALLBACK_RPC_URL = "https://radial-shy-cherry.arbitrum-mainnet.quiknode.pro/098e57e961b05b24bcde008c4ca02fff6fb13b51/";
|
||||
const PRIMARY_RPC_URL = "https://arb1.arbitrum.io/rpc";
|
||||
@@ -518,6 +631,17 @@ export const openGmxPositionImpl = async (
|
||||
direction: direction
|
||||
});
|
||||
|
||||
// Check gas fees before opening position
|
||||
console.log('⛽ Checking gas fees before opening position...');
|
||||
const estimatedGasFee = await estimatePositionGasFee(sdk, params);
|
||||
const gasFeeCheck = await checkGasFeeBalance(sdk, estimatedGasFee);
|
||||
|
||||
if (!gasFeeCheck.hasSufficientBalance) {
|
||||
throw new Error(gasFeeCheck.errorMessage || 'Insufficient ETH balance for gas fees');
|
||||
}
|
||||
|
||||
console.log('✅ Gas fee check passed, proceeding with position opening...');
|
||||
|
||||
console.log('🚀 Executing position order...');
|
||||
|
||||
if (direction === TradeDirection.Long) {
|
||||
@@ -596,6 +720,28 @@ export async function openGmxPosition(
|
||||
hash
|
||||
};
|
||||
} catch (error) {
|
||||
// Handle gas fee specific errors
|
||||
if (error instanceof Error && error.message.includes('Gas fee too high')) {
|
||||
reply.status(400);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
errorType: 'GAS_FEE_TOO_HIGH',
|
||||
suggestion: 'Please wait for lower gas fees or add more ETH to your wallet.'
|
||||
};
|
||||
}
|
||||
|
||||
// Handle insufficient ETH balance errors
|
||||
if (error instanceof Error && error.message.includes('Insufficient ETH balance')) {
|
||||
reply.status(400);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
errorType: 'INSUFFICIENT_ETH_BALANCE',
|
||||
suggestion: 'Please add more ETH to your wallet to cover gas fees.'
|
||||
};
|
||||
}
|
||||
|
||||
return handleError(this, reply, error, 'gmx/open-position');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user