eth_sendRawTransaction
Submits a pre-signed transaction to the network for broadcast and execution. Returns the transaction hash if successful.
When to Use This Method​
eth_sendRawTransaction
is required for:
- Sending ETH - Transfer value between accounts
- Token Transfers - Execute ERC20/NFT transfers
- Contract Interactions - Call state-changing functions
- Contract Deployment - Deploy new smart contracts
Parameters​
- Signed Transaction Data -
DATA
- The signed transaction data in RLP-encoded format
- Must be prefixed with
0x
{
"jsonrpc": "2.0",
"method": "eth_sendRawTransaction",
"params": [
"0xf86c0185046110...signed_transaction_data...90a0cf5e"
],
"id": 1
}
Returns​
DATA
- 32 Bytes - The transaction hash, or error if transaction invalid.
Implementation Examples​
- JavaScript
- Python
import { JsonRpcProvider, Wallet, parseEther, parseUnits } from 'ethers';
const provider = new JsonRpcProvider('https://api-base-mainnet.n.dwellir.com/YOUR_API_KEY');
// Transaction builder and sender
class TransactionManager {
constructor(provider, privateKey) {
this.provider = provider;
this.wallet = new Wallet(privateKey, provider);
}
async sendETH(to, amount) {
try {
// Build transaction
const tx = {
to: to,
value: parseEther(amount),
// Base uses EIP-1559 transactions
type: 2,
chainId: 8453, // Base mainnet
};
// Estimate gas and get fee data
const [gasLimit, feeData] = await Promise.all([
this.provider.estimateGas({...tx, from: this.wallet.address}),
this.provider.getFeeData()
]);
tx.gasLimit = gasLimit;
tx.maxFeePerGas = feeData.maxFeePerGas;
tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
// Sign and send
const signedTx = await this.wallet.signTransaction(tx);
const txHash = await this.provider.send('eth_sendRawTransaction', [signedTx]);
console.log('Transaction sent:', txHash);
// Wait for confirmation
const receipt = await this.provider.waitForTransaction(txHash);
return {
hash: txHash,
status: receipt.status === 1 ? 'success' : 'failed',
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed.toString(),
effectiveGasPrice: receipt.effectiveGasPrice.toString()
};
} catch (error) {
console.error('Transaction failed:', error);
throw error;
}
}
async sendToken(tokenAddress, to, amount, decimals = 18) {
const ERC20_ABI = [
"function transfer(address to, uint256 amount) returns (bool)"
];
const token = new Contract(tokenAddress, ERC20_ABI, this.wallet);
// Build transaction
const amountWei = parseUnits(amount, decimals);
const tx = await token.transfer.populateTransaction(to, amountWei);
// Add gas settings
const [gasLimit, feeData] = await Promise.all([
this.provider.estimateGas({...tx, from: this.wallet.address}),
this.provider.getFeeData()
]);
tx.gasLimit = gasLimit * 110n / 100n; // Add 10% buffer
tx.maxFeePerGas = feeData.maxFeePerGas;
tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
tx.type = 2;
tx.chainId = 8453;
// Sign and send
const signedTx = await this.wallet.signTransaction(tx);
const txHash = await this.provider.send('eth_sendRawTransaction', [signedTx]);
return txHash;
}
async deployContract(bytecode, abi, constructorArgs = []) {
const factory = new ContractFactory(abi, bytecode, this.wallet);
// Estimate deployment cost
const deployTx = factory.getDeployTransaction(...constructorArgs);
const gasLimit = await this.provider.estimateGas({
...deployTx,
from: this.wallet.address
});
// Deploy with proper gas settings
const contract = await factory.deploy(...constructorArgs, {
gasLimit: gasLimit * 120n / 100n, // 20% buffer for deployment
maxFeePerGas: (await this.provider.getFeeData()).maxFeePerGas,
maxPriorityFeePerGas: (await this.provider.getFeeData()).maxPriorityFeePerGas
});
await contract.waitForDeployment();
return {
address: await contract.getAddress(),
deploymentHash: contract.deploymentTransaction().hash,
gasUsed: (await contract.deploymentTransaction().wait()).gasUsed.toString()
};
}
async batchSend(transactions) {
const results = [];
const nonce = await this.provider.getTransactionCount(this.wallet.address);
for (let i = 0; i < transactions.length; i++) {
const tx = {
...transactions[i],
nonce: nonce + i,
type: 2,
chainId: 8453,
gasLimit: await this.provider.estimateGas({
...transactions[i],
from: this.wallet.address
})
};
// Add fee data
const feeData = await this.provider.getFeeData();
tx.maxFeePerGas = feeData.maxFeePerGas;
tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
// Sign and send
const signedTx = await this.wallet.signTransaction(tx);
const txHash = await this.provider.send('eth_sendRawTransaction', [signedTx]);
results.push({
index: i,
hash: txHash,
nonce: tx.nonce
});
}
return results;
}
}
// Advanced transaction with retry logic
async function sendWithRetry(provider, signedTx, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const txHash = await provider.send('eth_sendRawTransaction', [signedTx]);
return txHash;
} catch (error) {
if (error.message.includes('already known')) {
// Transaction already in mempool
const tx = ethers.Transaction.from(signedTx);
return tx.hash;
}
if (error.message.includes('replacement transaction underpriced')) {
// Need to increase gas price
throw new Error('Gas price too low, please rebuild transaction with higher fees');
}
if (i === maxRetries - 1) throw error;
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
// Monitor mempool for transaction
async function monitorTransaction(provider, txHash) {
const startTime = Date.now();
const timeout = 60000; // 1 minute timeout
while (Date.now() - startTime < timeout) {
// Check if transaction is mined
const receipt = await provider.getTransactionReceipt(txHash);
if (receipt) {
return {
status: 'confirmed',
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed.toString()
};
}
// Check if still in mempool
const tx = await provider.getTransaction(txHash);
if (!tx) {
return { status: 'dropped' };
}
await new Promise(resolve => setTimeout(resolve, 2000));
}
return { status: 'timeout' };
}
from web3 import Web3
from eth_account import Account
from eth_utils import to_wei, from_wei
import time
from typing import Dict, Any, Optional, List
w3 = Web3(Web3.HTTPProvider('https://api-base-mainnet.n.dwellir.com/YOUR_API_KEY'))
class TransactionSender:
"""Handle transaction signing and broadcasting on Base"""
def __init__(self, w3_instance, private_key):
self.w3 = w3_instance
self.account = Account.from_key(private_key)
self.address = self.account.address
self.chain_id = 8453 # Base mainnet
def send_eth(self, to_address: str, amount_eth: float) -> Dict[str, Any]:
"""Send ETH to an address"""
try:
# Build transaction
nonce = self.w3.eth.get_transaction_count(self.address)
# Get current gas prices (EIP-1559)
base_fee = self.w3.eth.get_block('latest')['baseFeePerGas']
max_priority_fee = self.w3.eth.max_priority_fee
max_fee = base_fee * 2 + max_priority_fee
transaction = {
'type': '0x2', # EIP-1559
'chainId': self.chain_id,
'from': self.address,
'to': to_address,
'value': to_wei(amount_eth, 'ether'),
'nonce': nonce,
'maxFeePerGas': max_fee,
'maxPriorityFeePerGas': max_priority_fee,
'gas': 21000 # Standard transfer
}
# Sign transaction
signed = self.account.sign_transaction(transaction)
# Send transaction
tx_hash = self.w3.eth.send_raw_transaction(signed.rawTransaction)
# Wait for receipt
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=60)
return {
'success': receipt['status'] == 1,
'hash': tx_hash.hex(),
'block': receipt['blockNumber'],
'gas_used': receipt['gasUsed'],
'effective_gas_price': receipt['effectiveGasPrice'],
'total_fee_eth': from_wei(
receipt['gasUsed'] * receipt['effectiveGasPrice'],
'ether'
)
}
except Exception as e:
return {'success': False, 'error': str(e)}
def send_token(
self,
token_address: str,
to_address: str,
amount: int,
gas_limit: int = 100000
) -> Dict[str, Any]:
"""Send ERC20 tokens"""
# ERC20 transfer function signature
function_signature = self.w3.keccak(text="transfer(address,uint256)")[:4]
# Encode parameters
encoded_to = to_address[2:].lower().zfill(64)
encoded_amount = hex(amount)[2:].zfill(64)
data = function_signature.hex() + encoded_to + encoded_amount
# Build transaction
nonce = self.w3.eth.get_transaction_count(self.address)
transaction = {
'type': '0x2',
'chainId': self.chain_id,
'from': self.address,
'to': token_address,
'value': 0,
'nonce': nonce,
'data': data,
'maxFeePerGas': self.w3.eth.gas_price * 2,
'maxPriorityFeePerGas': self.w3.eth.max_priority_fee,
'gas': gas_limit
}
# Estimate gas
try:
estimated_gas = self.w3.eth.estimate_gas(transaction)
transaction['gas'] = int(estimated_gas * 1.1) # 10% buffer
except:
pass
# Sign and send
signed = self.account.sign_transaction(transaction)
tx_hash = self.w3.eth.send_raw_transaction(signed.rawTransaction)
return {'hash': tx_hash.hex(), 'nonce': nonce}
def deploy_contract(
self,
bytecode: str,
constructor_args: bytes = b'',
gas_limit: int = 3000000
) -> Dict[str, Any]:
"""Deploy a smart contract"""
nonce = self.w3.eth.get_transaction_count(self.address)
transaction = {
'type': '0x2',
'chainId': self.chain_id,
'from': self.address,
'nonce': nonce,
'data': bytecode + constructor_args,
'value': 0,
'maxFeePerGas': self.w3.eth.gas_price * 2,
'maxPriorityFeePerGas': self.w3.eth.max_priority_fee,
'gas': gas_limit
}
# Estimate gas for deployment
try:
estimated_gas = self.w3.eth.estimate_gas(transaction)
transaction['gas'] = int(estimated_gas * 1.2) # 20% buffer
except Exception as e:
print(f"Gas estimation failed: {e}")
# Sign and send
signed = self.account.sign_transaction(transaction)
tx_hash = self.w3.eth.send_raw_transaction(signed.rawTransaction)
# Wait for deployment
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
return {
'success': receipt['status'] == 1,
'contract_address': receipt.get('contractAddress'),
'hash': tx_hash.hex(),
'gas_used': receipt['gasUsed']
}
def send_batch(
self,
transactions: List[Dict[str, Any]],
gas_price_multiplier: float = 1.1
) -> List[Dict[str, Any]]:
"""Send multiple transactions in sequence"""
results = []
base_nonce = self.w3.eth.get_transaction_count(self.address)
for i, tx_data in enumerate(transactions):
tx = {
'type': '0x2',
'chainId': self.chain_id,
'from': self.address,
'nonce': base_nonce + i,
'to': tx_data['to'],
'value': tx_data.get('value', 0),
'data': tx_data.get('data', '0x'),
'maxFeePerGas': int(self.w3.eth.gas_price * gas_price_multiplier),
'maxPriorityFeePerGas': self.w3.eth.max_priority_fee,
'gas': tx_data.get('gas', 21000)
}
try:
# Sign and send
signed = self.account.sign_transaction(tx)
tx_hash = self.w3.eth.send_raw_transaction(signed.rawTransaction)
results.append({
'index': i,
'hash': tx_hash.hex(),
'nonce': tx['nonce'],
'status': 'sent'
})
except Exception as e:
results.append({
'index': i,
'error': str(e),
'status': 'failed'
})
return results
def cancel_transaction(self, nonce: int) -> Optional[str]:
"""Cancel a pending transaction by sending 0 ETH to self with same nonce"""
transaction = {
'type': '0x2',
'chainId': self.chain_id,
'from': self.address,
'to': self.address,
'value': 0,
'nonce': nonce,
'maxFeePerGas': self.w3.eth.gas_price * 3, # Much higher gas
'maxPriorityFeePerGas': self.w3.eth.max_priority_fee * 3,
'gas': 21000
}
signed = self.account.sign_transaction(transaction)
try:
tx_hash = self.w3.eth.send_raw_transaction(signed.rawTransaction)
return tx_hash.hex()
except Exception as e:
return None
# Usage examples
sender = TransactionSender(w3, 'YOUR_PRIVATE_KEY')
# Send ETH
result = sender.send_eth('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5', 0.1)
print(f"Transaction hash: {result['hash']}")
print(f"Success: {result['success']}")
# Send tokens
token_result = sender.send_token(
token_address='0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
to_address='0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb5',
amount=1000000 # 1 USDC (6 decimals)
)
# Batch transactions
batch = [
{'to': '0xAddress1', 'value': to_wei(0.01, 'ether')},
{'to': '0xAddress2', 'value': to_wei(0.02, 'ether')},
{'to': '0xAddress3', 'value': to_wei(0.03, 'ether')}
]
batch_results = sender.send_batch(batch)
Common Use Cases​
1. Safe Transaction Broadcasting​
// Broadcast with safety checks
async function safeBroadcast(wallet, transaction) {
// Pre-flight checks
const balance = await wallet.provider.getBalance(wallet.address);
const estimatedCost = transaction.gasLimit * transaction.maxFeePerGas;
if (balance < estimatedCost + transaction.value) {
throw new Error('Insufficient balance for transaction');
}
// Check nonce
const expectedNonce = await wallet.provider.getTransactionCount(wallet.address);
if (transaction.nonce && transaction.nonce !== expectedNonce) {
console.warn(`Nonce mismatch: expected ${expectedNonce}, got ${transaction.nonce}`);
}
// Sign transaction
const signedTx = await wallet.signTransaction(transaction);
// Broadcast with retry
let attempts = 0;
while (attempts < 3) {
try {
const txHash = await wallet.provider.send('eth_sendRawTransaction', [signedTx]);
return txHash;
} catch (error) {
if (error.message.includes('already known')) {
// Transaction already sent
return ethers.Transaction.from(signedTx).hash;
}
attempts++;
if (attempts === 3) throw error;
await new Promise(r => setTimeout(r, 1000 * attempts));
}
}
}
2. Optimized Gas Strategy​
// Dynamic gas price management
async function optimizedSend(wallet, to, value) {
const provider = wallet.provider;
// Get network conditions
const [block, feeData, gasPrice] = await Promise.all([
provider.getBlock('latest'),
provider.getFeeData(),
provider.getGasPrice()
]);
// Calculate optimal fees
const baseFee = block.baseFeePerGas;
const priorityFee = feeData.maxPriorityFeePerGas;
// Adjust based on network congestion
const congestionFactor = Number(baseFee) / Number(parseUnits('10', 'gwei'));
const adjustedPriorityFee = priorityFee * BigInt(Math.ceil(congestionFactor));
const transaction = {
to: to,
value: parseEther(value),
type: 2,
chainId: 8453,
maxFeePerGas: baseFee * 2n + adjustedPriorityFee,
maxPriorityFeePerGas: adjustedPriorityFee,
gasLimit: 21000n
};
const signedTx = await wallet.signTransaction(transaction);
return await provider.send('eth_sendRawTransaction', [signedTx]);
}
3. Transaction Queue Management​
// Manage transaction queue
class TransactionQueue {
constructor(wallet) {
this.wallet = wallet;
this.queue = [];
this.processing = false;
}
async add(transaction) {
this.queue.push(transaction);
if (!this.processing) {
this.process();
}
}
async process() {
this.processing = true;
while (this.queue.length > 0) {
const tx = this.queue.shift();
try {
// Add nonce
tx.nonce = await this.wallet.provider.getTransactionCount(this.wallet.address);
// Sign and send
const signedTx = await this.wallet.signTransaction(tx);
const hash = await this.wallet.provider.send('eth_sendRawTransaction', [signedTx]);
console.log(`Transaction sent: ${hash}`);
// Wait for confirmation before next
await this.wallet.provider.waitForTransaction(hash);
} catch (error) {
console.error('Transaction failed:', error);
// Optionally re-queue or handle error
}
}
this.processing = false;
}
}
Error Handling​
Error Code | Description | Solution |
---|---|---|
-32000 | Invalid transaction | Check parameters and signature |
-32001 | Insufficient funds | Ensure account has enough ETH |
-32002 | Nonce too low | Transaction already processed |
-32003 | Transaction underpriced | Increase gas price |
async function handleSendError(error) {
const message = error.message || error.toString();
if (message.includes('insufficient funds')) {
return { error: 'Insufficient balance', recoverable: false };
}
if (message.includes('nonce too low')) {
return { error: 'Transaction already processed', recoverable: false };
}
if (message.includes('replacement transaction underpriced')) {
return { error: 'Gas price too low for replacement', recoverable: true };
}
if (message.includes('transaction already known')) {
return { error: 'Transaction already in mempool', recoverable: false };
}
return { error: message, recoverable: false };
}
Need help? Contact our support team or check the Base documentation.