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 {TOKENS} from '../../generated/gmxsdk/configs/tokens.js'
|
||||||
import {CONTRACTS} from '../../generated/gmxsdk/configs/contracts.js'
|
import {CONTRACTS} from '../../generated/gmxsdk/configs/contracts.js'
|
||||||
import {getClientForAddress, getTokenDataFromTicker} from './gmx.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 {Balance, Chain, Ticker} from '../../generated/ManagingApiTypes.js'
|
||||||
import {getCachedPrivySecrets} from './privy-secrets.js'
|
import {getCachedPrivySecrets} from './privy-secrets.js'
|
||||||
|
|
||||||
@@ -1296,6 +1296,72 @@ export const sendTokenImpl = async (
|
|||||||
const walletId = await getWalletIdFromAddress(senderAddress, fastify);
|
const walletId = await getWalletIdFromAddress(senderAddress, fastify);
|
||||||
|
|
||||||
if (ticker === 'ETH') {
|
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
|
// Native ETH transfer: no allowance, no data, value is amount as hex string
|
||||||
const { hash } = await privy.wallets().ethereum().sendTransaction(
|
const { hash } = await privy.wallets().ethereum().sendTransaction(
|
||||||
walletId,
|
walletId,
|
||||||
@@ -1304,7 +1370,7 @@ export const sendTokenImpl = async (
|
|||||||
params: {
|
params: {
|
||||||
transaction: {
|
transaction: {
|
||||||
to: recipientAddress as Address,
|
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,
|
chain_id: chainId,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user