eth_sendRawTransaction
Submits a pre-signed transaction for broadcast to the Linea network.
Parameters
- Signed Transaction Data -
DATA
- The signed transaction data in hexadecimal format
Returns
DATA, 32 Bytes
- The transaction hash, or the zero hash if the transaction is not yet available.
Request Example
{
"jsonrpc": "2.0",
"method": "eth_sendRawTransaction",
"params": [
"0xf86c808504a817c80082520894..."
],
"id": 1
}
Response Example
{
"jsonrpc": "2.0",
"id": 1,
"result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
Implementation Examples
- JavaScript
- Python
import { JsonRpcProvider, Wallet } from 'ethers';
const provider = new JsonRpcProvider('https://api-linea-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
// Send a simple ETH transfer
async function sendETH(privateKey, toAddress, amount) {
const wallet = new Wallet(privateKey, provider);
// Create transaction
const tx = {
to: toAddress,
value: ethers.parseEther(amount),
gasLimit: 21000n,
// Linea uses EIP-1559
maxFeePerGas: await provider.getFeeData().then(d => d.maxFeePerGas),
maxPriorityFeePerGas: await provider.getFeeData().then(d => d.maxPriorityFeePerGas),
chainId: 59144, // Linea chain ID
nonce: await wallet.getTransactionCount(),
type: 2 // EIP-1559 transaction
};
// Sign and send
const signedTx = await wallet.signTransaction(tx);
const txHash = await provider.send('eth_sendRawTransaction', [signedTx]);
console.log('Transaction hash:', txHash);
// Wait for confirmation
const receipt = await provider.waitForTransaction(txHash);
return receipt;
}
// Send contract interaction transaction
async function sendContractTx(privateKey, contractAddress, data) {
const wallet = new Wallet(privateKey, provider);
// Estimate gas for contract interaction
const gasEstimate = await provider.estimateGas({
from: wallet.address,
to: contractAddress,
data: data
});
// Add 20% buffer for zkEVM
const gasLimit = gasEstimate * 120n / 100n;
const tx = {
to: contractAddress,
data: data,
gasLimit: gasLimit,
maxFeePerGas: await provider.getFeeData().then(d => d.maxFeePerGas),
maxPriorityFeePerGas: await provider.getFeeData().then(d => d.maxPriorityFeePerGas),
chainId: 59144,
nonce: await wallet.getTransactionCount(),
type: 2
};
const signedTx = await wallet.signTransaction(tx);
const txHash = await provider.send('eth_sendRawTransaction', [signedTx]);
return txHash;
}
// Batch transaction sending
async function sendBatchTransactions(privateKey, transactions) {
const wallet = new Wallet(privateKey, provider);
const nonce = await wallet.getTransactionCount();
const signedTxs = [];
for (let i = 0; i < transactions.length; i++) {
const tx = {
...transactions[i],
nonce: nonce + i,
chainId: 59144,
maxFeePerGas: await provider.getFeeData().then(d => d.maxFeePerGas),
maxPriorityFeePerGas: await provider.getFeeData().then(d => d.maxPriorityFeePerGas),
type: 2
};
const signedTx = await wallet.signTransaction(tx);
signedTxs.push(signedTx);
}
// Send all transactions
const txHashes = [];
for (const signedTx of signedTxs) {
const hash = await provider.send('eth_sendRawTransaction', [signedTx]);
txHashes.push(hash);
}
return txHashes;
}
from web3 import Web3
from eth_account import Account
w3 = Web3(Web3.HTTPProvider('https://api-linea-mainnet-archive.n.dwellir.com/YOUR_API_KEY'))
def send_eth_transfer(private_key, to_address, amount_eth):
"""Send ETH transfer transaction"""
account = Account.from_key(private_key)
# Build transaction
transaction = {
'to': to_address,
'value': w3.to_wei(amount_eth, 'ether'),
'gas': 21000,
'gasPrice': w3.eth.gas_price,
'nonce': w3.eth.get_transaction_count(account.address),
'chainId': 59144 # Linea chain ID
}
# Sign transaction
signed = account.sign_transaction(transaction)
# Send raw transaction
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
# Wait for receipt
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
return {
'hash': tx_hash.hex(),
'status': 'success' if receipt['status'] == 1 else 'failed',
'block': receipt['blockNumber'],
'gas_used': receipt['gasUsed']
}
def send_contract_transaction(private_key, contract_address, data):
"""Send contract interaction transaction"""
account = Account.from_key(private_key)
# Estimate gas
gas_estimate = w3.eth.estimate_gas({
'from': account.address,
'to': contract_address,
'data': data
})
# Add buffer for zkEVM
gas_limit = int(gas_estimate * 1.2)
transaction = {
'to': contract_address,
'data': data,
'gas': gas_limit,
'gasPrice': w3.eth.gas_price,
'nonce': w3.eth.get_transaction_count(account.address),
'chainId': 59144
}
signed = account.sign_transaction(transaction)
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
return tx_hash.hex()
def send_eip1559_transaction(private_key, to, value=0, data='0x'):
"""Send EIP-1559 transaction with dynamic fees"""
account = Account.from_key(private_key)
# Get fee data
latest_block = w3.eth.get_block('latest')
base_fee = latest_block['baseFeePerGas']
max_priority_fee = w3.eth.max_priority_fee
transaction = {
'to': to,
'value': value,
'data': data,
'gas': 100000,
'maxFeePerGas': base_fee * 2 + max_priority_fee,
'maxPriorityFeePerGas': max_priority_fee,
'nonce': w3.eth.get_transaction_count(account.address),
'chainId': 59144,
'type': 2 # EIP-1559
}
signed = account.sign_transaction(transaction)
tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
return tx_hash.hex()
zkEVM Transaction Optimization
Gas Optimization
Linea zkEVM has specific gas considerations:
// Optimize gas for zkEVM transactions
async function optimizeGasForZkEVM(tx) {
const provider = getProvider();
// Get current network conditions
const [block, feeData] = await Promise.all([
provider.getBlock('latest'),
provider.getFeeData()
]);
// zkEVM-specific gas calculation
const gasEstimate = await provider.estimateGas(tx);
// Add buffer for zkProof overhead
const optimizedGas = {
gasLimit: gasEstimate * 115n / 100n, // 15% buffer
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas || 1000000000n // 1 gwei minimum
};
return { ...tx, ...optimizedGas };
}
Transaction Monitoring
// Monitor transaction with retry logic
async function sendWithRetry(signedTx, maxRetries = 3) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const txHash = await provider.send('eth_sendRawTransaction', [signedTx]);
// Monitor transaction
const receipt = await waitForTransaction(txHash, {
timeout: 60000, // 60 seconds timeout
confirmations: 1
});
if (receipt.status === 1) {
return receipt;
} else {
throw new Error('Transaction failed');
}
} catch (error) {
lastError = error;
if (error.message.includes('nonce too low')) {
throw error; // Don't retry nonce errors
}
if (error.message.includes('replacement fee too low')) {
// Increase gas price for retry
console.log('Increasing gas price for retry...');
await new Promise(r => setTimeout(r, 2000 * attempt));
}
}
}
throw lastError;
}
Error Handling
Common errors and solutions:
async function handleTransactionError(error, tx) {
if (error.code === 'NONCE_EXPIRED') {
// Nonce too low - transaction already processed
console.log('Transaction already processed or nonce reused');
// Get correct nonce
const correctNonce = await provider.getTransactionCount(tx.from);
return { ...tx, nonce: correctNonce };
} else if (error.code === 'REPLACEMENT_UNDERPRICED') {
// Gas price too low for replacement
const currentGasPrice = await provider.getGasPrice();
const newGasPrice = currentGasPrice * 110n / 100n; // Increase by 10%
return { ...tx, gasPrice: newGasPrice };
} else if (error.code === 'INSUFFICIENT_FUNDS') {
// Check balance
const balance = await provider.getBalance(tx.from);
const required = tx.value + (tx.gasLimit * tx.gasPrice);
console.error(`Insufficient funds. Have: ${balance}, Need: ${required}`);
throw error;
} else if (error.message.includes('transaction pool is full')) {
// Network congestion - wait and retry
console.log('Network congested, waiting...');
await new Promise(r => setTimeout(r, 5000));
// Retry with higher gas price
const gasPrice = await provider.getGasPrice();
return { ...tx, gasPrice: gasPrice * 120n / 100n };
}
throw error;
}
Best Practices
1. Always Use Proper Nonce Management
class NonceManager {
constructor(provider, address) {
this.provider = provider;
this.address = address;
this.pendingNonce = null;
}
async getNonce() {
const onchainNonce = await this.provider.getTransactionCount(this.address);
if (this.pendingNonce === null || this.pendingNonce < onchainNonce) {
this.pendingNonce = onchainNonce;
}
return this.pendingNonce++;
}
reset() {
this.pendingNonce = null;
}
}
2. Implement Transaction Queue
class TransactionQueue {
constructor(wallet) {
this.wallet = wallet;
this.queue = [];
this.processing = false;
}
async add(tx) {
this.queue.push(tx);
if (!this.processing) {
this.process();
}
}
async process() {
this.processing = true;
while (this.queue.length > 0) {
const tx = this.queue.shift();
try {
const signedTx = await this.wallet.signTransaction(tx);
const hash = await this.wallet.provider.send('eth_sendRawTransaction', [signedTx]);
console.log('Sent:', hash);
} catch (error) {
console.error('Failed to send:', error);
// Optionally re-queue or handle error
}
// Rate limiting
await new Promise(r => setTimeout(r, 1000));
}
this.processing = false;
}
}
Need help? Contact our support team or check the Linea documentation.