<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fagbemi Michael </title>
    <description>The latest articles on DEV Community by Fagbemi Michael  (@0xxmyke).</description>
    <link>https://dev.to/0xxmyke</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3587249%2F22bf1565-9ddb-4370-ab17-65bc2e30ad5e.png</url>
      <title>DEV Community: Fagbemi Michael </title>
      <link>https://dev.to/0xxmyke</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/0xxmyke"/>
    <language>en</language>
    <item>
      <title>Why Transaction Fail (and how to actually debug them)</title>
      <dc:creator>Fagbemi Michael </dc:creator>
      <pubDate>Thu, 30 Oct 2025 06:05:06 +0000</pubDate>
      <link>https://dev.to/0xxmyke/why-transaction-fail-and-how-to-actually-debug-them-12kb</link>
      <guid>https://dev.to/0xxmyke/why-transaction-fail-and-how-to-actually-debug-them-12kb</guid>
      <description>&lt;p&gt;&lt;strong&gt;NB: please use AI tool or a code editor for the code snippets in this article to structure the code for better understanding&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Short steps up front: when you see “TX failed” you don’t need to guess  ,follow a small repeatable checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;fetch receipt → 2) decode revert → 3) check gas/nonce/balance → 4) reproduce locally (fork) → 5) trace and fix state/ABI/permission problems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below is a plain, step-by-step article you can use to debug your transactions.&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;TL;DR (one-minute checklist)&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Get the tx receipt (eth_getTransactionReceipt). Look at status, gasUsed, logs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Try the same call with eth_call (or provider.call) to get a revert reason.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;eth_estimateGas vs submitted gas → Out-of-gas?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check eth_getTransactionByHash + txpool for nonce/pending issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reproduce on a local fork (anvil/hardhat) and use debug_traceTransaction to find the failing opcode.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix: allowance, approvals, owner/paused guards, correct ABI/address, or bump gas/nonce.&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;Who This is for &amp;amp; quick setup&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Target: Solidity engineers, integrators, infra/devops, and anyone sending transactions from wallets or bots.&lt;/p&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;p&gt;A JSON-RPC URL (Infura, Alchemy, QuickNode, or your running node).&lt;/p&gt;

&lt;p&gt;Tools (pick any): curl + node’s RPC, Foundry cast/anvil, Hardhat, ethers.js.&lt;/p&gt;

&lt;p&gt;Optional but useful: access to node with debug_traceTransaction enabled (geth or anvil do this).&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Quick setup commands (examples):&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;start a forked anvil (Foundry)&lt;br&gt;
anvil --fork-url &lt;a href="https://mainnet.infura.io/v3/$INFURA_KEY" rel="noopener noreferrer"&gt;https://mainnet.infura.io/v3/$INFURA_KEY&lt;/a&gt; --fork-block-number 18400000&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;or start Hardhat node fork&lt;br&gt;
npx hardhat node --fork &lt;a href="https://mainnet.infura.io/v3/$INFURA_KEY" rel="noopener noreferrer"&gt;https://mainnet.infura.io/v3/$INFURA_KEY&lt;/a&gt; --fork-block-number 18400000&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;*&lt;em&gt;Quick triage (first 60 seconds)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Run these checks immediately to eliminate common mistakes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Are you on the right chain / network id?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is the contract address correct and actually a contract? (eth_getCode)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Does the sender have enough ETH? (eth_getBalance)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is your nonce correct / any pending tx? (eth_getTransactionByHash + txpool)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Is there a missing token approve / allowance?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Was the gas limit too low?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;*&lt;em&gt;Commands to run right away:&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Receipt&lt;br&gt;
curl -s -X POST -H "Content-Type:application/json" \&lt;br&gt;
  --data '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":[""],"id":1}' $RPC_URL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Balance&lt;br&gt;
curl -s -X POST -H "Content-Type:application/json" \&lt;br&gt;
  --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["", "latest"],"id":1}' $RPC_URL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check if address has code (is a contract)&lt;br&gt;
curl -s -X POST -H "Content-Type:application/json" \&lt;br&gt;
  --data '{"jsonrpc":"2.0","method":"eth_getCode","params":["&lt;/p&gt;","latest"],"id":1}' $RPC_URL&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;*&lt;em&gt;Step 0 — Capture the exact tx data (always do this)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Before changing anything, copy the full original tx fields: from, to, data, value, gas, gasPrice or maxFeePerGas &amp;amp; maxPriorityFeePerGas, and nonce. You’ll replay this exact set in a local fork.&lt;/p&gt;

&lt;p&gt;You can fetch the on-chain transaction:&lt;/p&gt;

&lt;p&gt;curl -s -X POST -H "Content-Type:application/json" \&lt;br&gt;
  --data '{"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":[""],"id":1}' $RPC_URL&lt;/p&gt;

&lt;p&gt;Store those fields — they are the canonical reproduction input.&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;Step 1 — Inspect the receipt &amp;amp; quick interpretation&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Look at the transaction receipt fields of interest:&lt;/p&gt;

&lt;p&gt;status — 0 means revert; 1 means success.&lt;/p&gt;

&lt;p&gt;gasUsed — compare with what you set in gas. If gasUsed ≈ gas limit, likely OOG.&lt;/p&gt;

&lt;p&gt;logs — if events you expected are missing, the code likely reverted before emitting them.&lt;/p&gt;

&lt;p&gt;blockNumber — helpful to choose a fork block close to the original.&lt;/p&gt;

&lt;p&gt;Example interpretation:&lt;/p&gt;

&lt;p&gt;status: 0, gasUsed: 21000 → revert early (require/owner check or immediate revert)&lt;/p&gt;

&lt;p&gt;status: 0, gasUsed ~= gasLimit → likely ran out of gas&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;Step 2 — Decode the revert reason&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the node returns revert data, decode it. Use eth_call (simulation) because many providers return the revert string when simulating a call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ethers.js quick pattern:&lt;/p&gt;

&lt;p&gt;try {&lt;br&gt;
  await provider.call({ to: tx.to, data: tx.data, from: tx.from, value: tx.value });&lt;br&gt;
} catch (err) {&lt;br&gt;
  console.error("revert data:", err.error || err.data || err);&lt;br&gt;
  // If you have the ABI, use new ethers.utils.Interface(abi).parseError(revertData)&lt;br&gt;
}&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the revert data is ABI-encoded custom error, decode it with the contract ABI:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;const iface = new ethers.utils.Interface(contractAbi);&lt;br&gt;
const parsed = iface.parseError(revertData); // works for custom errors&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you only have raw bytes, first check for the standard Error selector (0x08c379a0 = "Error(string)"):&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;0x08c379a0 + encoded string → normal require("message").&lt;br&gt;
If another selector appears, it’s a custom error — parse with the ABI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the RPC doesn’t return a revert string, use local fork + provider.call or debug_traceTransaction to see more.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;*&lt;em&gt;Step 3 — Gas and out-of-gas checks&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Compare submitted gas to eth_estimateGas.&lt;/p&gt;

&lt;p&gt;If eth_estimateGas &amp;gt;&amp;gt; submitted gas → raise gas limit. If gasUsed is very close to gas limit, increase it.&lt;/p&gt;

&lt;p&gt;Commands:&lt;/p&gt;

&lt;h1&gt;
  
  
  Estimate gas for the same transaction
&lt;/h1&gt;

&lt;p&gt;curl -s -X POST -H "Content-Type:application/json" \&lt;br&gt;
 --data '{"jsonrpc":"2.0","method":"eth_estimateGas","params":[{ "to":"","data":"0x...","from":"0x..." }],"id":1}' $RPC_URL&lt;/p&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;p&gt;eth_estimateGas can fail if the call always reverts. Use a local fork and manipulate state to get a meaningful estimate if needed.&lt;/p&gt;

&lt;p&gt;On EIP-1559 chains, ensure maxFeePerGas and maxPriorityFeePerGas are set correctly.&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;Step 4 — Nonce / pending / replacement issues&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Symptoms: tx stays pending, or you get replacement transaction underpriced.&lt;/p&gt;

&lt;p&gt;Quick checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;eth_getTransactionByHash — looks at tx status.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;txpool_content (node-dependent) — inspect pending transactions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replace a stuck tx by resending with same nonce and a higher gas price / tip.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Commands:&lt;/p&gt;

&lt;h1&gt;
  
  
  txpool (Geth/Parity specific)
&lt;/h1&gt;

&lt;p&gt;curl -s -X POST -H "Content-Type:application/json" \&lt;br&gt;
  --data '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' $RPC_URL&lt;/p&gt;

&lt;p&gt;If you control the key, common fix is to resend a new transaction with same nonce + higher fee.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 5 — Reproduce the failure locally (forking)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Forking is the single most powerful debugging step. Start a local node at a block near the tx and replay the exact tx.&lt;/p&gt;

&lt;p&gt;Anvil (Foundry) example:&lt;/p&gt;

&lt;p&gt;anvil --fork-url $RPC_URL --fork-block-number  -p 8545&lt;/p&gt;

&lt;p&gt;Then use ethers or cast to call the tx or send the exact transaction fields to see the same failure locally. On a fork you can also change balances, allowances, or contract storage to test state-dependent failures.&lt;/p&gt;

&lt;p&gt;Replaying call with cast:&lt;/p&gt;

&lt;p&gt;cast call  "methodName(type...)" params --rpc-url &lt;a href="http://127.0.0.1:8545" rel="noopener noreferrer"&gt;http://127.0.0.1:8545&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Replaying raw via ethers:&lt;/p&gt;

&lt;p&gt;await provider.call({ to, data, from, value, gasLimit: gas });&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 6 — Trace the transaction to find the failing opcode&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use debug_traceTransaction to get a call-tree and the point of revert. This helps identify whether a particular external call reverts, a require fails, or an out-of-gas occurs.&lt;/p&gt;

&lt;p&gt;Example RPC:&lt;/p&gt;

&lt;p&gt;curl -s -X POST -H "Content-Type:application/json" \&lt;br&gt;
 --data '{"jsonrpc":"2.0","method":"debug_traceTransaction","params":["", {"tracer":"callTracer"}],"id":1}' $RPC_URL&lt;/p&gt;

&lt;p&gt;Interpreting traces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Look for the last call in the call tree with error or revert.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The trace may show the failing internal call (for example CALL to token contract failed).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you run traces locally with anvil/hardhat you’ll often get richer data than public nodes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;*&lt;em&gt;Step 7 — State-dependent issues (allowance, balances, timestamps)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Many reverts depend on the exact on-chain state at execution time:&lt;/p&gt;

&lt;p&gt;Common checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ERC20 allowance(owner, spender) and balanceOf(owner).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Contract paused() status.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Role/owner checks: owner() or hasRole(...).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Block/time dependence: block.timestamp or block.number used in logic.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On your fork you can mutate state before replaying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Transfer tokens into the account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Call a helper to set oracle price or mock contract responses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Increase allowance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example cast calls:&lt;/p&gt;

&lt;h1&gt;
  
  
  check allowance (foundry cast)
&lt;/h1&gt;

&lt;p&gt;cast call  "allowance(address,address)(uint256)" $OWNER $SPENDER --rpc-url &lt;a href="http://127.0.0.1:8545" rel="noopener noreferrer"&gt;http://127.0.0.1:8545&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 8 — External calls, oracles, and non-determinism&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your function calls other contracts or oracles, those external calls can revert or return unexpected values (zero price, stale timestamp, etc).&lt;/p&gt;

&lt;p&gt;How to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Trace to find which external call failed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On a fork, patch oracle storage or deploy a mock and point the caller to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For production fixes, add sanity checks (e.g., require(price &amp;gt; 0) or fallback behaviors).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Step 9 — Permission, pausability, initializer mistakes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Typical access issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Contract still paused.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Function restricted to onlyOwner or role-based guard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Missing initialize() or setup steps after deployment (proxy patterns).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick checks:&lt;/p&gt;

&lt;p&gt;const c = await ethers.getContractAt("MyContract", "&lt;/p&gt;");&lt;br&gt;
await c.owner();&lt;br&gt;
await c.paused();

&lt;p&gt;If you see revert: Ownable: caller is not the owner — adjust the caller or correct owner assignment.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 10 — ABI, address, and chain mismatch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you call the wrong address or wrong ABI, functions can revert or behave unexpectedly.&lt;/p&gt;

&lt;p&gt;Checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;eth_getCode(address) should return non-empty hex for a contract.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verified contract ABI from Etherscan / block explorer should match what you use locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For proxies, ensure you’re interacting with the proxy address with the implementation ABI (or use the proxy’s interface).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Command:&lt;/p&gt;

&lt;p&gt;curl -s -X POST -H "Content-Type:application/json" \&lt;br&gt;
  --data '{"jsonrpc":"2.0","method":"eth_getCode","params":["","latest"],"id":1}' $RPC_URL&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;Tooling quick-reference : &lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;anvil (Foundry) — fast local fork and tracing. anvil --fork-url  --fork-block-number &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;hardhat — local fork + console: npx hardhat node --fork &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;cast — quick read/write: cast call / cast send&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ethers.js — provider.call() to             simulate, provider.getTransaction() to fetch txs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;debug_traceTransaction — use for        opcode-level trace (requires node support)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tenderly / Blocknative / Etherscan TX viewer — nice UI for replays/traces&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;*&lt;em&gt;Ready scripts: fetch + replay + decode (copy/paste)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
1) replay-tx.js (node + ethers) — simulate and show revert data&lt;/p&gt;

&lt;p&gt;// node replay-tx.js  &lt;br&gt;
const { ethers } = require("ethers");&lt;br&gt;
(async()=&amp;gt;{&lt;br&gt;
  const [txHash, rpc] = process.argv.slice(2);&lt;br&gt;
  if(!txHash || !rpc) { console.error("Usage: node replay-tx.js  "); process.exit(1); }&lt;br&gt;
  const provider = new ethers.providers.JsonRpcProvider(rpc);&lt;br&gt;
  const tx = await provider.getTransaction(txHash);&lt;br&gt;
  console.log("tx:", tx);&lt;br&gt;
  try {&lt;br&gt;
    const res = await provider.call({&lt;br&gt;
      to: tx.to,&lt;br&gt;
      data: tx.data,&lt;br&gt;
      from: tx.from,&lt;br&gt;
      value: tx.value,&lt;br&gt;
      gasLimit: tx.gasLimit&lt;br&gt;
    });&lt;br&gt;
    console.log("call success (returned data):", res);&lt;br&gt;
  } catch (e) {&lt;br&gt;
    console.error("call reverted. raw:", e.error || e.data || e);&lt;br&gt;
  }&lt;br&gt;
})();&lt;/p&gt;

&lt;p&gt;2) quick-receipt.sh&lt;/p&gt;

&lt;h1&gt;
  
  
  !/usr/bin/env bash
&lt;/h1&gt;

&lt;p&gt;RPC=$1; TX=$2&lt;br&gt;
curl -s -X POST -H "Content-Type:application/json" --data \&lt;br&gt;
"{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionReceipt\",\"params\":[\"$TX\"],\"id\":1}" $RPC | jq .&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Short case studies :&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Case A — Missing ERC20 allowance&lt;/p&gt;

&lt;p&gt;Symptom: call to a marketplace contract reverts. Receipt shows status:0, no logs.&lt;/p&gt;

&lt;p&gt;Debug steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Check allowance(owner,market) → returns 0.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Approve correct amount and re-run the tx.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fix: erc20.approve(market, amount) or use permit() flow if available.&lt;/p&gt;

&lt;p&gt;Case B — Oracle returns 0 (price = 0)&lt;/p&gt;

&lt;p&gt;Symptom: Action reverts because division by zero or require(price &amp;gt; 0).&lt;/p&gt;

&lt;p&gt;Debug steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Trace shows external call to PriceFeed.latestAnswer() returned 0.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On a local fork set the oracle storage to a non-zero price or deploy a mock oracle.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fix: Update oracle, add guards to your contract, and add alerting on price feed changes.&lt;/p&gt;

&lt;p&gt;Case C — Out-of-gas because client used too small gasLimit&lt;/p&gt;

&lt;p&gt;Symptom: gasUsed equals the submitted gas and tx reverted.&lt;/p&gt;

&lt;p&gt;Debug steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;eth_estimateGas returns higher value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resend with increased gas limit or allow miner to pick gas.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fix: Use estimation in client or set a safe multiplier (e.g., 1.3 * estimateGas).&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;Advanced gotchas (short list)&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reentrancy / ordering: race conditions cause different behavior under MEV/front-running.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Constructor/initializer mismatch in proxies: calling implementation methods before initialization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Non-standard ERC20s: tokens that don’t return bool on transfer — use wrappers like OpenZeppelin SafeERC20.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;State shadowing / wrong storage slot: particularly when using low-level assembly or incorrect inheritance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chain reorganizations: rare, but can cause txs to reappear or reorder relative to observed state.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;One-page troubleshooting flow (copyable)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Get receipt (eth_getTransactionReceipt). If status == 1, the tx succeeded — check downstream logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If status == 0 -&amp;gt; run provider.call()/eth_call with same fields to capture revert reason.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;eth_estimateGas vs gas limit → OOG? Increase gas if estimate &amp;gt; gas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check eth_getBalance(from), allowance, and owner/paused flags.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fork locally (anvil/hardhat) at a nearby block and replay transaction. Mutate state if needed and rerun.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use debug_traceTransaction to see which internal call/opcode failed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix source (approve, set owner, update oracle, correct ABI/address, bump gas) and re-test on fork before sending live.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;When you see “TX failed”, don’t panic. Capture the tx fields, fetch the receipt, simulate the call to get the revert reason, reproduce on a local fork, and use traces to find the failing unit. Most failures are one of: permission, missing approval, out-of-gas, wrong ABI/address, or state-dependent external data.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;How helpful was this article ? &lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Follow me on X : @mykereckon&lt;br&gt;
 Connect with me on Zora : &lt;a href="https://zora.co/invite/myke_027" rel="noopener noreferrer"&gt;https://zora.co/invite/myke_027&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
