Is My Token Safe? A Pre-Launch Security Checklist
Launching an ERC-20 or similar token without a structured security review is one of the most common ways projects lose funds or credibility within hours of going live. Exploits, rug-pull vectors, and accidental bugs are rarely exotic — they are almost always one of a small set of well-known patterns that a systematic checklist would have caught. This guide walks through each of those patterns so you can answer the question is my token safe before your users have to ask it for you.
1. Ownership and Access Control
The owner or equivalent privileged address is the most powerful attack surface in most token contracts. Ask the following questions about every privileged function in your contract.
Who can call sensitive functions?
- Is
onlyOwner(or a role-based equivalent) applied to every function that moves funds, mints tokens, pauses transfers, or changes fee parameters? - Are there any functions that should be restricted but are currently
publicwith no modifier? - Does ownership use a two-step transfer pattern (
Ownable2Step) so that a typo in a new-owner address does not permanently brick admin access?
Is ownership renounced or transferred to a multisig?
Renouncing ownership removes all admin capabilities permanently. This reassures holders that no one can mint extra supply or change fees after launch. If you need ongoing admin ability — for example, to set treasury addresses — transfer ownership to a multi-signature wallet such as Safe (formerly Gnosis Safe) rather than leaving it on an EOA.
// Vulnerable: single EOA owns a mint function
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
// Safer: ownership held by a 3-of-5 multisig and mint is capped
function mint(address to, uint256 amount) external onlyOwner {
require(totalSupply() + amount <= MAX_SUPPLY, "Cap exceeded");
_mint(to, amount);
}
2. Mint Caps and Supply Controls
Unlimited minting is a critical rug-pull vector. Even if the current owner is trustworthy, a compromised private key gives an attacker the same power.
- Hard cap enforced in code: Define a
MAX_SUPPLYconstant and check it inside everymintcall. Do not rely solely on off-chain promises or documentation. - No hidden mint paths: Search for every internal call to
_mintor_mintBatchin your codebase, including inherited contracts. Third-party base contracts sometimes include minting functions that are not immediately obvious. - Emission schedules: If your token has vesting or staking rewards, verify that the smart contract itself enforces the schedule rather than depending on the owner to call a function at the right time.
3. Upgradeability Risks
Upgradeable proxy patterns (UUPS, Transparent Proxy, Beacon) add flexibility but also add significant risk surface. For most simple tokens they are unnecessary.
Do you actually need upgradeability?
If your token is a standard ERC-20 with no novel logic, an immutable deployment is safer and simpler. Upgradeability introduces storage collision risks, uninitialized implementation contracts, and a privileged upgradeTo function that can replace your entire contract logic.
If you do use a proxy
- Use OpenZeppelin's audited proxy libraries and never write your own proxy logic.
- Lock the implementation contract by calling
_disableInitializers()in its constructor so it cannot be initialized directly and used as an attack vector. - Verify storage layout compatibility between implementation versions using a tool like
hardhat-upgradesbefore any upgrade. - Protect
upgradeTo/upgradeToAndCallbehind a timelock and multisig so the community has time to review proposed upgrades.
// Uninitialized implementation — attacker can call initialize() directly
contract TokenV1 is Initializable, ERC20Upgradeable {
function initialize(string memory name, string memory symbol) public initializer {
__ERC20_init(name, symbol);
}
}
// Fixed: disable initializers on the implementation
constructor() {
_disableInitializers();
}
4. Fee and Transfer Mechanics
Custom transfer logic is a frequent source of both bugs and deliberate traps.
- Fee-on-transfer tokens must be carefully handled by any DEX router or yield aggregator. Document the fee explicitly and ensure it cannot be raised to 100% after launch.
- Blacklist and pause functions should be time-locked or governed by a multisig. An owner who can freeze arbitrary addresses or halt all transfers indefinitely is a centralization risk that many token auditors flag as a critical finding.
- Reflection and rebase mechanics are complex and historically error-prone. If your token modifies balances outside of explicit transfers, test edge cases with property-based fuzz testing.
5. Liquidity Locks
Technical contract security and economic security are separate concerns, but both matter to token holders. A technically clean contract can still be rugged by pulling liquidity.
- Lock initial liquidity provider (LP) tokens using a reputable time-lock contract (e.g., Unicrypt, Team Finance, or a custom audited locker) for a minimum of six months, ideally longer.
- Publish the lock transaction publicly. Holders should be able to verify the LP token balance of the locker contract on-chain without trusting any dashboard.
- If you are providing liquidity on a concentrated-liquidity AMM (Uniswap v3), be aware that the position is represented as an NFT, not a fungible LP token — standard ERC-20 lockers may not apply.
6. Standard Vulnerability Classes to Check
Beyond token-specific concerns, your contract is still subject to the full landscape of Solidity vulnerabilities. Before launch, verify that your contract is free from the following common issues. For a deeper treatment of each, see our guide on how to audit an ERC-20 token contract.
- Reentrancy: Any external call made before state updates is a potential reentrancy vector. Follow the checks-effects-interactions pattern.
- Integer overflow/underflow: Solidity 0.8.x has built-in overflow checks, but assembly blocks and
uncheckedregions bypass them. Audit everyuncheckedblock. - Approval front-running: Standard
approveis vulnerable to a race condition. ImplementincreaseAllowance/decreaseAllowanceor use EIP-2612 permit. - Tx.origin authentication: Never use
tx.originfor access control; usemsg.sender. - Incorrect event emission: Missing or incorrect events make off-chain monitoring unreliable and are flagged by static analyzers.
7. Run an Automated Scan Before You Deploy
Reading a checklist is a useful starting point, but automated tools can systematically traverse every execution path in your bytecode in ways a manual review of this kind cannot. Before deployment, run an automated scan against your contract to surface findings from multiple analysis engines simultaneously. A scan that runs Slither, Mythril, Aderyn, Semgrep, Solhint, SMTChecker, and Echidna in parallel will catch many of the issues described above — misuse of access control, unchecked arithmetic in unchecked blocks, unprotected initializers — and attribute each finding to the specific tool that detected it so you can prioritize remediation.
Automated scanning is not a substitute for a full manual audit by experienced auditors, particularly for novel or complex token mechanics. For guidance on when each approach is appropriate, see automated scanners vs manual audits: what is the difference? If you are weighing the cost of a professional audit, our breakdown of smart contract audit costs explains what drives pricing and what you get at each tier.
Pre-Launch Security Checklist Summary
- All privileged functions are gated by
onlyOwneror a role modifier. - Ownership is transferred to a multisig or renounced before public launch.
- A hard supply cap is enforced on-chain in every mint path.
- No hidden mint functions exist in inherited contracts.
- Upgradeability is either absent or protected by timelock and multisig.
- If using a proxy, the implementation contract has initializers disabled.
- Fee parameters are capped in code and cannot be raised to a harmful level post-launch.
- Blacklist and pause functions, if present, are documented and access-controlled.
- LP tokens are locked in a verifiable on-chain locker for a meaningful duration.
- The contract has been scanned with at least one automated static analysis tool.
- All critical and high findings from the scan have been remediated or documented with a rationale for acceptance.
- A re-scan has been run after remediation to confirm fixes did not introduce new issues.
No checklist or automated tool guarantees a contract is free of all vulnerabilities, but systematically working through these items before launch significantly reduces the most common and most costly failure modes. The goal is to make your token as difficult to exploit as possible before real funds are at stake.
Scan your contract before you ship
Run an automated, transparent security scan — seven industry tools in parallel, every finding labeled with its source tool. It is not a substitute for a full manual audit, but it is a fast first line of defense.
Scan a contractFrequently asked questions
Should I renounce ownership of my token contract?
It depends on your roadmap. Renouncing ownership permanently removes all admin capabilities, which is the strongest signal to holders that no one can mint extra tokens or change parameters. If you need ongoing admin functions — such as updating a treasury address or pausing in an emergency — transfer ownership to a reputable multi-signature wallet instead of a single EOA. The worst outcome is leaving ownership on a hot wallet that can be compromised.
What is the difference between a hard supply cap and a soft cap?
A hard cap is enforced by the smart contract itself: a require statement inside the mint function checks that total supply after the mint will not exceed a constant maximum. A soft cap is an off-chain or documentation-based promise that the owner will not mint beyond a certain amount. Soft caps provide no on-chain security guarantee — a compromised or malicious owner can ignore them entirely. Always implement the cap in code.
Can an automated scan tell me definitively that my token is safe?
No. Automated scanners systematically check for known vulnerability patterns across all execution paths, which makes them faster and more consistent than a quick manual read-through. However, they cannot evaluate business logic correctness, economic attack surfaces, or novel exploit patterns that have not been codified into detector rules. An automated scan is an essential first step and a useful complement to a manual audit, but it does not replace one for high-value or complex contracts.
Do I need to lock liquidity if I plan to add more liquidity later?
Yes. The initial liquidity lock is the most critical one because it covers the period immediately after launch when your token price is most volatile and investor trust is lowest. You can lock additional liquidity tranches as they are added. The lock should cover LP tokens representing a meaningful portion of the total liquidity pool, and the lock duration should be long enough that holders can reasonably trade and exit before the lock expires.
What are the biggest risks with upgradeable token contracts?
The main risks are: an uninitialized implementation contract that an attacker can initialize and exploit; storage layout collisions between implementation versions that corrupt state variables silently; and a privileged upgrade function that, if controlled by a single EOA, allows the entire contract logic to be replaced without community consent. If upgradeability is genuinely necessary, use OpenZeppelin's audited libraries, disable initializers on the implementation, and protect upgrade functions with a timelock and multisig.