Docs
Centrifuge

eth_sendRawTransaction - Broadcast Signed...

Submit signed transactions to the Centrifuge Mainnet network. Essential for sending ETH, interacting with contracts, and deploying smart contracts.

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

  1. Signed Transaction Data - DATA
    • The signed transaction data in RLP-encoded format
    • Must be prefixed with 0x
JSON
{
  "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
import { JsonRpcProvider, Wallet, parseEther, parseUnits } from 'ethers';

const provider = new JsonRpcProvider('https://api-centrifuge.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),
        // Centrifuge uses EIP-1559 transactions
        type: 2,
        chainId: 1, // Centrifuge 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 = 1;

    // 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: 1,
        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' };
}
Python
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-centrifuge.n.dwellir.com/YOUR_API_KEY'))

class TransactionSender:
    """Handle transaction signing and broadcasting on Ethereum"""
    
    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 = 1  # Centrifuge 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

JavaScript
// 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

JavaScript
// 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: 1,
    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

JavaScript
// 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 CodeDescriptionSolution
-32000Invalid transactionCheck parameters and signature
-32001Insufficient fundsEnsure account has enough ETH
-32002Nonce too lowTransaction already processed
-32003Transaction underpricedIncrease gas price
JavaScript
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 Centrifuge documentation.