Handle transactions
Handle EVM transactions in your Wagmi or Vanilla JavaScript dapp. With the SDK, you can:
- Send transactions.
- Track transaction status in real time.
- Estimate gas costs accurately.
- Handle transaction errors gracefully.
- Manage complex transaction patterns.
Use Wagmi
Wagmi provides hooks for sending transactions and tracking their status. The following are examples of sending a basic transaction and an advanced transaction with gas estimation.
Basic transaction
import { parseEther } from "viem"
import { useSendTransaction, useWaitForTransactionReceipt } from "wagmi"
function SendTransaction() {
  const { 
    data: hash,
    error,
    isPending,
    sendTransaction
  } = useSendTransaction()
  const { 
    isLoading: isConfirming,
    isSuccess: isConfirmed 
  } = useWaitForTransactionReceipt({
    hash
  })
  async function handleSend() {
    sendTransaction({
      to: "0x...", 
      value: parseEther("0.1")  // 0.1 ETH
    })
  }
  return (
    <div>
      <button 
        onClick={handleSend}
        disabled={isPending}
      >
        {isPending ? "Confirming..." : "Send 0.1 ETH"}
      </button>
      {hash && (
        <div>
          Transaction Hash: {hash}
          {isConfirming && <div>Waiting for confirmation...</div>}
          {isConfirmed && <div>Transaction confirmed!</div>}
        </div>
      )}
      {error && <div>Error: {error.message}</div>}
    </div>
  )
}
Advanced transaction with gas estimation
import { parseEther } from "viem"
import { 
  useSendTransaction, 
  useWaitForTransactionReceipt,
  useEstimateGas
} from "wagmi"
function AdvancedTransaction() {
  const transaction = {
    to: "0x...",
    value: parseEther("0.1"),
    data: "0x..." // Optional contract interaction data
  }
  // Estimate gas
  const { data: gasEstimate } = useEstimateGas(transaction)
  const { sendTransaction } = useSendTransaction({
    ...transaction,
    gas: gasEstimate,
    onSuccess: (hash) => {
      console.log("Transaction sent:", hash)
    }
  })
  return <button onClick={() => sendTransaction()}>Send with Gas Estimate</button>
}
Use Vanilla JavaScript
You can implement transaction handling directly in Vanilla JavaScript. The following are examples of sending a basic transaction and an advanced transaction with gas estimation.
Basic transaction
async function sendTransaction(recipientAddress, amount) {
  try {
    // Get current account
    const accounts = await ethereum.request({ 
      method: "eth_requestAccounts" 
    });
    const from = accounts[0];
    // Convert ETH amount to wei (hex)
    const value = `0x${(amount * 1e18).toString(16)}`;
    // Prepare transaction
    const transaction = {
      from,
      to: recipientAddress,
      value,
      // Gas fields are optional - MetaMask will estimate
    };
    // Send transaction
    const txHash = await ethereum.request({
      method: "eth_sendTransaction",
      params: [transaction],
    });
    return txHash;
  } catch (error) {
    if (error.code === 4001) {
      throw new Error("Transaction rejected by user");
    }
    throw error;
  }
}
// Track transaction status
function watchTransaction(txHash) {
  return new Promise((resolve, reject) => {
    const checkTransaction = async () => {
      try {
        const tx = await ethereum.request({
          method: "eth_getTransactionReceipt",
          params: [txHash],
        });
        if (tx) {
          if (tx.status === "0x1") {
            resolve(tx);
          } else {
            reject(new Error("Transaction failed"));
          }
        } else {
          setTimeout(checkTransaction, 2000); // Check every 2 seconds
        }
      } catch (error) {
        reject(error);
      }
    };
    checkTransaction();
  });
}
The following is an example implementation of the basic transaction:
<div class="transaction-form">
  <input type="text" id="recipient" placeholder="Recipient Address">
  <input type="number" id="amount" placeholder="Amount in ETH">
  <button onclick="handleSend()">Send ETH</button>
  <div id="status"></div>
</div>
<script>
async function handleSend() {
  const recipient = document.getElementById("recipient").value;
  const amount = document.getElementById("amount").value;
  const status = document.getElementById("status");
  
  try {
    status.textContent = "Sending transaction...";
    const txHash = await sendTransaction(recipient, amount);
    status.textContent = `Transaction sent: ${txHash}`;
    // Watch for confirmation
    status.textContent = "Waiting for confirmation...";
    await watchTransaction(txHash);
    status.textContent = "Transaction confirmed!";
  } catch (error) {
    status.textContent = `Error: ${error.message}`;
  }
}
</script>
Advanced transaction with gas estimation
async function estimateGas(transaction) {
  try {
    const gasEstimate = await ethereum.request({
      method: "eth_estimateGas",
      params: [transaction]
    });
    
    // Add 20% buffer for safety
    return BigInt(gasEstimate) * 120n / 100n;
  } catch (error) {
    console.error("Gas estimation failed:", error);
    throw error;
  }
}
info
See the Provider API reference and JSON-RPC API reference for more information.
Best practices
Follow these best practices when handling transactions.
Transaction security
- Always validate inputs before sending transactions.
- Check wallet balances to ensure sufficient funds.
- Verify addresses are valid.
Error handling
- Handle common errors like user rejection and insufficient funds.
- Provide clear error messages to users.
- Implement proper error recovery flows.
- Consider network congestion in gas estimates.
User experience
- Display clear loading states during transactions.
- Show transaction progress in real time.
- Provide detailed transaction information.
Common errors
| Error code | Description | Solution | 
|---|---|---|
| 4001 | User rejected transaction | Show a retry option and a clear error message. | 
| -32603 | Insufficient funds | Check the balance before sending a transaction. | 
| -32000 | Gas too low | Increase the gas limit or add a buffer to the estimation. | 
| -32002 | Request already pending | Prevent multiple concurrent transactions. | 
Next steps
See the following guides to add more functionality to your dapp: