⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip Alchemy's expensive compute units.
Switch Today →
Skip to main content

eth_sendRawTransaction

Submits a pre-signed transaction for broadcast to the Linea network.

Parameters

  1. 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

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;
}

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.