All posts
EducationApril 19, 20268 min read

7 Red Flags in Smart Contracts Every Investor Should Know

Before you send funds to any token or DeFi protocol, check for these 7 code patterns. A practical red-flag checklist with Solidity examples from real scams.

7 Red Flags in Smart Contracts Every Investor Should Know

A good smart contract is boring. It does one thing. The owner can't unilaterally take your money. The math checks out. Most of the tokens and DeFi protocols you'll encounter in the wild are not boring — they're loaded with "features" that exist purely for the benefit of whoever deployed them.

Here are seven red flags that should stop you from apeing in, with real Solidity examples of what each one looks like in code.

1. Unrestricted mint function

function mint(address to, uint256 amount) external onlyOwner {
    _mint(to, amount);
}

The owner can create new tokens at any time, in any amount, and send them anywhere. This dilutes every existing holder and gives the owner effectively unlimited selling pressure. Legitimate governance tokens sometimes have capped minting through a DAO, but a single-address onlyOwner mint is almost always dangerous.

What to look for: any _mint call that isn't wrapped in a permanent supply cap or behind a time-locked governance process.

2. Modifiable fees with no cap

uint256 public buyTax;
uint256 public sellTax;

function setTaxes(uint256 _buy, uint256 _sell) external onlyOwner {
    buyTax = _buy;
    sellTax = _sell;
}

Note what's missing: a require that caps the tax. The owner can set fees to 100% at any moment, instantly draining every future sell into their wallet. Even if the current taxes are reasonable, the absence of a cap means they don't need to stay that way.

What to look for: setTax, setFee, updateFees — any setter function without a hard-coded upper bound like require(_sell <= 10, "fee too high").

3. Centralized pause function

bool public paused;

modifier whenNotPaused() {
    require(!paused, "paused");
    _;
}

function pause() external onlyOwner {
    paused = true;
}

The owner can freeze all transfers at any time. This is sometimes legitimate for emergency stops, but in a token contract it's usually a rug vector — the owner waits for sellers to accumulate, then pauses while they dump liquidity.

What to look for: pause, Pausable, stopTrading, or any modifier gated by a single address that affects transfer logic.

4. Privileged fund withdrawal

function rescueTokens(address token, uint256 amount) external onlyOwner {
    IERC20(token).transfer(owner, amount);
}

function rescueETH() external onlyOwner {
    payable(owner).transfer(address(this).balance);
}

"Rescue" and "recover" functions are red flags even when they look innocent. If the contract holds user funds (staking, vaults, farms), these let the owner withdraw everything. Names to watch for: rescue, recover, withdraw, sweep, drain, claimTokens, emergencyWithdraw (when the emergencyWithdraw is for the owner, not users).

What to look for: any function that transfers tokens or ETH out of the contract to the owner or a privileged address.

5. Upgradable proxy without time-lock

function upgradeTo(address newImplementation) external onlyOwner {
    _upgradeTo(newImplementation);
}

Proxy contracts aren't inherently bad — many legitimate protocols use them for upgradability. The problem is when the upgrade happens instantly at the owner's discretion. An owner can upgrade the token contract to a malicious implementation mid-trade, effectively replacing every rule you relied on.

What to look for: upgradeTo, setImplementation, UUPSUpgradeable, or any function that modifies the code the contract runs. A safe version is gated behind a Timelock contract (usually 24-72 hours) so holders can exit before upgrades take effect.

6. Blacklist / whitelist functions

mapping(address => bool) public blacklisted;

function blacklist(address user) external onlyOwner {
    blacklisted[user] = true;
}

function _transfer(address from, address to, uint256 amount) internal {
    require(!blacklisted[from] && !blacklisted[to], "blacklisted");
    super._transfer(from, to, amount);
}

The owner can freeze any holder's tokens. Variants include isBot, isExcluded, sanctioned, and anti-MEV lists. Even if it's framed as anti-sniper protection, the owner retains unilateral power to block any address — including yours — from trading.

What to look for: any mapping indexed by address that gates _transfer logic, combined with an onlyOwner setter.

7. tx.origin authentication

modifier onlyOwner() {
    require(tx.origin == owner, "not owner");
    _;
}

This one is a code quality red flag rather than a scam flag, but it indicates the developer doesn't understand Solidity security basics. tx.origin is the original transaction initiator, not the direct caller — which makes this modifier vulnerable to phishing attacks via malicious intermediary contracts. If the developer made this mistake, they've likely made others.

What to look for: any use of tx.origin in access control. The standard is msg.sender.

The meta red flag

Beyond specific patterns, there's a meta pattern: contract complexity disproportionate to stated purpose. A simple memecoin with 500 lines of custom logic — dynamic fees, blacklists, max tx limits, cooldowns, anti-sniper modifiers, reflection rewards — is hiding something. Legitimate tokens are usually close to a standard OpenZeppelin ERC20 with minimal deviation.

Ask: does every line of this contract need to exist for the product to work? If the answer is no, ask who benefits from the extra code. Usually it's the deployer, at your expense.

Running the checklist in practice

You don't have to read every contract line-by-line. An AI auditor can do the pattern matching for you:

Or do it manually: open the contract on Etherscan, Ctrl+F for the keywords listed above, and read any matches carefully. If anything looks like it gives the owner post-deployment control over your tokens, you found a red flag.


Analyzing a contract right now? Run a free Unrugify scan and see the risk breakdown in under 30 seconds.

Ready to analyze a contract?

Free preview in 30 seconds. Full audit for $1.