Enhance ETH transfer logic in sendTokenImpl to reserve gas fees
- Updated sendTokenImpl to estimate gas costs for ETH transfers, ensuring sufficient balance is available for gas fees. - Implemented gas estimation with fallback mechanisms for gas price and limit, improving reliability of ETH transactions. - Adjusted the transfer amount based on available balance after accounting for estimated gas costs, providing clearer error messages for insufficient funds.
This commit is contained in:
@@ -11,7 +11,7 @@ import {ARBITRUM} from '../../generated/gmxsdk/configs/chains.js'
|
||||
import {TOKENS} from '../../generated/gmxsdk/configs/tokens.js'
|
||||
import {CONTRACTS} from '../../generated/gmxsdk/configs/contracts.js'
|
||||
import {getClientForAddress, getTokenDataFromTicker} from './gmx.js'
|
||||
import {Address, erc20Abi} from 'viem'
|
||||
import {Address, erc20Abi, formatEther, parseEther} from 'viem'
|
||||
import {Balance, Chain, Ticker} from '../../generated/ManagingApiTypes.js'
|
||||
import {getCachedPrivySecrets} from './privy-secrets.js'
|
||||
|
||||
@@ -1296,6 +1296,72 @@ export const sendTokenImpl = async (
|
||||
const walletId = await getWalletIdFromAddress(senderAddress, fastify);
|
||||
|
||||
if (ticker === 'ETH') {
|
||||
// For ETH transfers, we need to reserve gas fees
|
||||
// Get SDK to access public client for balance and gas estimation
|
||||
const sdk = await getClientForAddress(senderAddress);
|
||||
const balance = await sdk.publicClient.getBalance({ address: senderAddress as Address });
|
||||
|
||||
// Estimate gas for the transfer
|
||||
// Use a test amount (1 ETH) to estimate gas, then scale if needed
|
||||
const testAmount = parseEther('1');
|
||||
let estimatedGasLimit: bigint;
|
||||
try {
|
||||
estimatedGasLimit = await sdk.publicClient.estimateGas({
|
||||
to: recipientAddress as Address,
|
||||
value: testAmount,
|
||||
account: senderAddress as Address,
|
||||
});
|
||||
} catch (error) {
|
||||
// If estimation fails, use a conservative default (21,000 is standard for simple transfers)
|
||||
console.warn('Gas estimation failed, using default:', error);
|
||||
estimatedGasLimit = 21000n;
|
||||
}
|
||||
|
||||
// Get gas price
|
||||
const feeData = await sdk.publicClient.estimateFeesPerGas({
|
||||
type: 'legacy',
|
||||
chain: sdk.chain,
|
||||
});
|
||||
let gasPrice = feeData.gasPrice || 0n;
|
||||
|
||||
// If gas price is 0 or unavailable, use a conservative fallback
|
||||
// Arbitrum typically has gas prices around 0.1-1 gwei, so 1 gwei is a safe fallback
|
||||
if (gasPrice === 0n) {
|
||||
console.warn('Gas price estimation returned 0, using fallback of 1 gwei');
|
||||
gasPrice = 1000000000n; // 1 gwei = 1,000,000,000 wei
|
||||
}
|
||||
|
||||
// Calculate gas cost with 30% buffer for safety
|
||||
const gasBufferMultiplier = 130n; // 30% buffer
|
||||
const gasCost = (estimatedGasLimit * gasPrice * gasBufferMultiplier) / 100n;
|
||||
|
||||
// Calculate the maximum amount we can send (balance - gas cost)
|
||||
const maxSendableAmount = balance > gasCost ? balance - gasCost : 0n;
|
||||
|
||||
// Use the minimum of requested amount and max sendable amount
|
||||
const actualAmountToSend = amountBigInt > maxSendableAmount ? maxSendableAmount : amountBigInt;
|
||||
|
||||
if (actualAmountToSend <= 0n) {
|
||||
const balanceEth = formatEther(balance);
|
||||
const gasCostEth = formatEther(gasCost);
|
||||
throw new Error(
|
||||
`Insufficient ETH balance. Balance: ${balanceEth} ETH, ` +
|
||||
`Estimated gas cost: ${gasCostEth} ETH. ` +
|
||||
`Cannot send any amount as balance is less than required gas fees.`
|
||||
);
|
||||
}
|
||||
|
||||
// Log if we adjusted the amount
|
||||
if (actualAmountToSend < amountBigInt) {
|
||||
const requestedEth = formatEther(amountBigInt);
|
||||
const sendingEth = formatEther(actualAmountToSend);
|
||||
const gasCostEth = formatEther(gasCost);
|
||||
console.log(
|
||||
`⚠️ Adjusted ETH transfer amount: requested ${requestedEth} ETH, ` +
|
||||
`sending ${sendingEth} ETH (reserved ${gasCostEth} ETH for gas fees)`
|
||||
);
|
||||
}
|
||||
|
||||
// Native ETH transfer: no allowance, no data, value is amount as hex string
|
||||
const { hash } = await privy.wallets().ethereum().sendTransaction(
|
||||
walletId,
|
||||
@@ -1304,7 +1370,7 @@ export const sendTokenImpl = async (
|
||||
params: {
|
||||
transaction: {
|
||||
to: recipientAddress as Address,
|
||||
value: '0x' + amountBigInt.toString(16), // value in wei as hex string
|
||||
value: '0x' + actualAmountToSend.toString(16), // value in wei as hex string
|
||||
chain_id: chainId,
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user