Skip to content

Transaction Status

Track and monitor transaction status throughout its lifecycle, from submission to confirmation.

Basic Status Tracking

Use useWaitForTransactionReceipt to track transaction confirmation:

tsx
import { useWaitForTransactionReceipt } from "wagmi";
import { useSendTransaction } from "wagmi";

function TransactionTracker() {
  const { data: hash, sendTransaction } = useSendTransaction();

  const {
    isLoading: isConfirming,
    isSuccess: isConfirmed,
    isError,
    data: receipt,
  } = useWaitForTransactionReceipt({ hash });

  const handleSend = () => {
    sendTransaction({
      to: "0xA0Cf…251e",
      value: parseEther("0.05"),
    });
  };

  return (
    <div>
      <button onClick={handleSend}>Send Transaction</button>

      {hash && (
        <div>
          <p>Transaction Hash: {hash}</p>
          {isConfirming && <p>Waiting for confirmation...</p>}
          {isConfirmed && receipt && (
            <div>
              <p>✅ Transaction confirmed!</p>
              <p>Block: {receipt.blockNumber.toString()}</p>
              <p>Gas used: {receipt.gasUsed.toString()}</p>
            </div>
          )}
          {isError && <p>❌ Transaction failed</p>}
        </div>
      )}
    </div>
  );
}

Transaction Lifecycle States

Track all states of a transaction:

tsx
type TransactionState =
  | "idle"
  | "preparing"
  | "signing"
  | "pending"
  | "confirming"
  | "confirmed"
  | "failed";

function useTransactionState() {
  const [state, setState] = useState<TransactionState>("idle");
  const { sendTransaction, isPending, data: hash } = useSendTransaction();
  const {
    isLoading: isConfirming,
    isSuccess,
    isError,
  } = useWaitForTransactionReceipt({ hash });

  useEffect(() => {
    if (isPending && !hash) {
      setState("signing");
    } else if (hash && isConfirming) {
      setState("confirming");
    } else if (isSuccess) {
      setState("confirmed");
    } else if (isError) {
      setState("failed");
    }
  }, [isPending, hash, isConfirming, isSuccess, isError]);

  const handleTransaction = async () => {
    setState("preparing");
    try {
      await sendTransaction({
        to: "0xA0Cf…251e",
        value: parseEther("0.05"),
      });
    } catch (error) {
      setState("failed");
    }
  };

  return { state, handleTransaction };
}

Polling for Status

Manually poll for transaction status if needed:

tsx
import { usePublicClient } from "wagmi";

function useTransactionPolling(hash: `0x${string}` | undefined) {
  const publicClient = usePublicClient();
  const [status, setStatus] = useState<"pending" | "confirmed" | "failed">(
    "pending",
  );

  useEffect(() => {
    if (!hash || !publicClient) return;

    const checkStatus = async () => {
      try {
        const receipt = await publicClient.getTransactionReceipt({ hash });
        if (receipt) {
          setStatus("confirmed");
        }
      } catch (error) {
        // Transaction might not be mined yet
        setTimeout(checkStatus, 2000); // Poll every 2 seconds
      }
    };

    checkStatus();
  }, [hash, publicClient]);

  return status;
}

Transaction Receipt Details

Access detailed receipt information:

tsx
function TransactionDetails({ hash }: { hash: `0x${string}` }) {
  const { data: receipt, isLoading } = useWaitForTransactionReceipt({ hash });

  if (isLoading) {
    return <div>Loading transaction details...</div>;
  }

  if (!receipt) {
    return <div>Transaction not found</div>;
  }

  return (
    <div>
      <h3>Transaction Details</h3>
      <p>Status: {receipt.status === "success" ? "Success" : "Failed"}</p>
      <p>Block Number: {receipt.blockNumber.toString()}</p>
      <p>Block Hash: {receipt.blockHash}</p>
      <p>Gas Used: {receipt.gasUsed.toString()}</p>
      <p>Effective Gas Price: {receipt.gasPrice?.toString()}</p>
      <p>Transaction Hash: {receipt.transactionHash}</p>
      <p>From: {receipt.from}</p>
      <p>To: {receipt.to}</p>
    </div>
  );
}

Error handling

Transaction confirmation failures (e.g. revert) are exposed via useWaitForTransactionReceipt's isError and error. Use them to show a failure message or retry. For user rejection and send-time errors, see Best practices — Error handling.

Status UI Component

Create a reusable transaction status component:

tsx
function TransactionStatus({
  hash,
  onSuccess,
}: {
  hash: `0x${string}` | undefined;
  onSuccess?: () => void;
}) {
  const {
    isLoading,
    isSuccess,
    isError,
    data: receipt,
  } = useWaitForTransactionReceipt({ hash });

  useEffect(() => {
    if (isSuccess && onSuccess) {
      onSuccess();
    }
  }, [isSuccess, onSuccess]);

  if (!hash) return null;

  if (isLoading) {
    return (
      <div className="transaction-status pending">
        <Spinner />
        <p>Transaction pending...</p>
        <p className="hash">{hash}</p>
      </div>
    );
  }

  if (isError) {
    return (
      <div className="transaction-status error">
        <p>❌ Transaction failed</p>
        <p className="hash">{hash}</p>
      </div>
    );
  }

  if (isSuccess && receipt) {
    return (
      <div className="transaction-status success">
        <p>✅ Transaction confirmed!</p>
        <p>Block: {receipt.blockNumber.toString()}</p>
        <p className="hash">{hash}</p>
      </div>
    );
  }

  return null;
}

Best Practices

  1. Always track status: Use useWaitForTransactionReceipt to monitor transaction confirmation
  2. Show user feedback: Display clear status messages at each stage
  3. Handle errors gracefully: Provide actionable error messages
  4. Display transaction hash: Allow users to view their transaction on block explorers
  5. Call callbacks on success: Use useEffect to trigger actions when transaction confirms

Link to block explorers for transaction details:

tsx
function TransactionLink({
  hash,
  chainId,
}: {
  hash: `0x${string}`;
  chainId: number;
}) {
  const explorerUrl =
    chainId === 42220
      ? `https://celoscan.io/tx/${hash}` // Celo mainnet
      : `https://sepolia.celoscan.io/tx/${hash}`; // Celo Sepolia

  return (
    <a href={explorerUrl}>
      View on CeloScan
    </a>
  );
}

Next Steps