eth_sendRawTransaction
Sends a signed raw transaction to the Avalanche C-Chain network for execution.
When to Use This Method
eth_sendRawTransaction
is essential for:
- Hardware Wallet Integration - Submit transactions signed by hardware wallets
- Offline Signing - Send transactions signed in secure offline environments
- Custom Signing Logic - Implement custom transaction signing workflows
- Batch Transactions - Submit multiple pre-signed transactions efficiently
Parameters
- Signed Transaction Data -
DATA
- The signed raw transaction data in RLP-encoded format
- Format:
0x
prefixed hexadecimal string
{
"jsonrpc": "2.0",
"method": "eth_sendRawTransaction",
"params": [
"0xf86c808504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080258080"
],
"id": 1
}
Returns
DATA
- 32-byte transaction hash, or error if transaction is invalid.
Implementation Examples
- cURL
- JavaScript
- Python
- Go
curl -X POST https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_sendRawTransaction",
"params": [
"0xf86c808504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080258080"
],
"id": 1
}'
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';
const provider = new JsonRpcProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc');
// Create and send raw transaction
async function sendRawTransaction(privateKey, to, value) {
// Create wallet from private key
const wallet = new Wallet(privateKey, provider);
// Prepare transaction
const tx = {
to: to,
value: parseEther(value),
gasLimit: 21000n,
gasPrice: await provider.getGasPrice()
};
// Sign the transaction
const signedTx = await wallet.signTransaction(tx);
// Send raw transaction
const txResponse = await provider.broadcastTransaction(signedTx);
console.log('Transaction Hash:', txResponse.hash);
console.log('Waiting for confirmation...');
// Wait for confirmation
const receipt = await txResponse.wait();
console.log('Transaction confirmed in block:', receipt.blockNumber);
return receipt;
}
// Batch send multiple raw transactions
async function batchSendRawTransactions(signedTxs) {
const promises = signedTxs.map(async (signedTx, index) => {
try {
const txResponse = await provider.broadcastTransaction(signedTx);
return {
index,
success: true,
hash: txResponse.hash,
signedTx
};
} catch (error) {
return {
index,
success: false,
error: error.message,
signedTx
};
}
});
const results = await Promise.all(promises);
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
console.log(`Sent ${successful.length} transactions successfully`);
console.log(`Failed to send ${failed.length} transactions`);
return { successful, failed };
}
// Send EIP-1559 transaction
async function sendEIP1559Transaction(privateKey, to, value) {
const wallet = new Wallet(privateKey, provider);
// Get current fee data
const feeData = await provider.getFeeData();
const tx = {
to: to,
value: parseEther(value),
gasLimit: 21000n,
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
type: 2 // EIP-1559
};
const signedTx = await wallet.signTransaction(tx);
const txResponse = await provider.broadcastTransaction(signedTx);
return txResponse;
}
from web3 import Web3
from eth_account import Account
import os
w3 = Web3(Web3.HTTPProvider('https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc'))
def send_raw_transaction(private_key, to_address, value_avax, gas_price=None):
"""Send a raw transaction on Avalanche"""
# Create account from private key
account = Account.from_key(private_key)
# Get current nonce
nonce = w3.eth.get_transaction_count(account.address)
# Get gas price if not provided
if gas_price is None:
gas_price = w3.eth.gas_price
# Build transaction
transaction = {
'to': to_address,
'value': w3.to_wei(value_avax, 'ether'),
'gas': 21000,
'gasPrice': gas_price,
'nonce': nonce,
'chainId': 43114 # Avalanche mainnet
}
# Sign transaction
signed_txn = w3.eth.account.sign_transaction(transaction, private_key)
# Send raw transaction
tx_hash = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
print(f"Transaction sent: {tx_hash.hex()}")
# Wait for confirmation
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Transaction confirmed in block: {receipt['blockNumber']}")
return receipt
def send_eip1559_transaction(private_key, to_address, value_avax):
"""Send EIP-1559 transaction"""
account = Account.from_key(private_key)
nonce = w3.eth.get_transaction_count(account.address)
# Get current fee data
latest_block = w3.eth.get_block('latest')
base_fee = latest_block['baseFeePerGas']
max_priority_fee = w3.to_wei(2, 'gwei') # 2 Gwei tip
max_fee = base_fee * 2 + max_priority_fee
transaction = {
'to': to_address,
'value': w3.to_wei(value_avax, 'ether'),
'gas': 21000,
'maxFeePerGas': max_fee,
'maxPriorityFeePerGas': max_priority_fee,
'nonce': nonce,
'chainId': 43114,
'type': 2 # EIP-1559
}
signed_txn = w3.eth.account.sign_transaction(transaction, private_key)
tx_hash = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
return tx_hash.hex()
def batch_send_transactions(transactions_data):
"""Send multiple transactions in batch"""
results = []
for tx_data in transactions_data:
try:
tx_hash = send_raw_transaction(
tx_data['private_key'],
tx_data['to'],
tx_data['value']
)
results.append({
'success': True,
'hash': tx_hash,
'to': tx_data['to']
})
except Exception as e:
results.append({
'success': False,
'error': str(e),
'to': tx_data['to']
})
return results
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"log"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
client, err := ethclient.Dial("https://api-avalanche-mainnet-archive.n.dwellir.com/YOUR_API_KEY/ext/bc/C/rpc")
if err != nil {
log.Fatal(err)
}
// Example private key (replace with actual key)
privateKey, err := crypto.HexToECDSA("your_private_key_here")
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("Cannot assert type: publicKey is not of type *ecdsa.PublicKey")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
// Get nonce
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
// Transaction details
value := big.NewInt(1000000000000000000) // 1 AVAX
gasLimit := uint64(21000)
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
toAddress := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb")
// Create transaction
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)
// Sign transaction
chainID, err := client.NetworkID(context.Background())
if err != nil {
log.Fatal(err)
}
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
log.Fatal(err)
}
// Send raw transaction
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Transaction sent: %s\n", signedTx.Hash().Hex())
}
Response Example
Successful Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
Error Response
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "insufficient funds for gas * price + value"
}
}
Common Use Cases
1. Hardware Wallet Integration
Send transactions signed by hardware wallets:
// Simulate hardware wallet signing
async function sendHardwareWalletTransaction(hardwareSignedTx) {
try {
// Broadcast the pre-signed transaction
const txResponse = await provider.broadcastTransaction(hardwareSignedTx);
console.log('Hardware wallet transaction sent:', txResponse.hash);
// Monitor confirmation
const receipt = await txResponse.wait(1); // 1 confirmation
return {
success: true,
hash: txResponse.hash,
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed.toString()
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
2. Multi-Sig Wallet Operations
Handle multi-signature wallet transactions:
async function sendMultiSigTransaction(multiSigData, signatures) {
// Assume multi-sig contract interaction
const multiSigContract = new Contract(multiSigData.contractAddress, MULTISIG_ABI, provider);
// Encode the transaction data
const txData = multiSigContract.interface.encodeFunctionData('executeTransaction', [
multiSigData.to,
multiSigData.value,
multiSigData.data,
signatures
]);
// Create raw transaction
const tx = {
to: multiSigData.contractAddress,
data: txData,
gasLimit: 500000n,
gasPrice: await provider.getGasPrice()
};
// Sign with one of the owners
const wallet = new Wallet(multiSigData.ownerPrivateKey, provider);
const signedTx = await wallet.signTransaction(tx);
// Send raw transaction
const txResponse = await provider.broadcastTransaction(signedTx);
return txResponse.hash;
}
3. Gas Optimization Strategies
Implement dynamic gas pricing for optimal transaction fees:
async function sendOptimizedTransaction(privateKey, to, value) {
const wallet = new Wallet(privateKey, provider);
// Get current network conditions
const feeData = await provider.getFeeData();
const latestBlock = await provider.getBlock('latest');
// Calculate optimal gas price based on network congestion
const baseFee = latestBlock.baseFeePerGas || 0n;
const gasUsage = Number(latestBlock.gasUsed) / Number(latestBlock.gasLimit);
let priorityFee = feeData.maxPriorityFeePerGas || parseUnits('2', 'gwei');
// Adjust priority fee based on network congestion
if (gasUsage > 0.7) {
priorityFee = priorityFee * 150n / 100n; // +50% during congestion
} else if (gasUsage < 0.3) {
priorityFee = priorityFee * 80n / 100n; // -20% during low usage
}
const maxFeePerGas = baseFee * 2n + priorityFee;
const tx = {
to: to,
value: parseEther(value),
gasLimit: 21000n,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: priorityFee,
type: 2
};
const signedTx = await wallet.signTransaction(tx);
const txResponse = await provider.broadcastTransaction(signedTx);
console.log('Optimized transaction sent with fees:', {
maxFeePerGas: formatUnits(maxFeePerGas, 'gwei') + ' gwei',
priorityFee: formatUnits(priorityFee, 'gwei') + ' gwei'
});
return txResponse;
}
4. Transaction Queue Management
Manage pending transactions with nonce handling:
class TransactionQueue {
constructor(provider, privateKey) {
this.provider = provider;
this.wallet = new Wallet(privateKey, provider);
this.pendingTxs = new Map();
this.currentNonce = null;
}
async initialize() {
this.currentNonce = await this.provider.getTransactionCount(this.wallet.address);
}
async queueTransaction(to, value, data = '0x') {
if (this.currentNonce === null) {
await this.initialize();
}
const tx = {
to: to,
value: value,
data: data,
gasLimit: 100000n,
gasPrice: await this.provider.getGasPrice(),
nonce: this.currentNonce
};
const signedTx = await this.wallet.signTransaction(tx);
try {
const txResponse = await this.provider.broadcastTransaction(signedTx);
this.pendingTxs.set(this.currentNonce, {
hash: txResponse.hash,
timestamp: Date.now()
});
this.currentNonce++;
return txResponse.hash;
} catch (error) {
throw new Error(`Failed to queue transaction: ${error.message}`);
}
}
async checkPendingTransactions() {
const confirmedNonce = await this.provider.getTransactionCount(this.wallet.address);
// Remove confirmed transactions
for (const [nonce, txData] of this.pendingTxs) {
if (nonce < confirmedNonce) {
console.log(`Transaction ${txData.hash} confirmed`);
this.pendingTxs.delete(nonce);
}
}
return {
confirmedNonce,
pendingCount: this.pendingTxs.size
};
}
}
Error Handling
Common errors and solutions:
Error Code | Description | Solution |
---|---|---|
-32000 | Insufficient funds | Check account balance |
-32000 | Nonce too low | Use correct nonce value |
-32000 | Gas price too low | Increase gas price |
-32602 | Invalid transaction data | Verify RLP encoding |
async function safeSubmitRawTransaction(signedTx, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const txResponse = await provider.broadcastTransaction(signedTx);
return { success: true, hash: txResponse.hash };
} catch (error) {
console.error(`Attempt ${i + 1} failed:`, error.message);
if (error.message.includes('nonce too low')) {
return { success: false, error: 'Transaction nonce is too low. Transaction may already be processed.' };
}
if (error.message.includes('insufficient funds')) {
return { success: false, error: 'Insufficient AVAX balance for transaction.' };
}
if (error.message.includes('gas price too low')) {
// Could implement gas price increase logic here
return { success: false, error: 'Gas price too low. Please increase gas price.' };
}
// Retry for other errors
if (i === maxRetries - 1) {
return { success: false, error: error.message };
}
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // Exponential backoff
}
}
}
Security Best Practices
1. Private Key Management
// Never expose private keys in client-side code
// Use environment variables or secure key management
const privateKey = process.env.PRIVATE_KEY;
// Validate private key format
function validatePrivateKey(key) {
if (!key || key.length !== 66 || !key.startsWith('0x')) {
throw new Error('Invalid private key format');
}
return key;
}
2. Transaction Validation
async function validateTransaction(signedTx) {
try {
// Parse the signed transaction
const parsedTx = Transaction.from(signedTx);
// Validate transaction parameters
if (!parsedTx.to || !parsedTx.value || !parsedTx.gasLimit) {
throw new Error('Invalid transaction parameters');
}
// Check if sender has sufficient balance
const senderBalance = await provider.getBalance(parsedTx.from);
const totalCost = parsedTx.value + (parsedTx.gasLimit * parsedTx.gasPrice);
if (senderBalance < totalCost) {
throw new Error('Insufficient balance');
}
return { valid: true };
} catch (error) {
return { valid: false, error: error.message };
}
}
Need help? Contact our support team or check the Avalanche documentation.