DEFI RISK AND SMART CONTRACT SECURITY

Secure Your ERC20 Tokens: Best Practices for Approval and transferFrom

8 min read
#DeFi Security #Gas Optimization #TransferFrom #ERC20 Security #Token Approval
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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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

  1. 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.

  2. Use SafeMath for Decrements
    Always guard against underflows when subtracting the allowance. Prefer OpenZeppelin’s SafeERC20 wrappers.

  3. Add Reentrancy Guard
    Even though transferFrom typically only manipulates balances, a malicious spender could trigger further calls in the same transaction. A simple nonReentrant modifier protects against that.

  4. Avoid “Burner” Approvals
    Some contracts allow burning of allowances via a decreaseAllowance function. If not needed, skip it.

  5. Consider Permit (EIP‑2612)
    If you can add it, the permit function eliminates the need for on‑chain approval approvals, reducing gas costs and attack surface.

  6. Testing and Auditing
    Write unit tests for all edge cases: zero approvals, high approvals, rapid successive transferFrom calls. If possible, get a professional audit focused on approval logic.


Tooling and Community Resources

  • OpenZeppelin – The library’s SafeERC20 and ERC20Permit provide 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
Written by

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.

Contents