Can this token rug you? An owner-power checklist before you buy or deploy
By ContractScan Team ·
A "rug pull" is when the people who control a token or protocol take the money and leave. Sometimes that happens off-chain (the team dumps their allocation or drains a liquidity pool they control). But very often the ability to rug is written directly into the token contract, in plain sight, for anyone who reads the code.
This guide shows the patterns to look for. You do not need to be a Solidity expert: most of these are recognisable by function names and modifiers. When you look at a token on a block explorer, open the Contract tab. If the source is not verified, that is already a red flag: you cannot know what the code does.
Start with one question: what can the owner do?
Search the source for onlyOwner, onlyRole, onlyAdmin or any custom modifier starting with only. Every function behind one of those is a power that a single key (or a small group) holds over every holder. Then go through the list below.
1. Owner can mint new tokens
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
If the owner can create tokens at will, they can mint a huge amount to themselves and sell into the liquidity pool, crashing the price. A legitimate project might need minting (for example, a stablecoin or a reward token with an emission schedule), but then you want to see a hard cap, a schedule enforced in code, or a timelock and multisig controlling it.
2. Blacklists and "anti-bot" lists
mapping(address => bool) public isBlacklisted;
function setBlacklist(address account, bool value) external onlyOwner {
isBlacklisted[account] = value;
}
function _update(address from, address to, uint256 amount) internal override {
require(!isBlacklisted[from], "blocked");
super._update(from, to, amount);
}
A blacklist lets the owner freeze any wallet. Regulated stablecoins have this for legal reasons, and they say so openly. In a meme or launch token it is a classic honeypot mechanism: you can buy, but the owner can block you from selling. Watch for innocent-sounding names: bots, isSniper, _excluded, blocked.
3. Fees the owner can change
uint256 public sellFee = 2;
function setSellFee(uint256 newFee) external onlyOwner {
sellFee = newFee; // no upper bound
}
Transfer taxes are common, but if the owner can raise the sell fee to 99% or 100%, they can make selling pointless while buys still work. Look for a require(newFee <= SOME_MAX) line. If there is no cap, assume the fee can be anything.
4. Pausing and trading switches
bool public tradingEnabled;
function enableTrading() external onlyOwner { tradingEnabled = true; }
function _update(address from, address to, uint256 amount) internal override {
require(tradingEnabled || from == owner(), "trading not open");
super._update(from, to, amount);
}
A trading switch that can only be turned on once is a common launch mechanism. The danger is a switch that can be turned back off, a general pause() on transfers, or exemptions that let only the owner keep trading. Read the condition carefully: who is exempt?
5. Max transaction and max wallet limits
Functions like setMaxTxAmount or setMaxWallet are sold as anti-whale protection. If the owner can lower the limit to a tiny number (or zero) and exempt themselves, holders cannot sell. Check for minimum bounds in the setter.
6. Withdraw, rescue and sweep functions
function rescueTokens(address token, uint256 amount) external onlyOwner {
IERC20(token).transfer(owner(), amount);
}
A function that lets the owner pull tokens or ETH out of the contract is harmless in a plain token, but dangerous in anything that holds user deposits: vaults, staking contracts, presales. If users send funds to the contract, check whether the owner can take them.
7. Upgradeable proxies
If the token is behind a proxy (look for upgradeTo, UUPSUpgradeable, TransparentUpgradeableProxy, or a block explorer label like "Proxy"), the code you are reading can be replaced. Upgradeability has legitimate uses, but it means you are trusting whoever controls the upgrade, not the code. A timelock (so changes are announced in advance) and a multisig make this much safer.
8. Hidden balance edits
Be very suspicious of any privileged function that writes directly to the balance mapping:
function syncBalance(address account, uint256 value) external onlyOwner {
_balances[account] = value; // can set anyone's balance to zero
}
Names can be misleading ("sync", "airdrop", "reflect", "update"). What matters is that the owner can rewrite balances.
"Ownership renounced" is not the end of the story
Renouncing ownership (setting the owner to the zero address) removes onlyOwner powers, which is good. But check that:
- there are no other roles (
AccessControlroles, a separateoperator, a "marketing wallet" with special rights) that still hold power; - the contract is not a proxy whose admin is a different address;
- there is no function that lets someone reclaim ownership.
A quick checklist
- Is the source verified on the explorer?
- List every privileged function. What does each one let the owner do to your tokens?
- Are fees and limits capped in code?
- Can transfers be paused, or can specific addresses be blocked?
- Can new tokens be minted? Is there a cap?
- Is it upgradeable? Who controls upgrades, and is there a timelock?
- Who is the owner: a single wallet, a multisig, a timelock, or nobody?
None of these patterns prove a project is a scam. Plenty of honest projects have a pause function or an owner-controlled fee. But each one is a power you are trusting someone not to abuse. Know what you are trusting before you buy, and especially before you deploy a token that other people will hold.
Check your own contract
Paste a single-file Solidity contract into ContractScan for a free static-analysis check with plain-language explanations.
ContractScan is automated static analysis, not an audit. For contracts holding real value, get a professional audit.