TokenizeArt
Tutoriel complet pour créer votre premier NFT BEP-721 sur la BNB Smart Chain, de l'image jusqu'à un jeton non fongible miné, hébergé sur IPFS et vérifié sur BSCScan.
À propos
Niveau : débutant · Durée estimée : 3 à 4 h · 0 € requis
Ce tutoriel couvre l'intégralité du processus : concepts de base (NFT, ERC-721, IPFS), configuration de MetaMask, écriture du smart contract en Solidity, compilation, publication de l'image et des métadonnées sur IPFS via Pinata, déploiement sur le BSC Testnet, minage du NFT et vérification sur BSCScan.
Voir aussi
Ce tutoriel est lié à la fiche projet TokenizeArt qui présente le résultat final, la stack technique et les compétences acquises.
Les concepts sont expliqués en détail dans les pages NFT & ERC-721 et IPFS & Pinata, ainsi que dans le cours Blockchain.
1. Comprendre avant de coder
On ne part pas écrire du Solidity sans comprendre ce qu'est un NFT et où vivent ses données.
1.1 Qu'est-ce qu'un NFT ?
Un NFT (non-fungible token, ou jeton non fongible) est un certificat numérique d'authenticité et de propriété enregistré sur une blockchain.
Contrairement à un jeton fongible comme un token BEP-20 où un jeton en vaut un autre, chaque NFT est unique et non interchangeable : il porte un identifiant (tokenId) qui lui est propre.
En résumé
Un NFT = un identifiant unique sur la blockchain + des métadonnées (l'œuvre et sa description) stockées ailleurs, sur IPFS.
Pour aller plus loin : NFT & ERC-721.
1.2 Le standard ERC-721 (et BEP-721)
ERC-721 est la norme Ethereum qui définit l'API d'un jeton non fongible : ownerOf, transferFrom, approve… BEP-721 en est le strict équivalent sur la BNB Smart Chain (même interface, autre réseau).
On ne réécrit pas cette norme à la main : on hérite de l'implémentation ERC721URIStorage d'OpenZeppelin, qui gère pour nous la propriété, les transferts et l'URI des métadonnées.
1.3 Où sont stockées les données ?
Il y a deux étages dans un NFT, et c'est la clé du projet :
| Étage | Contenu | Où ? | Pourquoi |
|---|---|---|---|
| La blockchain | Le contrat, le propriétaire, le tokenId | BNB Smart Chain | Immuable, vérifiable, propriété prouvée |
| Le stockage distribué | L'image et le JSON de métadonnées | IPFS (via Pinata) | Stocker de gros fichiers on-chain coûterait une fortune |
Le contrat ne contient donc que l'adresse du JSON de métadonnées (tokenURI) ; c'est le wallet qui va chercher l'image à cette adresse pour l'afficher.
Pour aller plus loin : IPFS & Pinata.
1.4 Le vocabulaire à connaître
- Wallet (portefeuille) : application comme MetaMask qui détient tes adresses et tes fonds.
- Adresse : une longue chaîne (
0x...) qui identifie un compte, un peu comme un numéro de compte bancaire. - Smart contract : un programme qui vit sur la blockchain - ici, la fabrique qui crée les NFT.
- NFT : un jeton non fongible, unique, régi par la norme ERC-721 / BEP-721.
- Métadonnées : le « dossier » du NFT (nom, description, image, attributs) au format JSON.
- IPFS : un système de stockage distribué de fichiers.
- CID : l'empreinte unique d'un fichier sur IPFS (
ipfs://Qm...) - elle permet de le retrouver n'importe où. - Pinata : le service qui « épingle » (maintient en ligne) tes fichiers sur IPFS.
- Gas : la petite redevance payée pour exécuter une action sur la blockchain.
- Faucet : un site qui distribue gratuitement des jetons de test.
- Testnet : un réseau de test public où la crypto ne vaut rien.
- Block explorer (BSCScan) : un moteur de recherche de la blockchain.
Notre objectif
Un NFT unique nommé Zehd42 (symbole Z42) portant le chiffre 42, avec une image et des métadonnées épinglées sur IPFS, miné depuis un contrat déployé sur la BNB Smart Chain Testnet.
Rassure-toi
On ne travaille que sur le testnet. Aucun argent réel n'est engagé, et aucun code n'est dangereux pour ta machine.
2. Installer MetaMask et ajouter le réseau de test
MetaMask est le portefeuille qui va signer les transactions du contrat.
Objectif
MetaMask installé, connecté au BSC Testnet, avec une adresse prête à l'emploi.
2.1 Installer l'extension
- Va sur
https://metamask.ioet installe l'extension pour ton navigateur (ou l'application mobile). - Crée un portefeuille, choisis un mot de passe, puis note la phrase de récupération sur papier.
Attention
La phrase de récupération (seed phrase) ne se partage jamais et ne se stocke pas dans un fichier ou un dépôt Git. C'est la clé de tes fonds, même en test.
2.2 Ajouter le réseau BSC Testnet
MetaMask arrive configuré sur Ethereum. Il faut lui ajouter le réseau de test de la BNB Smart Chain.
- Ouvre MetaMask → menu des réseaux → Ajouter un réseau manuellement.
-
Renseigne :
Champ Valeur Nom du réseau BSC TestnetNouvelle URL RPC https://data-seed-prebsc-1-s1.binance.org:8545/ID de chaîne 97Symbole de la monnaie tBNBURL de l'explorateur de blocs https://testnet.bscscan.com -
Enregistre, puis sélectionne le réseau BSC Testnet.
Astuce
Le sélecteur de réseau en haut de MetaMask affiche le réseau actif. Tout déploiement se fera sur celui qui est sélectionné : vérifie-le avant chaque transaction importante.
2.3 Copier ton adresse
- Clique sur le nom du compte en haut de MetaMask pour copier l'adresse publique (
0x...). - Garde-la sous la main : c'est elle qui recevra le NFT et qui apparaîtra dans
ownerOf(1).
Vérif
La case réseau affiche BSC Testnet et une adresse en 0x est copiée.
3. Récupérer des tBNB gratuits (faucet)
Chaque transaction coûte du gas, payé en tBNB (BNB de test). Le faucet en distribue gratuitement.
Objectif
Un solde non nul en tBNB sur l'adresse MetaMask.
3.1 Utiliser le faucet de BNB Chain
- Va sur le faucet BNB Chain.
- Colle ton adresse MetaMask et valide le captcha.
- Attends la transaction : quelques
tBNBarrivent sur ton compte.
Si tu n'as rien reçu
Certains faucets limitent les demandes par adresse et par jour. Réessaie plus tard, et vérifie que tu as bien collé une adresse du BSC Testnet (et non du réseau principal).
Vérif
MetaMask affiche un solde en tBNB. Cela suffit largement pour tout le tutoriel.
4. Écrire le smart contract du NFT
On écrit le programme qui fabrique le NFT en Solidity. On réutilise la bibliothèque OpenZeppelin qui fournit un ERC-721 éprouvé (pas besoin de tout réinventer, c'est plus sûr - et c'est explicitement recommandé par le sujet).
Objectif
Un fichier zehd42.sol correct, commenté.
4.1 Le code complet du contrat
Ce fichier doit aller dans le dossier code/ de ton dépôt sous le nom zehd42.sol :
// SPDX-License-Identifier: MIT
// Zehd42 - NFT non fongible (BEP-721 / ERC-721) sur la BNB Smart Chain
pragma solidity ^0.8.20;
// Imports OpenZeppelin v5.1.0 : ERC721 (standard), ERC721URIStorage (URI), Ownable (propriété)
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v5.1.0/contracts/token/ERC721/ERC721.sol";
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v5.1.0/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v5.1.0/contracts/access/Ownable.sol";
contract Zehd42 is ERC721URIStorage, Ownable {
// Compteur des tokens créés (ids à partir de 1) :
uint256 private _tokenIds;
// URI de base des métadonnées (IPFS) :
string public baseURI;
// Constructeur : nom, symbole et URI de base :
constructor(string memory _baseURI) ERC721("Zehd42", "Z42") Ownable(msg.sender) {
baseURI = _baseURI;
}
// Miner un NFT, réservé au propriétaire :
function mintNFT(address recipient) public onlyOwner returns (uint256) {
_tokenIds += 1; // nouvel id
uint256 newTokenId = _tokenIds;
_safeMint(recipient, newTokenId); // vérifie que le destinataire accepte le NFT
_setTokenURI(newTokenId, baseURI); // associe les métadonnées IPFS
return newTokenId;
}
// Changer l'URI de base des prochains mints (réservé au propriétaire)
// Fait évoluer l'emplacement des métadonnées sans changer l'adresse du contrat :
function setBaseURI(string memory _newBaseURI) public onlyOwner {
baseURI = _newBaseURI;
}
}
4.2 Expliquer les mots du code
| Élément | Explication |
|---|---|
pragma solidity ^0.8.20; | Version du langage utilisée (≥ 0.8.20). |
contract Zehd42 is ERC721URIStorage, Ownable | On hérite du standard OpenZeppelin et du module Ownable (gestion du propriétaire). |
ERC721("Zehd42", "Z42") | Le nom et le symbole de la collection, passés au constructeur parent. |
_tokenIds | Compteur privé : chaque mint incrémente l'id, donc chaque NFT est unique. |
_safeMint(recipient, newTokenId) | Crée le NFT et vérifie que le destinataire est capable de le recevoir (norme ERC-721). |
_setTokenURI(newTokenId, baseURI) | Associe au jeton l'URL des métadonnées - c'est tokenURI(1) en lecture. |
onlyOwner | Modificateur apporté par Ownable : bloque la fonction si l'appelant n'est pas le propriétaire. |
setBaseURI(...) | Permet de changer l'URI des prochains mints sans redéployer ; les jetons déjà minés gardent leur tokenURI. |
Pourquoi _safeMint et pas _mint ?
_safeMint vérifie que le destinataire est un contrat capable de recevoir des NFT (onERC721Received), ou un simple compte externe. C'est la pratique recommandée pour éviter de « perdre » un NFT dans un contrat qui ne sait pas le gérer.
4.3 Mettre à jour ton dépôt
- Ouvre
code/zehd42.solet colle le code ci-dessus. - Assure-toi que chaque fonction a bien un commentaire lisible.
5. Compiler avec Remix
Remix IDE est l'IDE en ligne recommandé par le sujet : il permet d'écrire, compiler et déployer sans rien installer.
Objectif
Ton code compile sans erreur.
5.1 Ouvrir Remix et créer le fichier
- Va sur
https://remix.ethereum.org. - Dans l'explorateur de fichiers, crée un dossier
contractss'il n'existe pas, puis un fichierzehd42.sol. - Colle le code du contrat et enregistre (
Ctrl+S).
5.2 Comprendre les imports
Le contrat importe directement OpenZeppelin v5.1.0 par URL :
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/v5.1.0/contracts/token/ERC721/ERC721.sol";
Pourquoi cette version ?
La v5.1.0 ne dépend pas de l'opcode mcopy, ce qui simplifie la compilation avec la version d'EVM par défaut de Remix.
Remix télécharge automatiquement les imports lors de la compilation, tu n'as rien à installer manuellement.
5.3 Choisir la version et compiler
- Ouvre l'onglet Solidity Compiler.
- Choisis une version 0.8.20 ou supérieure (l'import exige
^0.8.20). - Clique sur Compile zehd42.sol.
Erreurs fréquentes
- Source not found : l'URL d'import est incomplète ou mal copiée> Vérifie la ligne
import. - ParserError : la version de Solidity sélectionnée est trop ancienne (il faut ≥ 0.8.20).
- Identifier already declared : le fichier existe en double dans l'explorateur de Remix.
Vérif
Une coche verte apparaît sur l'onglet Solidity Compiler.
6. Publier l'image et les métadonnées sur IPFS
On ne met pas l'image dans le contrat (ce serait hors de prix). On la publie sur IPFS via Pinata, puis on met le lien dans les métadonnées.
Objectif
Deux CID IPFS : celui de l'image, puis celui du JSON de métadonnées.
6.1 Préparer l'œuvre
L'image doit remplir les exigences du sujet :
- une image originale contenant le chiffre 42 clairement lisible ;
- un format
.pngou.jpg; - aucun terme ni contenu insultant.
Place-la dans images/ de ton dépôt (par exemple images/Zehd42.png).
6.2 Créer un compte Pinata
- Va sur pinata.cloud et crée un compte gratuit.
- Une fois connecté, l'onglet Files liste les fichiers épinglés.
L'offre gratuite suffit
Le quota d'épinglage gratuit est très large pour quelques fichiers. Une clé API n'est pas nécessaire pour ce projet : la clé secrète se garde confidentielle.
6.3 Épingler l'image
- Onglet Files → Add Files → File.
- Sélectionne l'image, puis clique sur Upload.
- Dans la liste, copie la colonne CID : c'est le CID image.
6.4 Écrire les métadonnées (JSON)
Crée code/zehd42.json - c'est le « dossier » du NFT, que les wallets vont lire :
{
"name": "Zehd42",
"description": "Un NFT unique créé dans le cadre du projet Zehd42. L'œuvre représente la réponse... et bien sûr, le 42.",
"image": "https://gateway.pinata.cloud/ipfs/<CID_IMAGE>",
"attributes": [
{ "trait_type": "Artiste", "value": "alamizan" },
{ "trait_type": "Projet", "value": "Zehd42" }
]
}
Deux pièges classiques
- Le champ
imagedoit contenir l'URL HTTPS de passerelle (https://gateway.pinata.cloud/ipfs/<CID_IMAGE>), pas un chemin local ni unipfs://. - Aucun espace parasite en début ou en fin d'URL : c'est un bug constaté sur ce projet, le wallet affiche alors une case vide.
6.5 Épingler les métadonnées
- Refaire Add Files → File avec
code/zehd42.json. - Copier son CID : il diffère de celui de l'image (deux contenus distincts). C'est le CID JSON.
- Tester l'affichage :
https://gateway.pinata.cloud/ipfs/<CID_JSON>doit renvoyer le JSON.
Vérif
Tu as deux CID, et l'URL de passerelle du JSON s'affiche dans le navigateur. Ne supprime plus ces fichiers du tableau de bord Pinata : tant qu'ils sont épinglés, ils restent disponibles.
7. Déployer sur la BSC Testnet
On déploie le contrat en lui passant l'URL de passerelle du JSON : c'est le _baseURI du constructeur.
Objectif
Une adresse de contrat déployée et reliée à tes métadonnées IPFS.
7.1 Connecter Remix à MetaMask
- Dans Remix, ouvre l'onglet Deploy & Run Transactions.
- Dans Environment, choisis Injected Provider - MetaMask.
Si rien ne se connecte
Vérifie que MetaMask est déverrouillé, que le réseau BSC Testnet est bien sélectionné, puis accepte la connexion demandée par Remix.
7.2 Lancer le déploiement
- Dans le champ du constructeur, saisis le
_baseURI:
- Clique sur Deploy.
- MetaMask ouvre une fenêtre : vérifie le réseau (BSC Testnet) et confirme.
- Attends la confirmation : le contrat apparaît dans Deployed Contracts.
Coût du déploiement
Le déploiement consomme du tBNB de test (quelques dixièmes suffisent largement). Aucun fonds réel n'est engagé.
7.3 Récupérer l'adresse du contrat
- Déplie le contrat déployé dans Remix.
- Copie l'icône « copier » à côté de son adresse.
- Vérifie que ce sont bien tes métadonnées qui sont référencées en lisant
baseURI().
Réussi !
Le contrat répond à baseURI(), name() et symbol() (Zehd42 / Z42).
8. Miner le NFT et vérifier la propriété
Le NFT n'existe pas encore : le contrat n'est que la fabrique. Il faut appeler mintNFT.
Objectif
Le tokenId 1 miné, propriétaire et tokenURI vérifiés.
8.1 Miner le NFT
- Dans Deployed Contracts, déplie le contrat.
- Repère la fonction
mintNFTet colle ton adresse MetaMask commerecipient. - Clique sur transact et confirme dans MetaMask.
Tu dois être le propriétaire
mintNFT est protégé par onlyOwner. Si tu appelles la fonction depuis une autre adresse, la transaction est annulée avec revert - c'est le comportement attendu et exactement ce qu'il faut montrer à l'évaluation.
8.2 Vérifier en lecture
Un appel transact modifie la chaîne ; un appel bleu (lecture) ne coûte rien et ne nécessite pas de signature.
| Appel | Résultat attendu |
|---|---|
mintNFT(<ton adresse>) | Un tokenId retourné. Le premier vaut 1 |
ownerOf(1) | Ton adresse |
tokenURI(1) | https://gateway.pinata.cloud/ipfs/<CID_JSON> |
balanceOf(<ton adresse>) | 1 |
baseURI() | https://gateway.pinata.cloud/ipfs/<CID_JSON> |
9. Vérifier et publier sur BSCScan
La vérification rend ton code source public et prouve que le contrat déployé correspond exactement à celui que tu as écrit.
Objectif
Le contrat vérifié sur BSCScan, avec un lien partageable.
9.1 Trouver le contrat
- Copie l'adresse du contrat depuis Remix.
- Ouvre-la sur BSCScan Testnet.
- L'onglet Contract montre d'abord le bytecode : illisible, donc à vérifier.
9.2 Vérifier le code source
- Sur la page du contrat → Contract → Verify and Publish.
- Choisis Solidity (Single file) si le contrat tient en un seul fichier, sinon Standard-Json-Input (obligatoire ici à cause des imports).
- Renseigne la version du compilateur utilisée dans Remix et l'optimisation activée ou non.
- Colle le code source et valide.
Si la vérification échoue
- Version du compilateur ou optimisation différente de Remix : ce sont les causes n° 1.
- Imports multiples : utilise Standard-Json-Input et fournis tous les fichiers, ou laisse BSCScan récupérer les imports par URL.
- Contrat déjà vérifié : dans ce cas, rien à faire, la mention « Verified » est déjà affichée.
Résultat
La mention Verified apparaît, et le code source devient consultable publiquement. Ton NFT est alors exploitable depuis l'explorateur.
10. Dépannage et questions fréquentes
10.1 « Not enough funds for gas »
Ton solde en tBNB est vide ou insuffisant. Retourne à l'étape 3 (faucet).
10.2 La transaction reste « pending »
Le testnet est parfois congestionné. Attends une minute, puis vérifie la transaction dans l'onglet Activity de MetaMask : tu peux en afficher le suivi sur BSCScan.
10.3 Le wallet affiche une image cassée
Trois causes possibles, dans l'ordre :
tokenURI(1)ne pointe pas vers le JSON (mais vers l'image) ;- le JSON contient une URL d'image invalide ou avec un espace parasite ;
- le fichier n'est plus épinglé sur Pinata.
10.4 « revert » quand j'appelle mintNFT depuis un autre compte
Normal ! mintNFT est protégé par onlyOwner. C'est le comportement attendu et c'est exactement ce qu'il faut montrer à l'évaluation.
10.5 La passerelle Pinata renvoie une page vide
Propagation IPFS : un fichier tout juste publié peut mettre de quelques secondes à quelques minutes à être visible via toutes les passerelles. Attends, puis recharge la page.
10.6 Est-ce que je risque de perdre de l'argent ?
Non. On ne travaille que sur la BSC Testnet avec des tBNB de test qui ne valent rien. N'utilise jamais le Mainnet ni des fonds réels.
10.7 Et si je veux changer les métadonnées après le mint ?
Les métadonnées sont figées par CID : modifier le JSON produit un nouveau CID. setBaseURI(...) (réservé au propriétaire) permet de pointer les prochains mints vers les nouvelles métadonnées, sans redéployer le contrat. Les jetons déjà minés conservent leur tokenURI.
Lien vers source : BNB Chain · Remix IDE · BSCScan Testnet · OpenZeppelin · Pinata