After getting a quote and handling any required approvals, you need to execute the actual transfer transaction. This page covers chain verification, gas handling, signing, and broadcasting.
The Transaction Request
The quote response includes a ready-to-use transactionRequest object:
{
"transactionRequest": {
"to": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
"data": "0x4630a0d8...",
"value": "0x0",
"gasLimit": "0x55730",
"gasPrice": "0x5d21dba00",
"chainId": 1
}
}
| Field | Description |
|---|
to | LI.FI contract address |
data | Encoded function call |
value | Native token amount (hex, in wei) |
gasLimit | Estimated gas limit (hex) |
gasPrice | Suggested gas price (hex, optional) |
chainId | Chain ID for the transaction |
Pre-Execution Checklist
Before sending the transaction:
Step 1: Verify Chain
Ensure the wallet is on the correct chain before sending:
JavaScript (viem)
import { createWalletClient, http } from 'viem';
import { mainnet, arbitrum, optimism, polygon, base } from 'viem/chains';
const CHAIN_MAP = {
1: mainnet,
42161: arbitrum,
10: optimism,
137: polygon,
8453: base
};
async function ensureCorrectChain(walletClient, targetChainId) {
const currentChainId = await walletClient.getChainId();
if (currentChainId !== targetChainId) {
console.log(`Switching from chain ${currentChainId} to ${targetChainId}`);
await walletClient.switchChain({ id: targetChainId });
// Verify switch succeeded
const newChainId = await walletClient.getChainId();
if (newChainId !== targetChainId) {
throw new Error(`Failed to switch to chain ${targetChainId}`);
}
}
return true;
}
Python (web3.py)
def ensure_correct_chain(web3, target_chain_id):
current_chain = web3.eth.chain_id
if current_chain != target_chain_id:
raise Exception(
f"Wrong chain. Connected to {current_chain}, need {target_chain_id}"
)
return True
Step 2: Estimate Gas (Optional)
The quote includes a gasLimit, but you can re-estimate for accuracy:
async function estimateGas(publicClient, txRequest, fromAddress) {
try {
const gasEstimate = await publicClient.estimateGas({
account: fromAddress,
to: txRequest.to,
data: txRequest.data,
value: BigInt(txRequest.value || '0x0')
});
// Add 20% buffer
return (gasEstimate * 120n) / 100n;
} catch (error) {
// Fall back to quote's gasLimit
console.log('Using quote gasLimit:', txRequest.gasLimit);
return BigInt(txRequest.gasLimit);
}
}
The quote’s gasLimit is usually accurate. Only re-estimate if you need precise gas costs or are optimizing for gas efficiency.
Step 3: Build the Transaction
Convert the quote’s transaction request into a sendable transaction:
JavaScript (viem)
function buildTransaction(txRequest, gasLimit) {
return {
to: txRequest.to,
data: txRequest.data,
value: BigInt(txRequest.value || '0x0'),
gas: gasLimit || BigInt(txRequest.gasLimit),
chainId: txRequest.chainId
};
}
Gas Price Options
You can use the quote’s suggested gas price or fetch current prices:
// Option 1: Use quote's gas price
const tx = {
...buildTransaction(txRequest),
gasPrice: BigInt(txRequest.gasPrice)
};
// Option 2: Let wallet determine gas price (recommended)
const tx = buildTransaction(txRequest);
// Don't set gasPrice - wallet will use current market rate
// Option 3: Use EIP-1559 (for supported chains)
const tx = {
...buildTransaction(txRequest),
maxFeePerGas: parseGwei('50'),
maxPriorityFeePerGas: parseGwei('2')
};
Step 4: Sign and Send
JavaScript (viem)
async function executeTransaction(walletClient, publicClient, txRequest) {
// Ensure on correct chain
await ensureCorrectChain(walletClient, txRequest.chainId);
// Build transaction
const tx = {
to: txRequest.to,
data: txRequest.data,
value: BigInt(txRequest.value || '0x0'),
gas: BigInt(txRequest.gasLimit)
};
// Send transaction
console.log('Sending transaction...');
const hash = await walletClient.sendTransaction(tx);
console.log(`Transaction sent: ${hash}`);
// Wait for confirmation
console.log('Waiting for confirmation...');
const receipt = await publicClient.waitForTransactionReceipt({
hash,
confirmations: 1
});
if (receipt.status !== 'success') {
throw new Error(`Transaction failed: ${hash}`);
}
console.log(`Transaction confirmed in block ${receipt.blockNumber}`);
return {
hash,
receipt
};
}
Python (web3.py)
def execute_transaction(web3, tx_request, private_key):
account = web3.eth.account.from_key(private_key)
# Build transaction
tx = {
'to': tx_request['to'],
'data': tx_request['data'],
'value': int(tx_request.get('value', '0x0'), 16),
'gas': int(tx_request['gasLimit'], 16),
'nonce': web3.eth.get_transaction_count(account.address),
'chainId': tx_request['chainId']
}
# Get gas price
tx['gasPrice'] = web3.eth.gas_price
# Sign transaction
signed = web3.eth.account.sign_transaction(tx, private_key)
# Send transaction
print('Sending transaction...')
tx_hash = web3.eth.send_raw_transaction(signed.rawTransaction)
print(f'Transaction sent: {tx_hash.hex()}')
# Wait for confirmation
print('Waiting for confirmation...')
receipt = web3.eth.wait_for_transaction_receipt(tx_hash)
if receipt['status'] != 1:
raise Exception(f'Transaction failed: {tx_hash.hex()}')
print(f'Confirmed in block {receipt["blockNumber"]}')
return {
'hash': tx_hash.hex(),
'receipt': receipt
}
Step 5: Handle the Result
After execution, save the transaction hash for status polling:
const { hash: txHash } = await executeTransaction(walletClient, publicClient, quote.transactionRequest);
// Save for status polling (only txHash is required, others speed up response)
const statusParams = {
txHash, // Required
fromChain: quote.action.fromChainId, // Recommended for faster response
toChain: quote.action.toChainId, // Optional
bridge: quote.tool // Optional (e.g., "stargate")
};
// Now poll for cross-chain status
// See: Status & Recovery page
Complete Execution Flow
async function executeQuote(quote, walletClient, publicClient) {
const { transactionRequest, action, tool, estimate } = quote;
// 1. Verify chain
await ensureCorrectChain(walletClient, transactionRequest.chainId);
// 2. Handle approval if needed
await ensureApproval(quote, walletClient, publicClient);
// 3. Execute main transaction
const tx = {
to: transactionRequest.to,
data: transactionRequest.data,
value: BigInt(transactionRequest.value || '0x0'),
gas: BigInt(transactionRequest.gasLimit)
};
const hash = await walletClient.sendTransaction(tx);
// 4. Wait for source chain confirmation
const receipt = await publicClient.waitForTransactionReceipt({
hash,
confirmations: 1
});
if (receipt.status !== 'success') {
throw new Error('Transaction reverted');
}
// 5. Return data for status polling
return {
txHash: hash,
bridge: tool,
fromChain: action.fromChainId,
toChain: action.toChainId,
receipt
};
}
Error Handling
Common Execution Errors
| Error | Cause | Solution |
|---|
insufficient funds | Not enough ETH for gas + value | Check balance before sending |
nonce too low | Pending transaction exists | Wait or speed up pending tx |
gas too low | Gas limit insufficient | Increase gas limit |
execution reverted | Contract rejected transaction | Quote may be stale, get new quote |
replacement fee too low | Trying to replace pending tx | Increase gas price |
Retry Logic
async function executeWithRetry(quote, walletClient, publicClient, maxRetries = 2) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await executeQuote(quote, walletClient, publicClient);
} catch (error) {
const message = error.message.toLowerCase();
// Don't retry these errors
if (message.includes('insufficient funds') ||
message.includes('user rejected') ||
message.includes('denied')) {
throw error;
}
// Quote might be stale - get new quote and retry
if (message.includes('reverted') && attempt < maxRetries - 1) {
console.log('Transaction reverted, fetching new quote...');
quote = await getQuote(originalParams);
continue;
}
throw error;
}
}
}
Gas Optimization Tips
- Use EIP-1559 where supported - More predictable gas costs
- Don’t over-estimate gas - Quote’s gasLimit is usually accurate
- Check gas prices - High gas times may make transfers expensive
- Consider L2s - Arbitrum, Optimism, Base have much lower gas costs
// Check if gas cost is reasonable
const gasCostWei = BigInt(txRequest.gasLimit) * BigInt(txRequest.gasPrice);
const gasCostEth = Number(gasCostWei) / 1e18;
if (gasCostEth > 0.01) { // More than 0.01 ETH
console.log(`Warning: High gas cost: ${gasCostEth} ETH`);
}
Related Pages