Skip to content
All posts

ERC-20 Token Security Risks: Owner Mint, Blacklist, and Fee-on-Transfer

6 min readERC-20 · smart contract security · token audit · DeFi

The ERC-20 standard defines what a token does — transfer, approve, balanceOf — but it says nothing about who controls it or what privileges the deployer kept. That gap is where most ERC-20 token security problems live. Two contracts can expose an identical interface while one is a fair, immutable token and the other lets a single key mint unlimited supply, freeze your wallet, or quietly tax every trade.

This post walks through the common risky patterns in ERC-20 contracts, why each one matters to holders and integrators, and how you can detect them directly on-chain before you commit capital or write integration code.

Why interface compliance is not safety

When you read a token's verified source on a block explorer, the function names look reassuringly standard. The danger is almost never in the standard functions — it is in the extra functions and the modifiers wrapped around the standard ones. A transfer that internally checks a _blacklist[from] mapping still satisfies the ERC-20 ABI perfectly. So does a mint reachable only by onlyOwner.

Security, then, comes down to answering questions the interface can't:

The risky patterns, one by one

Owner mint (mintable supply)

A mint function gated by an owner or minter role means the circulating supply is not fixed. In legitimate projects this powers staking rewards or controlled emissions. In malicious ones it is the classic rug: the deployer mints a massive balance and dumps it, diluting every holder to near zero. For integrators, an inflatable supply breaks any assumption baked into price oracles, collateral ratios, or reward math.

Blacklist and allowlist

A blacklist lets a privileged address mark wallets that can no longer transfer. Allowlists invert it: only approved addresses may transact. Both hand a central party the power to freeze funds. Some compliant stablecoins use blacklists deliberately and disclose it — that is a known trade-off. The problem is an undisclosed blacklist on a supposedly permissionless token, which can trap buyers (a core honeypot mechanic) while letting insiders sell.

Fee-on-transfer / tax mechanics

Fee-on-transfer tokens deduct a percentage on every move, routing it to a treasury, liquidity, or reflection pool. The receiver gets less than the sent amount. This is one of the most common sources of integration bugs: naive contracts assume amountReceived == amountSent and revert or mis-account when they don't. Worse, the fee rate is often an owner-settable variable that can be raised — sometimes to 100%, which is itself a honeypot.

Pausable transfers

A pausable token exposes a switch that halts all transfers. It is a reasonable emergency brake during an active exploit, but it also means an owner can freeze the entire market at will. If you are providing liquidity or holding through a pause, your exit can vanish without warning.

Privileged ownership concentration

Many of the risks above collapse into one meta-risk: who holds the keys. A single externally-owned account with onlyOwner over mint, fees, pause, and blacklist is a single point of failure — one compromised or malicious key, and every protection is moot. Ownership behind a timelock or a multisig is meaningfully safer than ownership behind one private key, and renounced ownership removes the lever entirely (at the cost of upgradeability).

Upgradeable proxies

A proxy contract delegates its logic to an implementation address that an admin can change. Today's audited bytecode can be replaced tomorrow with anything. Upgradeability is legitimate and widespread, but it means the code you reviewed is not necessarily the code that will run on your next transaction. Always check who controls the upgrade and whether it is timelocked.

Risk pattern summary

Pattern Risk to holders Risk to integrators On-chain signal
Owner mint Supply dilution, rug Broken supply/oracle assumptions mint/minter role reachable
Blacklist / allowlist Funds frozen, honeypot Transfers revert for some users _blacklist checks in transfer path
Fee-on-transfer Value lost per move received != sent accounting bugs Balance delta < amount sent
Pausable Market frozen, no exit Calls revert when paused whenNotPaused / paused()
Privileged owner One key controls all Unpredictable rule changes EOA owner, no timelock/multisig
Upgradeable proxy Logic swapped post-audit Reviewed code can change Proxy admin + implementation slot

How to detect these on-chain

You don't have to take a project's word for it. Two complementary approaches cover most cases.

1. Read the verified source. If the contract is verified on a block explorer (e.g., Etherscan), the Solidity is public. Search the transfer path for onlyOwner, whenNotPaused, fee variables, and blacklist mappings. Static analysis tools like Slither flag privileged functions and dangerous patterns automatically; Mythril and Echidna go further with symbolic execution and fuzzing.

2. Probe the live contract. Source can be misleading or absent, so confirm against the deployed bytecode. Low-level eth_call selector probes ask the contract directly whether a given function selector exists and how it responds — for example, checking for an owner(), a mint(...), a paused(), or a fee getter. A practical fee check is to simulate a transfer and compare the recipient's balance delta against the amount sent: if it's smaller, there's a tax.

A crucial honesty caveat: heuristic selector probing can produce false positives and false negatives. A selector collision can suggest a function that isn't really there, and obfuscated or proxied logic can hide one that is. Treat automated findings as strong signals to investigate, not as proof — and treat any check that couldn't run as a coverage gap, not a clean pass.

Where automated auditing fits

Manually doing all of this for every token is impractical, which is exactly why automated scanning exists. NANOTESTING runs an ERC-20 audit module that combines static analysis (Slither, Mythril, Echidna) with on-chain eth_call selector probes against verified source, surfacing owner-mint, blacklist/allowlist, fee-on-transfer, pausable transfers, and ownership concentration — alongside honeypot, LP-lock, and multisig checks — and grades the result A–F across 7 EVM chains. In keeping with the caveats above, a check that can't run is reported as a skip or coverage gap, never as a silent pass.

Takeaways

ERC-20 token security is about privilege, not interface. The standard tells you a token can transfer; it never tells you who can stop it, inflate it, tax it, or replace it. Before you buy, provide liquidity, or integrate a token contract:

Verify on-chain, distrust unverified source, and remember that an honest "we couldn't check this" is far more useful than a confident clean pass that wasn't earned.

Related posts