Tutoriel pour frapper des NFT
L'un des plus grands défis pour les développeurs venus du Web2 est de comprendre comment connecter son contrat intelligent à un projet d'interface et interagir avec lui.
En construisant un générateur de NFT — une interface simple où vous pouvez saisir un lien vers votre ressource numérique, un titre et une description — vous apprendrez à :
- Vous connecter à MetaMask via votre projet en frontend
- Appeler les méthodes du contrat intelligent depuis votre interface
- Signer des transactions à l'aide de MetaMask
Dans ce tutoriel, nous utiliserons React (opens in a new tab) comme framework frontend. Puisque ce tutoriel s'intéresse avant tout au développement Web3, nous ne nous attarderons pas à expliquer les bases de React. Au lieu de cela, nous nous concentrerons sur l'ajout de fonctionnalités à notre projet.
En prérequis, il vous faudra un niveau débutant en React — savoir comment fonctionnent les composants, les props, useState/useEffect, et les appels des fonctions de base. Si vous n'avez jamais entendu parler de l'un de ces termes auparavant, vous pouvez consulter ce tutoriel d'introduction à React (opens in a new tab). Pour les apprenants plus visuels, nous recommandons vivement cette excellente série de vidéos Full Modern React Tutorial (opens in a new tab) par Net Ninja.
Et si vous ne l'avez pas déjà fait, vous aurez certainement besoin d'un compte Alchemy pour terminer ce tutoriel ainsi que pour construire quoi que ce soit sur la blockchain. Créez un compte gratuit ici (opens in a new tab).
Sans plus attendre, commençons !
Création de NFT 101
Avant même de commencer à regarder du code, il est important de comprendre comment la fabrication d'un NFT fonctionne. Elle comporte deux étapes :
Publier un contrat intelligent de NFT sur la blockchain Ethereum
La plus grande différence entre les deux normes de contrat intelligent NFT est que l'ERC-1155 est un standard multijeton et inclut la fonctionnalité de lot. Alors que l'ERC-721 est un standard à jeton unique et supporte donc uniquement le transfert d'un jeton à la fois.
Appeler la fonction de frappe
Généralement, cette fonction de frappe vous demande de transmettre deux variables en tant que paramètres, premièrement le destinataire, qui spécifie l'adresse qui recevra votre NFT fraîchement frappé, et deuxièmement le tokenURI du NFT, une chaîne qui renvoie à un document JSON décrivant les métadonnées du NFT.
Les métadonnées d'un NFT sont ce qui lui donne vie, lui permettant d'avoir des propriétés configurables, comme un nom, une description, une image et d'autres attributs. Voici un exemple de tokenURI (opens in a new tab), qui contient les métadonnées d'un NFT.
Dans ce tutoriel, nous allons nous concentrer sur la deuxième partie, en appelant la fonction existante de frappe d'un contrat intelligent de type NFT avec notre interface React.
Voici un lien (opens in a new tab) vers le contrat intelligent de NFT ERC-721 que nous appellerons dans ce tutoriel. Si vous souhaitez apprendre comment nous l'avons créé, nous vous recommandons vivement de consulter notre autre tutoriel, "Comment créer un NFT" (opens in a new tab).
Bien, maintenant que nous comprenons comment la fabrication de NFT fonctionne, clonons nos fichiers et démarrons !
Cloner les fichiers de démarrage
Tout d'abord, accédez au dépôt GitHub nft-minter-tutorial (opens in a new tab) pour obtenir les fichiers de démarrage de ce projet. Clonez ce dépôt dans votre environnement local.
Lorsque vous ouvrez ce dépôt nft-minter-tutorial cloné, vous remarquerez qu'il contient deux dossiers : minter-starter-files et nft-minter.
minter-starter-filescontient les fichiers de démarrage (essentiellement l'interface utilisateur React) de ce projet. Dans ce tutoriel, nous travaillerons dans ce répertoire, où vous apprendrez à donner vie à cette interface utilisateur en la connectant à votre portefeuille Ethereum et à un contrat intelligent de NFT.nft-mintercontient le tutoriel entièrement terminé et est là pour vous servir de référence si vous êtes bloqué.
Ensuite, ouvrez votre copie de minter-starter-files dans votre éditeur de code, puis accédez à votre dossier src.
Tout le code que nous allons écrire se trouvera dans le dossier src. Nous allons modifier le composant Minter.js et écrire des fichiers javascript supplémentaires pour donner à notre projet des fonctionnalités Web3.
Étape 2 : consultez nos fichiers de démarrage
Avant de commencer à coder, il est important de connaître ce qui est déjà fourni dans les fichiers de base.
Faire fonctionner votre projet React
Commençons par exécuter le projet React dans notre navigateur. La beauté de React est qu'une fois que notre projet est en cours d'exécution dans notre navigateur, toutes les modifications que nous sauvegardons seront mises à jour en direct dans notre navigateur.
Pour faire fonctionner le projet, accédez au répertoire racine du dossier minter-starter-files et exécutez npm install dans votre terminal pour installer les dépendances du projet :
cd minter-starter-filesnpm installUne fois l'installation terminée, exécutez npm start dans votre terminal :
npm startCela devrait ouvrir http://localhost:3000/ (opens in a new tab) dans votre navigateur, où vous verrez le frontend pour notre projet. Il devrait se composer de 3 champs : un pour renseigner le lien vers l'actif de votre NFT, un pour le nom de votre NFT, et un pour fournir une description.
Si vous essayez de cliquer sur les boutons « Connect Wallet » (connecter le portefeuille) ou « Mint NFT » (frapper un NFT), vous remarquerez qu'ils ne fonctionnent pas. C'est parce qu'il nous faut encore programmer leur fonctionnalité ! :)
Le composant Minter.js
REMARQUE : Assurez-vous d'être dans le dossier minter-starter-files et non dans le dossier nft-minter !
Retournons dans le dossier src de notre éditeur et ouvrons le fichier Minter.js. Il est très important de comprendre tout ce qui se trouve dans ce fichier, car c'est le composant principal de React sur lequel nous allons travailler.
En haut de ce fichier, nous avons nos variables d'état que nous mettrons à jour après des événements spécifiques.
1//Variables d'état2const [walletAddress, setWallet] = useState("")3const [status, setStatus] = useState("")4const [name, setName] = useState("")5const [description, setDescription] = useState("")6const [url, setURL] = useState("")Vous n'avez jamais entendu parler de variables d'état React ou de hooks d'état ? Consultez cette (opens in a new tab) documentation.
Voici ce que chacune des variables représente :
walletAddress: une chaîne qui stocke l'adresse du portefeuille de l'utilisateurstatus: une chaîne qui contient un message à afficher en bas de l'interface utilisateurname: une chaîne qui stocke le nom du NFTdescription: une chaîne qui stocke la description du NFTurl: une chaîne qui est un lien vers l'actif numérique du NFT
Après les variables d'état, vous verrez trois fonctions non implémentées : useEffect, connectWalletPressed et onMintPressed. Vous remarquerez que toutes ces fonctions sont async, c'est parce que nous y effectuerons des appels d'API asynchrones ! Leurs noms correspondent à leurs fonctionnalités :
1useEffect(async () => {2 //TODO: à implémenter3}, [])45const connectWalletPressed = async () => {6 //TODO: à implémenter7}89const onMintPressed = async () => {10 //TODO: à implémenter11}Afficher toutuseEffect(opens in a new tab) : il s'agit d'un hook React qui est appelé après le rendu de votre composant. Comme il reçoit une prop de tableau vide[](voir ligne 3), il ne sera appelé que lors du premier rendu du composant. Ici, nous appellerons notre écouteur de portefeuille et une autre fonction de portefeuille pour mettre à jour notre interface utilisateur afin de déterminer si un portefeuille est déjà connecté.connectWalletPressed: cette fonction sera appelée pour connecter le portefeuille MetaMask de l'utilisateur à notre dapp.onMintPressed: cette fonction sera appelée pour frapper le NFT de l'utilisateur.
Vers la fin de ce fichier, nous avons l'interface utilisateur de notre composant. Si vous examinez attentivement ce code, vous remarquerez que nous mettons à jour nos variables d'état url, name et description lorsque l'entrée dans les champs de texte correspondants change.
Vous verrez également que connectWalletPressed et onMintPressed sont appelées lorsque les boutons avec les ID mintButton et walletButton sont cliqués respectivement.
1//L'interface utilisateur de notre composant2return (3 <div className="Minter">4 <button id="walletButton" onClick={connectWalletPressed}>5 {walletAddress.length > 0 ? (6 "Connecté : " +7 String(walletAddress).substring(0, 6) +8 "..." +9 String(walletAddress).substring(38)10 ) : (11 <span>Connecter le portefeuille</span>12 )}13 </button>1415 <br></br>16 <h1 id="title">🧙♂️ Outil de frappe de NFT Alchemy</h1>17 <p>18 Ajoutez simplement le lien, le nom et la description de votre actif, puis appuyez sur "Frapper le NFT".19 </p>20 <form>21 <h2>🖼 Lien vers l'actif : </h2>22 <input23 type="text"24 placeholder="par ex., https://gateway.pinata.cloud/ipfs/<hash>"25 onChange={(event) => setURL(event.target.value)}26 />27 <h2>🤔 Nom : </h2>28 <input29 type="text"30 placeholder="par ex., Mon premier NFT !"31 onChange={(event) => setName(event.target.value)}32 />33 <h2>✍️ Description : </h2>34 <input35 type="text"36 placeholder="par ex., Encore plus cool que les cryptokitties ;)"37 onChange={(event) => setDescription(event.target.value)}38 />39 </form>40 <button id="mintButton" onClick={onMintPressed}>41 Frapper le NFT42 </button>43 <p id="status">{status}</p>44</div>45)Afficher toutEnfin, occupons-nous de l'endroit où ajouter ce composant Minter.
Si vous allez dans le fichier App.js, qui est le composant principal de React et qui sert de conteneur pour tous les autres composants, vous verrez que notre composant Minter est injecté à la ligne 7.
Dans ce tutoriel, nous ne modifierons que le fichier Minter.js et ajouterons des fichiers dans notre dossier src.
Maintenant que nous comprenons ce avec quoi nous travaillons, mettons en place notre portefeuille Ethereum !
Configurer votre portefeuille Ethereum
Pour que les utilisateurs puissent interagir avec votre contrat intelligent, ils devront connecter leur portefeuille Ethereum à votre dApp.
Télécharger MetaMask
Pour ce tutoriel, nous allons utiliser MetaMask, un portefeuille virtuel intégré au navigateur, servant à gérer les adresses de votre compte Ethereum. Si vous voulez en savoir plus sur le fonctionnement des transactions sur Ethereum, consultez cette page.
Vous pouvez télécharger et créer un compte MetaMask gratuitement ici (opens in a new tab). Lorsque vous créez un compte, ou si vous en avez déjà un, assurez-vous de basculer sur « Réseau de test Ropsten » en haut à droite (afin de ne pas utiliser d'argent réel).
Ajouter de l'ether depuis un robinet
Afin de frapper nos NFT (ou de signer des transactions sur la blockchain Ethereum), nous aurons besoin de faux Eth. Pour obtenir de l'ETH, vous pouvez vous rendre sur le robinet Ropsten (opens in a new tab), saisir l'adresse de votre compte Ropsten, puis cliquer sur « Send Ropsten Eth ». Vous devriez voir les ETH dans votre compte MetaMask peu de temps après !
Vérifier votre solde
Pour vérifier que notre solde est bien là, faisons une requête eth_getBalance (opens in a new tab) en utilisant l'outil de composition d'Alchemy (opens in a new tab). Cela va retourner la quantité d'ETH que contient votre portefeuille. Après avoir entré l'adresse de votre compte MetaMask et cliqué sur « Send Request », vous devriez voir une réponse comme celle-ci :
1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}REMARQUE : ce résultat est en wei et non en eth. Le wei est utilisé comme la plus petite dénomination d'ether. La conversion de wei vers eth est : 1 eth = 10¹⁸ wei. Donc si on convertit 0xde0b6b3a7640000 en nombre décimal, nous obtenons 1*10¹⁸ ce qui correspond à 1 eth.
Ouf ! Notre faux argent est bien là !
Connecter MetaMask à votre interface utilisateur
Maintenant que notre portefeuille MetaMask est configuré, connectons-y notre dApp !
Parce que nous voulons adhérer au paradigme MVC (opens in a new tab), nous allons créer un fichier séparé qui contient nos fonctions pour gérer la logique, les données et les règles de notre dapp, puis transmettre ces fonctions à notre frontend (notre composant Minter.js).
La fonction connectWallet
Pour ce faire, créons un nouveau dossier appelé utils dans votre répertoire src et ajoutons-y un fichier appelé interact.js, qui contiendra toutes nos fonctions d'interaction avec le portefeuille et le contrat intelligent.
Dans notre fichier interact.js, nous allons écrire une fonction connectWallet, que nous importerons et appellerons ensuite dans notre composant Minter.js.
Dans votre fichier interact.js, ajoutez ce qui suit
1export const connectWallet = async () => {2 if (window.ethereum) {3 try {4 const addressArray = await window.ethereum.request({5 method: "eth_requestAccounts",6 })7 const obj = {8 status: "👆🏽 Écrivez un message dans le champ de texte ci-dessus.",9 address: addressArray[0],10 }11 return obj12 } catch (err) {13 return {14 address: "",15 status: "😥 " + err.message,16 }17 }18 } else {19 return {20 address: "",21 status: (22 <span>23 <p>24 {" "}25 🦊 <a target="_blank" href={`https://metamask.io/download`}>26 Vous devez installer MetaMask, un portefeuille Ethereum virtuel, dans votre27 navigateur.28 </a>29 </p>30 </span>31 ),32 }33 }34}Afficher toutDécomposons ce que fait ce code :
Tout d'abord, notre fonction vérifie si window.ethereum est activé dans votre navigateur.
window.ethereum est une API globale injectée par MetaMask et d'autres fournisseurs de portefeuilles qui permet aux sites Web de demander les comptes Ethereum des utilisateurs. S'il est approuvé, un site peut lire les données des blockchains auxquels l'utilisateur est connecté et proposer à l'utilisateur de signer des messages et des transactions. Consultez la documentation de MetaMask (opens in a new tab) pour plus d'informations !
Si window.ethereum n'est pas présent, cela signifie que MetaMask n'est pas installé. Cela se traduit par le renvoi d'un objet JSON, où l'address renvoyée est une chaîne vide, et où l'objet status JSX relaie que l'utilisateur doit installer MetaMask.
La plupart des fonctions que nous écrivons renverront des objets JSON que nous pourrons utiliser pour mettre à jour nos variables d'état et notre interface utilisateur.
Maintenant, si window.ethereum est présent, c'est là que les choses deviennent intéressantes.
À l'aide d'une boucle try/catch, nous allons essayer de nous connecter à MetaMask en appelant window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). L'appel de cette fonction ouvrira MetaMask dans le navigateur, où l'utilisateur sera invité à connecter son portefeuille à votre dApp.
- Si l'utilisateur choisit de se connecter,
method: "eth_requestAccounts"renverra un tableau contenant toutes les adresses de compte de l'utilisateur qui sont connectées à la dapp. Au total, notre fonctionconnectWalletrenverra un objet JSON qui contient la premièreadressede ce tableau (voir ligne 9) et un messaged'étatqui invite l'utilisateur à écrire un message au contrat intelligent. - Si l'utilisateur rejette la connexion, l'objet JSON contiendra une chaîne vide pour l'
addressrenvoyée et un messaged'étatindiquant que l'utilisateur a rejeté la connexion.
Ajouter la fonction connectWallet à votre composant d'interface utilisateur Minter.js
Maintenant que nous avons écrit cette fonction connectWallet, connectons-la à notre composant Minter.js.
Tout d'abord, nous devrons importer notre fonction dans notre fichier Minter.js en ajoutant import { connectWallet } from "./utils/interact.js"; au début du fichier Minter.js. Vos 11 premières lignes de Minter.js devraient maintenant ressembler à ceci :
1import { useEffect, useState } from "react";2import { connectWallet } from "./utils/interact.js";34const Minter = (props) => {56 //Variables d'état7 const [walletAddress, setWallet] = useState("");8 const [status, setStatus] = useState("");9 const [name, setName] = useState("");10 const [description, setDescription] = useState("");11 const [url, setURL] = useState("");Afficher toutEnsuite, à l'intérieur de notre fonction connectWalletPressed, nous appellerons notre fonction connectWallet importée, comme ceci :
1const connectWalletPressed = async () => {2 const walletResponse = await connectWallet()3 setStatus(walletResponse.status)4 setWallet(walletResponse.address)5}Remarquez-vous que la plupart de nos fonctionnalités sont extraites de notre composant Minter.js à partir du fichier interact.js ? C'est ainsi que nous respectons le paradigme M-V-C !
Dans connectWalletPressed, nous effectuons simplement un appel en attente vers notre fonction importée connectWallet et, à l'aide de sa réponse, nous mettons à jour nos variables status et walletAddress via leurs hooks d'état.
Maintenant, enregistrons les deux fichiers Minter.js et interact.js et testons notre interface utilisateur jusqu'à présent.
Ouvrez votre navigateur sur localhost:3000, et cliquez sur le bouton « Connect Wallet » en haut à droite de la page.
Si MetaMask est installé, vous devriez être invité à connecter votre portefeuille à votre dApp. Accepter l'invitation à se connecter.
Vous devriez voir que le bouton du portefeuille précise maintenant que votre adresse est connectée.
Ensuite, essayez de rafraîchir la page... c'est étrange. Notre bouton de portefeuille nous invite à connecter MetaMask bien qu'il soit déjà connecté...
Mais ne vous inquiétez pas ! Nous pouvons facilement résoudre ce problème en implémentant une fonction appelée getCurrentWalletConnected, qui vérifiera si une adresse est déjà connectée à notre dapp et mettra à jour notre interface utilisateur en conséquence !
La fonction getCurrentWalletConnected
Dans votre fichier interact.js, ajoutez la fonction getCurrentWalletConnected suivante :
1export const getCurrentWalletConnected = async () => {2 if (window.ethereum) {3 try {4 const addressArray = await window.ethereum.request({5 method: "eth_accounts",6 })7 if (addressArray.length > 0) {8 return {9 address: addressArray[0],10 status: "👆🏽 Écrivez un message dans le champ de texte ci-dessus.",11 }12 } else {13 return {14 address: "",15 status: "🦊 Connectez-vous à MetaMask en utilisant le bouton en haut à droite.",16 }17 }18 } catch (err) {19 return {20 address: "",21 status: "😥 " + err.message,22 }23 }24 } else {25 return {26 address: "",27 status: (28 <span>29 <p>30 {" "}31 🦊 <a target="_blank" href={`https://metamask.io/download`}>32 Vous devez installer MetaMask, un portefeuille Ethereum virtuel, dans votre33 navigateur.34 </a>35 </p>36 </span>37 ),38 }39 }40}Afficher toutCe code est très similaire à la fonction connectWallet que nous venons d'écrire.
La principale différence est qu'au lieu d'appeler la méthode eth_requestAccounts, qui ouvre MetaMask pour que l'utilisateur connecte son portefeuille, nous appelons ici la méthode eth_accounts, qui renvoie simplement un tableau contenant les adresses MetaMask actuellement connectées à notre dapp.
Pour voir cette fonction en action, appelons-la dans la fonction useEffect de notre composant Minter.js.
Comme nous l'avons fait pour connectWallet, nous devons importer cette fonction de notre fichier interact.js dans notre fichier Minter.js comme ceci :
1import { useEffect, useState } from "react"2import {3 connectWallet,4 getCurrentWalletConnected, //importer ici5} from "./utils/interact.js"Maintenant, nous l'appelons simplement dans notre fonction useEffect :
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)5}, [])Notez que nous utilisons la réponse de notre appel à getCurrentWalletConnected pour mettre à jour nos variables d'état walletAddress et status.
Une fois que vous avez ajouté ce code, essayez de rafraîchir votre fenêtre de navigateur. Le bouton devrait indiquer que vous êtes connecté et afficher un aperçu de l'adresse de votre portefeuille connecté, même après avoir été actualisé !
Implémenter addWalletListener
La dernière étape de la configuration de notre dApp de portefeuille consiste à mettre en place le listener de portefeuille afin que notre interface utilisateur soit mise à jour lorsque l'état de notre portefeuille change, par exemple lorsque l'utilisateur se déconnecte ou change de compte.
Dans votre fichier Minter.js, ajoutez une fonction addWalletListener qui ressemble à ce qui suit :
1function addWalletListener() {2 if (window.ethereum) {3 window.ethereum.on("accountsChanged", (accounts) => {4 if (accounts.length > 0) {5 setWallet(accounts[0])6 setStatus("👆🏽 Écrivez un message dans le champ de texte ci-dessus.")7 } else {8 setWallet("")9 setStatus("🦊 Connectez-vous à MetaMask en utilisant le bouton en haut à droite.")10 }11 })12 } else {13 setStatus(14 <p>15 {" "}16 🦊 <a target="_blank" href={`https://metamask.io/download`}>17 Vous devez installer MetaMask, un portefeuille Ethereum virtuel, dans votre navigateur.18 </a>19 </p>20 )21 }22}Afficher toutDécomposons rapidement ce qui se passe ici :
- Tout d'abord, notre fonction vérifie si
window.ethereumest activé (c'est-à-dire si MetaMask est installé).- Si ce n'est pas le cas, nous définissons simplement notre variable d'état
statussur une chaîne JSX qui invite l'utilisateur à installer MetaMask. - S'il est activé, nous configurons l'écouteur
window.ethereum.on("accountsChanged")à la ligne 3 qui écoute les changements d'état dans le portefeuille MetaMask, qui incluent le moment où l'utilisateur connecte un compte supplémentaire à la dapp, change de compte ou déconnecte un compte. S'il y a au moins un compte connecté, la variable d'étatwalletAddressest mise à jour en tant que premier compte dans le tableauaccountsrenvoyé par l'écouteur. Sinon,walletAddressest défini comme une chaîne vide.
- Si ce n'est pas le cas, nous définissons simplement notre variable d'état
Enfin, nous devons l'appeler dans notre fonction useEffect :
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)56 addWalletListener()7}, [])Et voilà ! Nous avons terminé la programmation de toutes les fonctionnalités de notre portefeuille ! Maintenant que notre portefeuille est configuré, regardons comment créer notre NFT !
NFT Metadonnées 101
Rappelez-vous que les métadonnées NFT dont nous venons de parler à l'étape 0 de ce tutoriel, donnent vie à un NFT, lui permettant d'avoir des propriétés, comme un actif numérique, un nom, une description et d'autres attributs.
Nous allons devoir configurer ces métadonnées en tant qu'objet JSON et les stocker, afin de pouvoir les transmettre en tant que paramètre tokenURI lors de l'appel de la fonction mintNFT de notre contrat intelligent.
Le texte des champs « Lien vers l'actif », « Nom » et « Description » comprendra les différentes propriétés des métadonnées de notre NFT. Nous allons formater ces métadonnées sous la forme d'un objet JSON, mais il existe plusieurs options pour le stockage de cet objet JSON :
- Nous pourrions la stocker sur la blockchain Ethereum, mais cela serait très coûteux.
- Nous pourrions le stocker sur un serveur centralisé, comme AWS ou Firebase. Mais cela irait à l'encontre de notre philosophie de décentralisation.
- Nous pourrions utiliser IPFS, un protocole décentralisé et un réseau peer-to-peer pour stocker et partager des données dans un système de fichiers distribué. Comme ce protocole est décentralisé et gratuit, c'est notre meilleure option !
Pour stocker nos métadonnées sur IPFS, nous utiliserons Pinata (opens in a new tab), une API et une boîte à outils IPFS pratiques. Dans l'étape suivante, nous vous expliquerons exactement comment faire !
Utiliser Pinata pour épingler vos métadonnées sur IPFS
Si vous n'avez pas de compte Pinata (opens in a new tab), créez un compte gratuit ici (opens in a new tab) et suivez les étapes pour vérifier votre e-mail et votre compte.
Créez votre clé API Pinata
Accédez à la page https://pinata.cloud/keys (opens in a new tab), puis sélectionnez le bouton « Nouvelle clé » en haut, activez le widget Admin et nommez votre clé.
Vous verrez ensuite une popup avec vos infos d'API. Assurez-vous de mettre cela dans un endroit sûr.
Maintenant que notre clé est configurée, ajoutons-la à notre projet pour que nous puissions l'utiliser.
Créer un fichier .env
Nous pouvons stocker en toute sécurité notre clé et notre secret Pinata dans un fichier d'environnement. Installons le paquetage dotenv (opens in a new tab) dans le répertoire de votre projet.
Ouvrez un nouvel onglet dans votre terminal (distinct de celui qui exécute l'hôte local) et assurez-vous que vous êtes dans le dossier minter-starter-files, puis exécutez la commande suivante dans votre terminal :
1npm install dotenv --saveEnsuite, créez un fichier .env dans le répertoire racine de votre minter-starter-files en saisissant ce qui suit sur votre ligne de commande :
1vim.envCela ouvrira votre fichier .env dans vim (un éditeur de texte). Pour l'enregistrer, appuyez sur « esc » + « : » + « q » sur votre clavier et dans cet ordre.
Ensuite, dans VSCode, accédez à votre fichier .env et ajoutez-y votre clé API et votre secret API Pinata, comme ceci :
1REACT_APP_PINATA_KEY = <pinata-api-key>2REACT_APP_PINATA_SECRET = <pinata-api-secret>Enregistrez le fichier et vous êtes prêt à commencer à écrire la fonction pour télécharger vos métadonnées JSON sur IPFS !
Implémenter pinJSONToIPFS
Heureusement pour nous, Pinata dispose d'une API spécialement conçue pour téléverser des données JSON vers IPFS (opens in a new tab) et d'un exemple pratique JavaScript avec axios que nous pouvons utiliser, avec quelques légères modifications.
Dans votre dossier utils, créons un autre fichier appelé pinata.js, puis importons notre secret et notre clé Pinata depuis le fichier .env comme ceci :
1require("dotenv").config()2const key = process.env.REACT_APP_PINATA_KEY3const secret = process.env.REACT_APP_PINATA_SECRETEnsuite, collez le code supplémentaire ci-dessous dans votre fichier pinata.js. Ne vous inquiétez pas, nous allons expliquer ce que tout cela signifie !
1require("dotenv").config()2const key = process.env.REACT_APP_PINATA_KEY3const secret = process.env.REACT_APP_PINATA_SECRET45const axios = require("axios")67export const pinJSONToIPFS = async (JSONBody) => {8 const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`9 //faire une requête POST axios à Pinata ⬇️10 return axios11 .post(url, JSONBody, {12 headers: {13 pinata_api_key: key,14 pinata_secret_api_key: secret,15 },16 })17 .then(function (response) {18 return {19 success: true,20 pinataUrl:21 "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash,22 }23 })24 .catch(function (error) {25 console.log(error)26 return {27 success: false,28 message: error.message,29 }30 })31}Afficher toutAlors, que fait ce code exactement ?
Tout d'abord, il importe axios (opens in a new tab), un client HTTP basé sur les promesses pour le navigateur et node.js, que nous utiliserons pour faire une requête à Pinata.
Ensuite, nous avons notre fonction asynchrone pinJSONToIPFS, qui prend un JSONBody en entrée et la clé et le secret de l'API Pinata dans son en-tête, le tout pour effectuer une requête POST à leur API pinJSONToIPFS.
- Si cette requête POST réussit, notre fonction renvoie un objet JSON avec le booléen
successà « true » et l'pinataUrloù nos métadonnées ont été épinglées. Nous utiliserons cettepinataUrlrenvoyée comme entréetokenURIpour la fonction de frappe de notre contrat intelligent. - Si cette requête POST échoue, notre fonction renvoie un objet JSON avec le booléen
successà « false » et une chaînemessagequi relaie notre erreur.
Comme pour les types de retour de notre fonction connectWallet, nous renvoyons des objets JSON afin de pouvoir utiliser leurs paramètres pour mettre à jour nos variables d'état et notre interface utilisateur.
Charger votre contrat intelligent
Maintenant que nous avons un moyen de téléverser nos métadonnées NFT sur IPFS via notre fonction pinJSONToIPFS, nous allons avoir besoin d'un moyen de charger une instance de notre contrat intelligent afin de pouvoir appeler sa fonction mintNFT.
Comme nous l'avons mentionné précédemment, dans ce tutoriel, nous utiliserons ce contrat intelligent de NFT existant (opens in a new tab) ; cependant, si vous souhaitez apprendre comment nous l'avons créé, ou en créer un vous-même, nous vous recommandons vivement de consulter notre autre tutoriel, "Comment créer un NFT." (opens in a new tab).
L'ABI du contrat
Si vous avez examiné attentivement nos fichiers, vous aurez remarqué que dans notre répertoire src, il y a un fichier contract-abi.json. Une ABI est nécessaire pour spécifier quelle fonction un contrat invoquera en s'assurant également que la fonction retournera des données dans le format que vous attendez.
Nous allons également avoir besoin d'une clé API Alchemy et de l'API Alchemy Web3 pour nous connecter à la blockchain Ethereum et charger notre contrat intelligent.
Créez votre clé API Alchemy
Si vous n'avez pas encore de compte Alchemy, inscrivez-vous gratuitement ici. (opens in a new tab)
Une fois votre compte Alchemy créé, vous pouvez générer une clé API en créant une application. Cela va nous permettre d'émettre des requêtes sur le réseau de test Ropsten.
Accédez à la page "Create App" dans votre Tableau de bord Alchemy, en survolant "Apps" dans la barre de navigation et en cliquant sur "Create App".
Nommez votre application. Ici nous avons choisi "My First NFT!", donnez une brève description, sélectionnez "Staging" pour l'environnement utilisé pour la comptabilité de votre application, et choisissez "Ropsten" pour votre réseau.
Cliquez sur « Create app », et voilà ! Votre application devrait apparaître dans le tableau ci-dessous.
Génial ! Maintenant que nous avons créé notre URL pour l'API HTTP Alchemy, copiez-la dans votre presse-papiers...
…et ensuite ajoutons-la à notre fichier .env. Dans l'ensemble, votre fichier .env devrait ressembler à ceci :
1REACT_APP_PINATA_KEY = <pinata-key>2REACT_APP_PINATA_SECRET = <pinata-secret>3REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/<alchemy-key>Maintenant que nous avons notre ABI de contrat et notre clé d'API Alchemy, nous sommes prêts à charger notre contrat intelligent en utilisant Alchemy Web3 (opens in a new tab).
Configurer votre point de terminaison et votre contrat Alchemy Web3
Tout d'abord, si vous ne l'avez pas déjà, vous devrez installer Alchemy Web3 (opens in a new tab) en naviguant vers le répertoire de base : nft-minter-tutorial dans le terminal :
1cd ..2npm install @alch/alchemy-web3Revenons maintenant à notre fichier interact.js. En haut du fichier, ajoutez le code suivant pour importer votre clé Alchemy à partir de votre fichier .env et configurez votre point de terminaison Alchemy Web3 :
1require("dotenv").config()2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")4const web3 = createAlchemyWeb3(alchemyKey)Alchemy Web3 (opens in a new tab) est un wrapper autour de Web3.js (opens in a new tab), qui fournit des méthodes API améliorées et d'autres avantages cruciaux pour vous faciliter la vie en tant que développeur web3. Il est conçu pour nécessiter une configuration minimale afin que vous puissiez commencer à l'utiliser immédiatement dans votre application !
Ensuite, ajoutons notre contrat ABI et l'adresse de notre contrat à notre fichier.
1require("dotenv").config()2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")4const web3 = createAlchemyWeb3(alchemyKey)56const contractABI = require("../contract-abi.json")7const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE"Une fois que nous avons les deux, nous sommes prêts à commencer à coder notre fonction de frappage !
Implémenter la fonction mintNFT
À l'intérieur de votre fichier interact.js, définissons notre fonction, mintNFT, qui, comme son nom l'indique, frappera notre NFT.
Parce que nous allons réaliser de nombreux appels asynchrones (à Pinata pour épingler nos métadonnées à IPFS, Alchemy Web3 pour charger notre contrat intelligent, et MetaMask pour signer nos transactions), notre fonction sera également asynchrone.
Les trois entrées de notre fonction seront l'url de notre actif numérique, le name et la description. Ajoutez la signature de fonction suivante sous la fonction connectWallet :
1export const mintNFT = async (url, name, description) => {}Gestion des erreurs de saisie
Naturellement, il est logique d'avoir une sorte de gestion des erreurs d'entrée au début de la fonction et ainsi, nous quitterons cette fonction si nos paramètres d'entrée ne sont pas corrects. Dans notre fonction, ajoutons le code suivant :
1export const mintNFT = async (url, name, description) => {2 //gestion des erreurs3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Veuillez vous assurer que tous les champs sont remplis avant la frappe.",7 }8 }9}Afficher toutEssentiellement, si l'un des paramètres d'entrée est une chaîne vide, nous renvoyons un objet JSON où le booléen success est à « false », et la chaîne status relaie que tous les champs de notre interface utilisateur doivent être remplis.
Téléverser les métadonnées sur IPFS
Une fois que nous savons que nos métadonnées sont formatées correctement, l'étape suivante consiste à les envelopper dans un objet JSON et à les téléverser sur IPFS via le pinJSONToIPFS que nous avons écrit !
Pour ce faire, nous devons d'abord importer la fonction pinJSONToIPFS dans notre fichier interact.js. Tout en haut du fichier interact.js, ajoutons :
1import { pinJSONToIPFS } from "./pinata.js"Rappelez-vous que pinJSONToIPFS prend un corps JSON. Ainsi, avant de l'appeler, nous allons devoir formater nos paramètres url, name et description dans un objet JSON.
Mettons à jour notre code pour créer un objet JSON appelé metadata, puis appelons pinJSONToIPFS avec ce paramètre metadata :
1export const mintNFT = async (url, name, description) => {2 //gestion des erreurs3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Veuillez vous assurer que tous les champs sont remplis avant la frappe.",7 }8 }910 //créer les métadonnées11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description1516 //faire un appel pinata17 const pinataResponse = await pinJSONToIPFS(metadata)18 if (!pinataResponse.success) {19 return {20 success: false,21 status: "😢 Une erreur s'est produite lors du téléversement de votre tokenURI.",22 }23 }24 const tokenURI = pinataResponse.pinataUrl25}Afficher toutNotez que nous stockons la réponse de notre appel à pinJSONToIPFS(metadata) dans l'objet pinataResponse. Ensuite, nous analysons cet objet pour vérifier les erreurs.
En cas d'erreur, nous renvoyons un objet JSON où le booléen success est à « false » et notre chaîne status relaie que notre appel a échoué. Sinon, nous extrayons la pinataURL de la pinataResponse et la stockons en tant que notre variable tokenURI.
Maintenant, il est temps de charger notre contrat intelligent en utilisant l'API Alchemy Web3 que nous avons initialisée en haut de notre fichier. Ajoutez la ligne de code suivante au bas de la fonction mintNFT pour définir le contrat dans la variable globale window.contract :
1window.contract = await new web3.eth.Contract(contractABI, contractAddress)La dernière chose à ajouter dans notre fonction mintNFT est notre transaction Ethereum :
1//configurer votre transaction Ethereum2const transactionParameters = {3 to: contractAddress, // Requis sauf lors des publications de contrats.4 from: window.ethereum.selectedAddress, // doit correspondre à l'adresse active de l'utilisateur.5 data: window.contract.methods6 .mintNFT(window.ethereum.selectedAddress, tokenURI)7 .encodeABI(), //faire appel au contrat intelligent NFT8}910//signer la transaction via MetaMask11try {12 const txHash = await window.ethereum.request({13 method: "eth_sendTransaction",14 params: [transactionParameters],15 })16 return {17 success: true,18 status:19 "✅ Consultez votre transaction sur Etherscan : https://ropsten.etherscan.io/tx/" +20 txHash,21 }22} catch (error) {23 return {24 success: false,25 status: "😥 Quelque chose s'est mal passé : " + error.message,26 }27}Afficher toutSi vous êtes déjà familier avec les transactions Ethereum, vous remarquerez que la structure est assez similaire à ce que vous avez déjà vu.
- Tout d'abord, nous configurons nos paramètres de transactions.
tospécifie l'adresse du destinataire (notre contrat intelligent)fromspécifie le signataire de la transaction (l'adresse connectée de l'utilisateur à MetaMask :window.ethereum.selectedAddress)datacontient l'appel à la méthodemintNFTde notre contrat intelligent, qui reçoit notretokenURIet l'adresse du portefeuille de l'utilisateur,window.ethereum.selectedAddress, comme entrées
- Ensuite, nous effectuons un appel en attente,
window.ethereum.request, où nous demandons à MetaMask de signer la transaction. Notez que, dans cette requête, nous spécifions notre méthode eth (eth_SentTransaction) et nous transmettons nostransactionParameters. À ce stade, MetaMask s'ouvrira dans le navigateur, et demandera à l'utilisateur de signer ou rejeter la transaction.- Si la transaction réussit, la fonction renverra un objet JSON où le booléen
successest à « true » et la chaînestatusinvite l'utilisateur à consulter Etherscan pour plus d'informations sur sa transaction. - Si la transaction échoue, la fonction renverra un objet JSON où le booléen
successest à « false », et la chaînestatusrelaie le message d'erreur.
- Si la transaction réussit, la fonction renverra un objet JSON où le booléen
Au total, notre fonction mintNFT devrait ressembler à ceci :
1export const mintNFT = async (url, name, description) => {2 //gestion des erreurs3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Veuillez vous assurer que tous les champs sont remplis avant la frappe.",7 }8 }910 //créer les métadonnées11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description1516 //requête d'épinglage pinata17 const pinataResponse = await pinJSONToIPFS(metadata)18 if (!pinataResponse.success) {19 return {20 success: false,21 status: "😢 Une erreur s'est produite lors du téléversement de votre tokenURI.",22 }23 }24 const tokenURI = pinataResponse.pinataUrl2526 //charger le contrat intelligent27 window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract();2829 //configurer votre transaction Ethereum30 const transactionParameters = {31 to: contractAddress, // Requis sauf lors des publications de contrats.32 from: window.ethereum.selectedAddress, // doit correspondre à l'adresse active de l'utilisateur.33 data: window.contract.methods34 .mintNFT(window.ethereum.selectedAddress, tokenURI)35 .encodeABI(), //faire appel au contrat intelligent NFT36 }3738 //signer la transaction via MetaMask39 try {40 const txHash = await window.ethereum.request({41 method: "eth_sendTransaction",42 params: [transactionParameters],43 })44 return {45 success: true,46 status:47 "✅ Consultez votre transaction sur Etherscan : https://ropsten.etherscan.io/tx/" +48 txHash,49 }50 } catch (error) {51 return {52 success: false,53 status: "😥 Quelque chose s'est mal passé : " + error.message,54 }55 }56}Afficher toutC'est une fonction géante ! Maintenant, nous devons simplement connecter notre fonction mintNFT à notre composant Minter.js...
Connecter mintNFT à notre frontend Minter.js
Ouvrez votre fichier Minter.js et mettez à jour la ligne import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js"; en haut pour :
1import {2 connectWallet,3 getCurrentWalletConnected,4 mintNFT,5} from "./utils/interact.js"Enfin, implémentez la fonction onMintPressed pour faire un appel en attente vers votre fonction mintNFT importée et mettre à jour la variable d'état status pour refléter si notre transaction a réussi ou a échoué :
1const onMintPressed = async () => {2 const { status } = await mintNFT(url, name, description)3 setStatus(status)4}Déployez votre NFT sur un site web en direct
Prêt à mettre en ligne votre projet pour que les utilisateurs puissent interagir avec ? Consultez ce tutoriel (opens in a new tab) pour déployer votre Minter sur un site web en direct.
Encore une dernière étape...
Prenez d'assaut le monde de la blockchain
Je plaisante, vous êtes arrivé à la fin du tutoriel !
Pour récapituler, en construisant un Minter de NFT, vous avez appris avec succès à :
- Vous connecter à MetaMask via votre projet en frontend
- Appeler les méthodes du contrat intelligent depuis votre interface
- Signer des transactions à l'aide de MetaMask
Vraisemblablement, vous aimeriez pouvoir montrer les NFT frappés via votre dapp dans votre portefeuille — alors n'oubliez pas de consulter notre tutoriel rapide Comment voir votre NFT dans votre portefeuille (opens in a new tab) !
Et, comme toujours, si vous avez des questions, nous sommes là pour vous aider dans le Discord d'Alchemy (opens in a new tab). Nous avons hâte de voir comment vous appliquez les concepts de ce tutoriel à vos futurs projets !
Dernière mise à jour de la page : 25 février 2026