Secure Your ERC20 Tokens: Best Practices for Approval and transferFrom
When I first stepped out of the corporate trading floor, I was excited to translate the logic I’d learned about risk and diversification into something people could use to protect their own savings. One of the first lessons I found myself repeating to students was: security in the blockchain is not a feature of the technology but a discipline of the developer. That idea rings especially true when we look at the ERC‑20 token standard, the workhorse of DeFi that lets anyone create a fungible token. The two most common functions in that standard—approve and transferFrom—sound innocuous, but they are the hinges that can unlock a vault for an attacker if they’re not handled with care.
Let’s zoom out and remember that tokens are like money in a digital wallet. When you give someone permission to spend your money, you’re trusting them not to misuse it. In a conventional bank, you would ask a trusted intermediary to hold that permission for you. In Ethereum, that intermediary is a smart contract. And the smart contract is only as trustworthy as the code you trust it to run. That is the root of the risk.
The Anatomy of approve and transferFrom
The approve function is simple: you tell a spender contract how many of your tokens it can pull from your account. The transferFrom function is what the spender contract calls to move those tokens. Think of it as writing a check that only the spender can cash, up to a certain amount.
The logic in most token contracts looks like this:
approve(spender, amount)
transferFrom(from, to, amount)
The approval is stored as a mapping:
mapping(address => mapping(address => uint256)) allowances;
Where the outer key is the owner, the inner key is the spender, and the value is the approved allowance.
When a spender calls transferFrom, the contract subtracts the amount from the allowance. If the allowance is zero, the call fails. At first glance, that seems safe. But a few design choices and historical patterns have exposed many tokens to abuse.
Why the Pair is Tricky
The “Race Condition”
A spender can call transferFrom twice in quick succession before the allowance updates are propagated. If the contract uses allowance - amount without checking for underflow, the second call may still succeed because the allowance was not yet reduced. In many implementations, a reentrancy guard or a proper decrement prevents this, but older contracts did not.
The “Change of Mind” Problem
Imagine you approve a new DEX to spend 10 000 tokens, but you realize later you only want to give it 5 000. You call approve(spender, 5000). In the classic ERC‑20 pattern, the allowance is set to 5 000, but the spender can still use the old 10 000 allowance if it had already started a transfer. If the spender had an active transaction, the old allowance is still in effect until the new one takes place. This race can be exploited if the spender is malicious.
A common mitigation is the double-approve pattern: first set the allowance to 0, then set it to the new value. Some contracts even enforce that a non‑zero allowance can only be set if the current allowance is 0.
The “Zero Approval” Flaw
Because many wallets and libraries assume that a zero allowance is harmless, a token contract that does not explicitly forbid approve(spender, 0) can be tricked into giving the spender permission to spend all of the owner’s balance. For example, a malicious contract could first call approve(spender, 0) to reset the allowance, then immediately call transferFrom to siphon tokens. Some libraries now check that the allowance is not being set to zero unless the current allowance is also zero.
Real‑World Examples
The infamous 2017 “DAO” hack was a result of reentrancy in a transfer function, but it set the stage for thinking about token approvals more broadly.
In 2020, a number of stablecoins and governance tokens were targeted because their contracts used the unsafe allowance pattern. Attackers exploited the double‑approve issue to drain funds from wallets that had previously approved the attackers’ contracts.
A more recent incident involved a rug‑pull where a malicious contract called transferFrom repeatedly on an LP pool token, draining liquidity before the owners could react.
Each of these attacks underscores that the security of approve and transferFrom is not just a matter of code but also of user behaviour and wallet design.
Best Practices for Token Owners
-
Use Safe Approvals
Always set the allowance to 0 before setting a new value. Many wallets now provide a “reset approval” button. If yours doesn’t, remember to do it yourself. -
Limit the Amount
Never approve more than you need. If you only need to interact with a contract once, approve the exact amount required. This limits the exposure if the contract misbehaves. -
Revoke Unused Approvals
After you’re done, go to the contract’s interface and set the allowance to 0 again. Some tokens do not expose a revoke function, so you may need to interact directly with the contract. -
Use Token Holders that Support EIP‑2612
This “permit” extension allows approvals via off‑chain signatures, so you never need to broadcast an on‑chain transaction that changes an allowance. It reduces the attack surface. -
Beware of “Infinite Approvals”
Some DeFi protocols ask for infinite allowances for convenience. That is risky. If the protocol is not audited, you could lose everything if the contract is compromised. -
Check the Contract’s Source
Use tools like Etherscan to verify that the token’s code matches the deployed bytecode. An unverified contract can hide malicious logic.
Best Practices for Contract Developers
-
Implement the Double-Approve Safeguard
Require that a new non‑zero allowance can only be set if the current allowance is 0. The code pattern is:function approve(address spender, uint256 amount) public returns (bool) { require(amount == 0 || allowance[msg.sender][spender] == 0, "Non-zero to non-zero not allowed"); allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; }This eliminates the race condition and the change‑of‑mind problem.
-
Use SafeMath for Decrements
Always guard against underflows when subtracting the allowance. Prefer OpenZeppelin’sSafeERC20wrappers. -
Add Reentrancy Guard
Even thoughtransferFromtypically only manipulates balances, a malicious spender could trigger further calls in the same transaction. A simplenonReentrantmodifier protects against that. -
Avoid “Burner” Approvals
Some contracts allow burning of allowances via adecreaseAllowancefunction. If not needed, skip it. -
Consider Permit (EIP‑2612)
If you can add it, thepermitfunction eliminates the need for on‑chain approval approvals, reducing gas costs and attack surface. -
Testing and Auditing
Write unit tests for all edge cases: zero approvals, high approvals, rapid successivetransferFromcalls. If possible, get a professional audit focused on approval logic.
Tooling and Community Resources
- OpenZeppelin – The library’s
SafeERC20andERC20Permitprovide battle‑tested implementations. - MythX, Slither, Securify – Static analysis tools that flag dangerous approval patterns.
- Etherscan “Contract Source Code” – Use the “Verify and Publish” feature to check if a token matches its source.
- GitHub Discussions – Many projects have public issue trackers where users report approval bugs.
The Human Element
Even with all the best practices, users sometimes overlook approvals because they’re focused on the upside of a new protocol. I remember a friend who, after a few days of excitement, had set a 100 000 token allowance for a new yield farm. The next day, the farm’s contract was updated to a malicious version that drained all of the funds. Had she set a smaller allowance or revoked it after she had finished farming, she could have mitigated the loss.
Security in DeFi is as much about behaviour as it is about code. It’s the same principle that holds for any investment: diversify, keep a buffer, and don’t leave your capital in a single, unverified pool.
Staying Informed
The DeFi space evolves fast. New standards emerge, bugs are discovered, and attack vectors shift. Here are a few ways to keep your eyes open:
- Follow the core developers on Twitter or Discord – They often announce changes to the ERC‑20 standard.
- Subscribe to newsletters from audit firms – They publish findings that can affect token behaviour.
- Engage with community forums – Reddit, StackExchange, and Telegram groups often surface real‑time problems.
- Run your own audits – Even simple checks, like using Slither on a token contract, can reveal hidden pitfalls.
Takeaway
In a world where a single line of code can hold or drain thousands of dollars, the simple act of approving a token allowance must be treated with the same diligence as checking a bank statement. The safest approach is a disciplined routine: always set approvals to zero before changing them, keep allowances tight, revoke unused approvals, and choose contracts that follow the double‑approve rule. For developers, the code must enforce these rules, guard against reentrancy, and ideally implement permit to avoid on‑chain approvals altogether.
By making these habits part of our workflow, we give ourselves a stronger buffer against the unpredictable nature of the market and the occasional rogue contract. It’s less about timing, more about time – about building a system where we can trust that our digital vaults will stay locked until we choose otherwise.
Lucas Tanaka
Lucas is a data-driven DeFi analyst focused on algorithmic trading and smart contract automation. His background in quantitative finance helps him bridge complex crypto mechanics with practical insights for builders, investors, and enthusiasts alike.
Random Posts
A Deep Dive Into Smart Contract Mechanics for DeFi Applications
Explore how smart contracts power DeFi, from liquidity pools to governance. Learn the core primitives, mechanics, and how delegated systems shape protocol evolution.
1 month ago
Guarding Against Logic Bypass In Decentralized Finance
Discover how logic bypass lets attackers hijack DeFi protocols by exploiting state, time, and call order gaps. Learn practical patterns, tests, and audit steps to protect privileged functions and secure your smart contracts.
5 months ago
Smart Contract Security and Risk Hedging Designing DeFi Insurance Layers
Secure your DeFi protocol by understanding smart contract risks, applying best practice engineering, and adding layered insurance like impermanent loss protection to safeguard users and liquidity providers.
3 months ago
Beyond Basics Advanced DeFi Protocol Terms and the Role of Rehypothecation
Explore advanced DeFi terms and how rehypothecation can boost efficiency while adding risk to the ecosystem.
4 months ago
DeFi Core Mechanics Yield Engineering Inflationary Yield Analysis Revealed
Explore how DeFi's core primitives, smart contracts, liquidity pools, governance, rewards, and oracles, create yield and how that compares to claimed inflationary gains.
4 months ago
Latest Posts
Foundations Of DeFi Core Primitives And Governance Models
Smart contracts are DeFi’s nervous system: deterministic, immutable, transparent. Governance models let protocols evolve autonomously without central authority.
1 day ago
Deep Dive Into L2 Scaling For DeFi And The Cost Of ZK Rollup Proof Generation
Learn how Layer-2, especially ZK rollups, boosts DeFi with faster, cheaper transactions and uncovering the real cost of generating zk proofs.
1 day ago
Modeling Interest Rates in Decentralized Finance
Discover how DeFi protocols set dynamic interest rates using supply-demand curves, optimize yields, and shield against liquidations, essential insights for developers and liquidity providers.
1 day ago