Most NFT incidents we are called for are not contract bugs. They are compromised mod accounts posting phishing links, leaked deployer keys, fake mint URLs in announcements, and webhook leaks. The contract is usually fine; the operation around it is the attack surface.
This is the pre-launch checklist we run with NFT projects, covering both the contract and the operational layer that tends to get skipped.
The contract
1. The mint logic actually matches the spec
Off-by-one in supply, broken allowlist logic, signature replay across chains, missing access control on withdraw, missing pause function, ETH being sent to a hardcoded EOA instead of a Safe. These are common findings in NFT contracts, and they all originate from the same cause: the contract is written in a hurry the week before launch.
A real smart contract audit catches them. A friend "having a quick look" rarely does.
2. Reentrancy on the mint function
NFT contracts often call back into the receiver via _safeMint, which invokes onERC721Received. A malicious receiver contract can re-enter the mint function before its supply counter is updated, minting more NFTs than intended in a single call. The fix is the checks-effects-interactions pattern: update state before calling external code.
3. Allowlist verification cannot be replayed
If you use signed allowlist entries (a common pattern), the signature must be bound to the chain ID and to a per-user nonce. Without that, a signature valid on one network can be replayed on another, or by another wallet.
4. The withdraw function is on a multi-sig, not an EOA
The contract collects ETH from minters. The address that can pull that ETH out is the address that controls the mint revenue. If that address is a hot EOA on the deployer's laptop, you have rebuilt the FTX commingled-wallet problem on your own project. Use a multi-sig. Always.
5. Royalty configuration is set correctly on every marketplace
ERC-2981 is the standard, but every marketplace handles it differently. Test royalties end-to-end on OpenSea, Blur, Magic Eden, X2Y2, whichever venues your collection will trade on. Misconfigured royalties cost real money over a collection's life.
The deployment
6. The deployer wallet is single-use and clean
The wallet that deploys the contract should not hold funds, should not have history with malicious contracts, and should be retired after deployment. The deployer key is the most-attacked address in your project, leak it once and the attacker has the keys to your contract's privileged functions.
7. Privileged roles are transferred immediately
Right after deployment, transfer ownership and any admin roles from the deployer EOA to the Safe. Verify on-chain that the transfer happened and the deployer no longer has privileges. Many incidents we have investigated traced back to "we meant to do that and forgot."
8. The contract is verified on Etherscan
Verified source matches the deployed bytecode. Buyers, scanners, and your community can read the actual logic. An unverified contract is a red flag for sophisticated buyers and an open invitation to fake "verified" copies on the same chain.
The community
9. Discord and X are locked down
Most NFT incidents start on Discord. Concrete defenses:
- 2FA enforced for every moderator and admin role. Not optional. Authenticator app, not SMS.
- Bot-token permissions audited. A leaked webhook can post phishing links from inside your server, with the project's branding.
- Channel permissions reviewed. Only specific roles should be able to post in announcement channels; only specific bots should be allowed to embed links.
- Founders' personal accounts hardened separately. A hijacked founder X account is the highest-value phishing platform in your project's universe.
10. The mint URL is canonical and announced
There should be one mint URL, hosted on a domain you control, communicated through every official channel before launch. If your community sees a different URL on launch day, they should be skeptical by default. Repeat the URL in every announcement, and tell holders explicitly: we will never DM you a different mint link.
11. There is a written incident playbook
Before launch, write down:
- Who in the team has the authority to pause the contract?
- Who posts the official "we are aware" message, and where?
- Who contacts the marketplaces if a stolen item starts moving?
- Who has the Incident Response firm's emergency number?
The first hour of an incident decides what the next year looks like. A team that has rehearsed recovers; a team that improvises does not.
Post-launch
12. The treasury and creator wallets are surveilled
Post-mint, the project wallet holds the revenue and the unminted reserve. The creator wallets hold the founder allocations. These addresses are on-chain billboards for attackers.
Wallet Surveillance on these addresses is real-time alerting on:
- Approvals to unfamiliar contracts.
- Outgoing transfers that diverge from the established pattern.
- Interactions with addresses linked to phishing infrastructure.
- Queued multi-sig proposals before they execute.
The cost is small. The benefit is that the moment a signer's device is compromised, you find out, before the attacker drains.
The one thing nobody wants to do
Ship the contract three weeks before mint, not three days. The audit needs time. The fixes need re-audit. The deployer process needs a dry run on testnet that mirrors mainnet exactly. The community needs to see the contract, the URL, and the official channels well before the moment of FOMO.
Every project we have helped recover from a botched launch had the same regret: they shipped on the announced date instead of the ready date. Move the date once. Save the project.