La plupart des incidents NFT pour lesquels on est appelés ne sont pas des bugs de contrat. Ce sont des comptes modos compromis qui postent des liens de phishing, des clés de deployer fuitées, de fausses URLs de mint dans les annonces, des fuites de webhook. Le contrat est en général OK ; l'opération autour, c'est la surface d'attaque.
Voici la checklist pré-lancement qu'on déroule avec les projets NFT, qui couvre à la fois le contrat et la couche opérationnelle souvent négligée.
Le contrat
1. La logique de mint correspond vraiment au spec
Off-by-one sur le supply, allowlist cassée, signature replay entre chaînes, access control manquant sur withdraw, fonction de pause manquante, ETH envoyé à une EOA hardcodée au lieu d'un Safe. Ce sont des findings courants dans les contrats NFT, et ils viennent tous de la même cause : le contrat est écrit dans la précipitation la semaine du lancement.
Un vrai audit de smart contract les attrape. Un copain qui « jette un coup d'œil rapide », rarement.
2. Reentrancy sur la fonction de mint
Les contrats NFT appellent souvent le receveur via _safeMint, qui invoque onERC721Received. Un contrat receveur malicieux peut ré-entrer la fonction de mint avant que le compteur de supply ne soit mis à jour, en mintant plus de NFTs qu'attendu en un seul appel. Le correctif, c'est le pattern checks-effects-interactions : mettre à jour l'état avant d'appeler du code externe.
3. La vérification d'allowlist ne peut pas être rejouée
Si vous utilisez des entrées d'allowlist signées (pattern courant), la signature doit être liée au chain ID et à un nonce par utilisateur. Sans ça, une signature valide sur un réseau peut être rejouée sur un autre, ou par un autre wallet.
4. La fonction de withdraw est sur un multi-sig, pas une EOA
Le contrat collecte les ETH des minteurs. L'adresse qui peut tirer ces ETH, c'est l'adresse qui contrôle la revenue du mint. Si c'est une hot EOA sur le laptop du deployer, vous avez reconstruit le problème commingled-wallet de FTX dans votre projet. Utilisez un multi-sig. Toujours.
5. La config de royalties est correcte sur chaque marketplace
ERC-2981 est le standard, mais chaque marketplace le gère différemment. Testez les royalties bout en bout sur OpenSea, Blur, Magic Eden, X2Y2, les places où votre collection va trader. Des royalties mal configurées coûtent de l'argent sur la durée d'une collection.
Le déploiement
6. Le wallet de deployer est à usage unique et propre
Le wallet qui déploie le contrat ne doit pas détenir de fonds, ne doit pas avoir d'historique avec des contrats malicieux, et doit être retiré après déploiement. La clé de deployer, c'est l'adresse la plus attaquée de votre projet, fuitez-la une fois et l'attaquant a les clés des fonctions privilégiées.
7. Les rôles privilégiés sont transférés immédiatement
Juste après le déploiement, transférez l'ownership et tout rôle admin de l'EOA de deployer vers le Safe. Vérifiez on-chain que le transfert a eu lieu et que le deployer n'a plus de privilèges. Beaucoup d'incidents qu'on a investigué ont ce pattern : « on voulait le faire et on a oublié ».
8. Le contrat est vérifié sur Etherscan
Source vérifiée qui matche le bytecode déployé. Acheteurs, scanners et votre communauté lisent la vraie logique. Un contrat non vérifié est un drapeau rouge pour les acheteurs sophistiqués et une invitation ouverte à de fausses copies « vérifiées » sur la même chaîne.
La communauté
9. Discord et X sont verrouillés
La plupart des incidents NFT commencent sur Discord. Défenses concrètes :
- 2FA exigé pour chaque rôle modo et admin. Pas optionnel. Authenticator app, pas SMS.
- Permissions des bot tokens auditées. Un webhook fuité peut poster des liens de phishing depuis l'intérieur de votre serveur, avec votre branding.
- Permissions de canaux revues. Seuls des rôles spécifiques doivent pouvoir poster dans les canaux d'annonces ; seuls des bots spécifiques doivent pouvoir embed des liens.
- Comptes perso des fondateurs durcis séparément. Un compte X de fondateur hijacké, c'est la plateforme de phishing à plus haute valeur dans l'univers de votre projet.
10. L'URL de mint est canonique et annoncée
Il doit y avoir une URL de mint, hostée sur un domaine que vous contrôlez, communiquée à travers chaque canal officiel avant le lancement. Si votre communauté voit une URL différente le jour J, elle doit être sceptique par défaut. Répétez l'URL dans chaque annonce, et dites-le explicitement aux holders : nous ne vous DM-erons jamais un autre lien de mint.
11. Il existe un playbook d'incident écrit
Avant le lancement, écrivez :
- Qui dans l'équipe a l'autorité de mettre le contrat en pause ?
- Qui poste le message officiel « nous sommes au courant » et où ?
- Qui contacte les marketplaces si un item volé commence à bouger ?
- Qui a le numéro d'urgence du cabinet Réponse à incident ?
La première heure d'un incident décide de l'année qui vient. Une équipe qui a répété récupère ; une équipe qui improvise non.
Post-lancement
12. La trésorerie et les wallets créateurs sont surveillés
Post-mint, le wallet projet détient la revenue et la réserve non-mintée. Les wallets créateurs détiennent les allocations fondateurs. Ces adresses sont des panneaux d'affichage on-chain pour les attaquants.
Wallet Surveillance sur ces adresses, c'est de l'alerting temps réel sur :
- Approvals à des contrats inconnus.
- Sorties qui divergent du pattern établi.
- Interactions avec des adresses liées à des infras de phishing.
- Propositions multi-sig en file avant exécution.
Le coût est petit. Le bénéfice, c'est que l'instant où le device d'un signataire est compromis, vous le savez, avant que l'attaquant ne draine.
La seule chose que personne ne veut faire
Livrer le contrat trois semaines avant le mint, pas trois jours. L'audit a besoin de temps. Les corrections ont besoin d'un re-audit. Le process de deployer a besoin d'un dry run sur testnet qui mime exactement le mainnet. La communauté a besoin de voir le contrat, l'URL et les canaux officiels bien avant le moment de FOMO.
Chaque projet qu'on a aidé à récupérer d'un lancement raté avait le même regret : ils ont livré à la date annoncée au lieu de la date prête. Décalez la date une fois. Sauvez le projet.