Add ETH and USDC balance check before start/restart bot and autoswap

This commit is contained in:
2025-09-23 14:03:46 +07:00
parent d13ac9fd21
commit 40f3c66694
23 changed files with 847 additions and 284 deletions

View File

@@ -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');
}
}