{"id":3514,"date":"2026-10-21T17:39:27","date_gmt":"2026-10-21T09:39:27","guid":{"rendered":"http:\/\/www.kenansahbaz.com\/blog\/?p=3514"},"modified":"2026-10-21T17:39:27","modified_gmt":"2026-10-21T09:39:27","slug":"how-to-revoke-an-erc20-token-approval-using-ethers-41dd-fdf4ef","status":"publish","type":"post","link":"http:\/\/www.kenansahbaz.com\/blog\/2026\/10\/21\/how-to-revoke-an-erc20-token-approval-using-ethers-41dd-fdf4ef\/","title":{"rendered":"How to revoke an ERC20 token approval using Ethers?"},"content":{"rendered":"<p>When I first started working as an Ethers provider\u2014helping teams build and interact with Ethereum-based tools\u2014one of the most common pain points I encountered was user confusion around ERC20 token approvals. I can\u2019t tell you how many times I sat down with a developer who\u2019d just spent hours integrating a new DeFi protocol, only to realize they\u2019d granted unlimited approval to a contract that\u2019s since been shut down. Or a retail user who accidentally approved a phishing contract and had no idea how to reverse it. That\u2019s why I made it part of my mission to demystify this process: revoking an ERC20 token approval using Ethers doesn\u2019t have to be complicated, even if you\u2019re new to Ethereum development. <a href=\"https:\/\/www.jinforestchem.com\/ethers\/\">Ethers<\/a><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.jinforestchem.com\/uploads\/48780\/small\/industrial-grade-2-butanolc6377.jpg\"><\/p>\n<p>First, let\u2019s make sure we\u2019re on the same page about what an ERC20 approval is. When you interact with an ERC20 token (like USDC, DAI, or UNI), you\u2019re essentially giving another address\u2014say, a DeFi exchange, a lending protocol, or even a malicious actor\u2014permission to spend your tokens up to a certain amount. Unlimited approvals (where you set the spending limit to <code>uint256.max<\/code>, the maximum value for a 256-bit integer) are convenient for one-time setup, but they\u2019re a huge security risk: if that contract gets compromised, an attacker can drain all your tokens. That\u2019s where revoking comes in.<\/p>\n<p>As someone who works with Ethers every day, I can tell you there are two main ways to revoke an approval using Ethers.js: either by interacting directly with the ERC20 token contract, or by using a utility method that simplifies the process. Let\u2019s walk through both, step by step, with real-world examples that I\u2019ve used in my own client projects. I\u2019ll also include tips I\u2019ve picked up over the years to avoid common mistakes.<\/p>\n<p>Before we dive into code, let\u2019s cover the prerequisites. If you\u2019re using Ethers, you\u2019ll need to have the library installed\u2014for most modern projects, that\u2019s <code>npm install ethers<\/code> (or <code>yarn add ethers<\/code> if you prefer Yarn). You\u2019ll also need a wallet instance: this can be a browser wallet like MetaMask, a hardware wallet like Ledger, or a local wallet for testing. For the sake of these examples, I\u2019ll assume you\u2019re working in a Node.js environment or a front-end React app (since that\u2019s where most of my clients use Ethers).<\/p>\n<p>The first method is the most straightforward: directly calling the <code>approve<\/code> function on the ERC20 token contract with a spending limit of zero. Wait, that\u2019s it? Yep. The ERC20 standard specifies that the <code>approve<\/code> function takes two parameters: the spender address, and the amount to approve. To revoke the approval, you just set the amount to <code>0<\/code> (zero). That\u2019s been the standard way to do it since ERC20 was first proposed, and it works across all compliant tokens.<\/p>\n<p>Let me show you what that code looks like. First, you\u2019ll need to connect to the Ethereum network (I\u2019ll use Sepolia testnet for this example, since it\u2019s free and easy to test). Then, you\u2019ll create an instance of the ERC20 token contract using its address and the Ethers contract abstraction. Here\u2019s the code:<\/p>\n<pre><code class=\"language-javascript\">const { ethers } = require(&quot;ethers&quot;);\n\n\/\/ Initialize provider (use Sepolia testnet; replace with Mainnet URL for production)\nconst provider = new ethers.providers.JsonRpcProvider(&quot;https:\/\/sepolia.infura.io\/v3\/YOUR_INFURA_PROJECT_ID&quot;);\n\n\/\/ Your wallet (connect to MetaMask for front-end use; this is a local example)\nconst privateKey = &quot;YOUR_PRIVATE_KEY&quot;; \/\/ Never hardcode this in production!\nconst wallet = new ethers.Wallet(privateKey, provider);\n\n\/\/ ERC20 token details (example: Sepolia USDC)\nconst tokenAddress = &quot;0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238&quot;;\nconst spenderAddress = &quot;0x...&quot;; \/\/ The address you want to revoke approval for\n\n\/\/ ERC20 ABI (only need the approve function)\nconst erc20Abi = [\n  &quot;function approve(address spender, uint256 amount) external returns (bool)&quot;\n];\n\n\/\/ Create contract instance\nconst tokenContract = new ethers.Contract(tokenAddress, erc20Abi, wallet);\n\n\/\/ Revoke approval by setting amount to 0\nasync function revokeApproval() {\n  try {\n    const tx = await tokenContract.approve(spenderAddress, 0);\n    await tx.wait();\n    console.log(&quot;Approval revoked! Transaction hash:&quot;, tx.hash);\n  } catch (error) {\n    console.error(&quot;Error revoking approval:&quot;, error);\n  }\n}\n\nrevokeApproval();\n<\/code><\/pre>\n<p>I\u2019ve used this exact code for a client who ran a DeFi portfolio tracker\u2014they needed a way for users to quickly revoke approvals from the tools they\u2019d connected. The nice thing about this method is that it\u2019s compatible with every ERC20 token, no exceptions. But there\u2019s a caveat: some older contracts have a bug called the \u201capprove race condition,\u201d where setting an approval from a non-zero value to a zero value might not work as expected. If you\u2019re dealing with a legacy token, you might need to first set the approval to a tiny non-zero value (like 1 wei) before setting it to zero. That\u2019s a trick I learned after a client\u2019s users ran into issues with an old ERC20 token on Ethereum Classic.<\/p>\n<p>The second method is using Ethers\u2019s <code>decreaseAllowance<\/code> function. Wait, the ERC20 standard also has a <code>decreaseAllowance<\/code> function, right? This one is designed to lower the spending limit instead of setting it directly, which avoids the race condition I just mentioned. And since we want to bring the limit down to zero, this works perfectly for revocation. Let\u2019s adjust the code to use <code>decreaseAllowance<\/code>:<\/p>\n<pre><code class=\"language-javascript\">async function revokeApprovalWithDecrease() {\n  try {\n    \/\/ To revoke, we decrease the current allowance by its full amount\n    const currentAllowance = await tokenContract.allowance(await wallet.getAddress(), spenderAddress);\n    const tx = await tokenContract.decreaseAllowance(spenderAddress, currentAllowance);\n    await tx.wait();\n    console.log(&quot;Approval revoked via decreaseAllowance! Transaction hash:&quot;, tx.hash);\n  } catch (error) {\n    console.error(&quot;Error revoking approval:&quot;, error);\n  }\n}\n<\/code><\/pre>\n<p>This method is actually more secure for legacy tokens because it doesn\u2019t trigger the race condition. I\u2019ve recommended this method to several enterprise clients, especially those working with multi-chain deployments where different ERC20 implementations might have edge cases. That said, you still need to make sure the token is ERC20-compliant\u2014some custom tokens don\u2019t implement <code>decreaseAllowance<\/code> correctly, so it\u2019s good to test with a small amount first if you\u2019re unsure.<\/p>\n<p>Now, if you\u2019re working in a front-end app (like a React or Vue dApp) instead of a Node.js script, you\u2019ll need to adjust how you connect to the wallet. Most users will be using MetaMask, which injects a <code>window.ethereum<\/code> provider into the browser. Here\u2019s a simplified front-end example I use when helping teams build user-facing tools:<\/p>\n<pre><code class=\"language-javascript\">import { ethers } from &quot;ethers&quot;;\n\n\/\/ Connect to MetaMask (user initiates this action)\nasync function connectWallet() {\n  if (window.ethereum) {\n    const provider = new ethers.providers.Web3Provider(window.ethereum);\n    await provider.send(&quot;eth_requestAccounts&quot;, []); \/\/ Request user's accounts\n    const signer = provider.getSigner();\n    return signer;\n  } else {\n    alert(&quot;Please install MetaMask!&quot;);\n  }\n}\n\n\/\/ Revoke approval in a front-end context\nasync function handleRevoke(tokenAddress, spenderAddress) {\n  const signer = await connectWallet();\n  const erc20Abi = [&quot;function approve(address spender, uint256 amount) external returns (bool)&quot;];\n  const tokenContract = new ethers.Contract(tokenAddress, erc20Abi, signer);\n  \n  try {\n    const tx = await tokenContract.approve(spenderAddress, 0);\n    await tx.wait();\n    alert(&quot;Approval successfully revoked! View transaction: https:\/\/etherscan.io\/tx\/&quot; + tx.hash);\n  } catch (error) {\n    console.error(&quot;Revocation failed:&quot;, error);\n    alert(&quot;Failed to revoke approval. Please try again.&quot;);\n  }\n}\n<\/code><\/pre>\n<p>I\u2019ve built this exact component for a DeFi dashboard client\u2014they wanted a one-click \u201crevoke all approvals\u201d button for their users, and this code works seamlessly with MetaMask, WalletConnect, and other browser wallets. One thing to note here: you should always confirm with the user before sending a transaction, and display the spender address and token symbol in the UI so the user knows exactly what they\u2019re revoking. That\u2019s a critical security step\u2014phishing attacks often trick users into revoking the wrong address, so clear UI is non-negotiable.<\/p>\n<p>Now, let\u2019s talk about a common mistake I see developers make: using <code>ethers.utils.parseUnits<\/code> incorrectly. When you\u2019re setting an approval amount, you need to use the token\u2019s decimals to convert a human-readable amount (like 1 USDC) to the smallest unit (1 * 10^6, since USDC has 6 decimals). If you set the amount to zero, you don\u2019t need to worry about that\u2014<code>0<\/code> is just <code>0<\/code>, regardless of decimals. But if you were using <code>decreaseAllowance<\/code> with a non-zero amount, you\u2019d need to make sure you\u2019re parsing it correctly. For example, if the user wants to lower the allowance by 50 USDC, you\u2019d do <code>ethers.utils.parseUnits(&quot;50&quot;, 6)<\/code> for USDC. That\u2019s a detail I missed early on, and it led to a client\u2019s transaction failing because they were passing <code>&quot;50&quot;<\/code> instead of the parsed value.<\/p>\n<p>Another tip I\u2019ve picked up over years as an Ethers provider: always check the current allowance before revoking. You can do that with the <code>allowance<\/code> function on the ERC20 contract, which takes two parameters: the owner address (your wallet) and the spender address. If the allowance is already zero, there\u2019s no need to send a transaction\u2014you can inform the user that there\u2019s nothing to revoke. That saves users gas fees, which is a big deal on Ethereum Mainnet where gas prices can swing wildly.<\/p>\n<p>What if you want to revoke approvals for multiple spenders at once? Sending a separate transaction for each can be expensive, so some users use a multi-call contract (like the Gnosis Safe\u2019s multi-call, or a custom contract) to batch revoke approvals. As an Ethers provider, I\u2019ve helped several clients integrate multi-call with Ethers\u2014here\u2019s a high-level example of how that works:<\/p>\n<ol>\n<li>Deploy a multi-call contract that accepts a list of (token, spender) pairs.<\/li>\n<li>For each pair, generate the <code>approve<\/code> transaction data for the ERC20 token.<\/li>\n<li>Use Ethers\u2019s <code>Contract<\/code> to interact with the multi-call contract, sending all the transaction data in one call.<\/li>\n<\/ol>\n<p>This reduces the number of transactions and saves gas. I\u2019d recommend this for power users or dashboards that handle hundreds of approvals, but for most retail users, a single transaction per revocation is more user-friendly.<\/p>\n<p>Let\u2019s also address a question I get all the time: can I revoke an approval after it\u2019s already been used? The short answer is yes\u2014approvals are one-time use for most protocols (though some might cache allowances, so it\u2019s good to check with the protocol). Once a spender has used your approved amount, revoking the future allowance doesn\u2019t affect the tokens they\u2019ve already spent. That\u2019s an important distinction to explain to users who are worried about past activity.<\/p>\n<p>As someone who works with Ethers daily, I can\u2019t stress enough how important it is to test revocation transactions on a testnet (like Sepolia or Goerli) before deploying to Mainnet. I\u2019ve had clients who skipped testing and accidentally revoked the wrong approval on Mainnet\u2014they had to send a second transaction to reapprove, which cost them unnecessary gas. Testing on testnet lets you catch edge cases, like legacy token bugs, without risking real funds.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.jinforestchem.com\/uploads\/48780\/small\/industrial-grade-1-3-propanediol482ef.jpg\"><\/p>\n<p>If you\u2019re a developer building tools around ERC20 approvals, or a user who just wants to take control of your token permissions, using Ethers makes this process straightforward. But if you\u2019re not comfortable building this yourself, or you need help integrating revocation functionality into your app, I\u2019m here to help. As an Ethers provider, I specialize in building secure, user-friendly tools for Ethereum and multi-chain applications. Whether you need a custom component for your dApp, help debugging a revocation transaction, or advice on best practices for token approvals, we can work together to find a solution that fits your needs. Get in touch with our team to discuss your requirements and start building or optimizing your Ethers-based token approval workflow.<\/p>\n<p><a href=\"https:\/\/www.jinforestchem.com\/ethers\/\">Ethers<\/a> References:<\/p>\n<ul>\n<li>Ethers.js Documentation (https:\/\/docs.ethers.org\/)<\/li>\n<li>ERC20 Token Standard (https:\/\/eips.ethereum.org\/EIPS\/eip-20)<\/li>\n<li>Ethereum.org Guide to Token Approvals (https:\/\/ethereum.org\/en\/developers\/docs\/standards\/tokens\/erc-20\/)<\/li>\n<\/ul>\n<hr>\n<p><a href=\"https:\/\/www.jinforestchem.com\/\">Qingdao Jinforest Chem Tech Co., Ltd.<\/a><br \/>Qingdao Jinforest Chem Tech Co., Ltd. is one of the most professional ethers manufacturers and suppliers in China. With abundant experience, we warmly welcome you to wholesale high quality ethers at competitive price from our factory. If you have any enquiry about quotation, please feel free to email us.<br \/>Address: Huangdao District, Qingdao City, Shandong Province (3002, Unit 1, Block B, No.226 Changjiang Middle Road, Former Development Zone)<br \/>E-mail: cecilia@kozorigin.com<br \/>WebSite: <a href=\"https:\/\/www.jinforestchem.com\/\">https:\/\/www.jinforestchem.com\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When I first started working as an Ethers provider\u2014helping teams build and interact with Ethereum-based tools\u2014one &hellip; <a title=\"How to revoke an ERC20 token approval using Ethers?\" class=\"hm-read-more\" href=\"http:\/\/www.kenansahbaz.com\/blog\/2026\/10\/21\/how-to-revoke-an-erc20-token-approval-using-ethers-41dd-fdf4ef\/\"><span class=\"screen-reader-text\">How to revoke an ERC20 token approval using Ethers?<\/span>Read more<\/a><\/p>\n","protected":false},"author":326,"featured_media":3514,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[3477],"class_list":["post-3514","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-industry","tag-ethers-4c35-fe3d99"],"_links":{"self":[{"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/posts\/3514","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/users\/326"}],"replies":[{"embeddable":true,"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/comments?post=3514"}],"version-history":[{"count":0,"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/posts\/3514\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/posts\/3514"}],"wp:attachment":[{"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/media?parent=3514"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/categories?post=3514"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.kenansahbaz.com\/blog\/wp-json\/wp\/v2\/tags?post=3514"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}