eth_sendRawTransaction - Celo RPC Method
Submit signed transactions to Celo. Essential for broadcasting transactions for mobile stablecoin payments (MiniPay 10M+ wallets), remittances, humanitarian aid, and local currency stablecoins (cUSD, cNGN, cEUR).
Submits a pre-signed transaction for broadcast to Celo.
Why Celo? Build on the mobile-first L2 powering 500K+ daily active users and $2B+ monthly stablecoin volume with phone number-based addressing, sub-cent fees, 150+ country adoption, Nightfall privacy layer, and Opera browser integration.
When to Use This Method
The eth_sendRawTransaction method serves these key scenarios for mobile payment developers, fintech builders, and teams targeting emerging markets:
- Submit pre-signed transactions - Broadcast transactions that were signed offline or in a secure enclave, keeping private keys away from the RPC endpoint
- Broadcast from offline signers - Air-gapped devices and hardware wallets first sign, then use this method to push the signed payload to Celo
- Resubmit stuck transactions - Replace pending transactions with the same nonce and a higher gas price when the original is not being included in blocks
- Deploy contracts programmatically - Submit contract creation transactions containing compiled bytecode and constructor arguments
Common Use Cases
1. Sign and Send an ETH Transfer
Build, sign, and broadcast a native ETH transfer with proper nonce management. The nonce must be retrieved from the node immediately before signing to avoid conflicts with pending transactions.
import { JsonRpcProvider, Wallet, parseEther, Transaction } from 'ethers';
const provider = new JsonRpcProvider('https://api-celo-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);
async function sendNativeTransfer(to, amountInEth) {
const tx = await wallet.sendTransaction({
to: to,
value: parseEther(amountInEth)
});
console.log('Transaction submitted:', tx.hash);
const receipt = await tx.wait();
console.log(`Confirmed in block ${receipt.blockNumber}, gas used: ${receipt.gasUsed}`);
return receipt;
}
const receipt = await sendNativeTransfer('0x471EcE3750Da237f93B8E339c536989b8978a438', '0.01');2. Resubmit a Stuck Transaction
If a pending transaction is not being confirmed due to low gas, submit a replacement with the same nonce and a higher gas price. The replacement must use an increased fee to be accepted by the Celo mempool.
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';
const provider = new JsonRpcProvider('https://api-celo-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);
async function speedUpTransaction(originalTxHash, gasMultiplier = 1.5) {
const pendingTx = await provider.getTransaction(originalTxHash);
if (!pendingTx) throw new Error('Transaction not found');
const feeData = await provider.getFeeData();
const replacementTx = {
to: pendingTx.to,
value: pendingTx.value,
data: pendingTx.data,
nonce: pendingTx.nonce,
maxFeePerGas: feeData.maxFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * BigInt(Math.floor(gasMultiplier * 100)) / 100n,
gasLimit: pendingTx.gasLimit
};
const tx = await wallet.sendTransaction(replacementTx);
console.log('Replacement transaction:', tx.hash);
return tx;
}
const newTx = await speedUpTransaction('0x...');3. Deploy a Compiled Contract
Submit a contract deployment transaction containing the compiled bytecode. The to field is omitted for contract creation transactions; the contract address is deterministically derived from the sender address and nonce.
import { JsonRpcProvider, Wallet, ContractFactory } from 'ethers';
const provider = new JsonRpcProvider('https://api-celo-mainnet-archive.n.dwellir.com/YOUR_API_KEY');
const wallet = new Wallet('PRIVATE_KEY', provider);
const contractAbi = ['constructor(string memory name, uint256 supply)'];
const contractBytecode = '0x608060...';
async function deployContract(constructorArgs) {
const factory = new ContractFactory(contractAbi, contractBytecode, wallet);
const contract = await factory.deploy(...constructorArgs);
console.log('Deployment tx:', contract.deploymentTransaction().hash);
await contract.waitForDeployment();
console.log('Contract deployed at:', await contract.getAddress());
return contract;
}
const contract = await deployContract(['MyToken', 1000000]);Best Practices
- Always sign transactions client-side: never send private keys to any RPC endpoint, including https://api-celo-mainnet-archive.n.dwellir.com/YOUR_API_KEY
- Track nonces carefully to avoid gaps or conflicts: retrieve the pending nonce from
eth_getTransactionCountwith"pending"tag before each signing - The return value is a transaction hash: use
eth_getTransactionReceiptto poll for confirmation status - Handle replacement transactions correctly: the replacement must use the same nonce with a higher gas price
- Use
eth_estimateGasto set an appropriate gas limit before signing and sending
Code Examples
Error Handling
| Error Code | Message | Description |
|---|---|---|
| -32000 | Nonce too low | Transaction nonce already used |
| -32000 | Insufficient funds | Account has insufficient balance |
| -32000 | Gas too low | Gas limit insufficient |
| -32000 | Replacement underpriced | Gas price too low for replacement |
Related Methods
eth_estimateGas- Estimate gas requiredeth_gasPrice- Get current gas priceeth_getTransactionReceipt- Get transaction result
eth_accounts
Returns a list of addresses owned by the client on Celo. Typically returns an empty array on public RPC endpoints.
eth_getTransactionByHash
Retrieve transaction details by hash on Celo. Essential for mobile payment developers, fintech builders, and teams targeting emerging markets tracking transactions on the mobile-first L2 powering 500K+ daily active users and $2B+ monthly stablecoin volume.