Tutorial del Coniatore di NFT
Una delle più grandi sfide per gli sviluppatori provenienti da Web2 è comprendere come connettere il proprio smart contract a un progetto di frontend, e come interagire con esso.
Creando un coniatore di NFT, una semplice UI in cui è possibile inserire un link alla risorsa digitale, un titolo e una descrizione, imparerai come:
- Connetterti a MetaMask tramite il progetto del tuo frontend
- Chiamare i metodi dello smart contract dal tuo frontend
- Firmare le transazioni usando MetaMask
In questo tutorial, utilizzeremo React(opens in a new tab) come framework di frontend. Poiché questo tutorial è incentrato principalmente sullo sviluppo di Web3, non dedicheremo molto tempo ad analizzare i fondamenti di React. Al contrario, ci concentreremo sul portare funzionalità al nostro progetto.
Come prerequisito, dovresti avere conoscenze di base di React e sapere come funzionano i componenti, gli accessori, useState/useEffect e la chiamata delle funzioni di base. Se non hai mai sentito parlare di alcuno di questi termini prima d'ora, è consigliabile dare un'occhiata a questo tutorial d'introduzione a React(opens in a new tab). Per chi preferisce l'apprendimento visivo, consigliamo vivamente quest'eccellente serie di video Tutorial moderno e completo su React(opens in a new tab) di Net Ninja.
E se non lo hai già fatto, necessiterai decisamente di un conto di Alchemy, per completare questo tutorial, nonché per creare qualsiasi cosa sulla blockchain. Registra gratuitamente un conto,qui(opens in a new tab).
Iniziamo quindi!
Guida alla Creazione di NFT
Prima ancora d'iniziare ad esaminare qualsiasi codice, è importante comprendere come funziona la creazione di un NFT. Si articola in due fasi:
Pubblicare lo smart contract di un NFT sulla blockchain di Ethereum
La più grande differenza tra i due standard di smart contract di NFT è che ERC-1155 è uno standard multi-token e comprende funzionalità batch, mentre ERC-721 è uno standard a token singolo, supporta dunque solo il trasferimento di un token per volta.
Chiamare la funzione di conio
Solitamente, questa funzione di conio richiede di passare due variabili come parametri, prima recipient
, che specifica l'indirizzo che riceverà il tuo NFT appena coniato e poi il tokenURI
del NFT, una stringa che si risolve a un documento JSON che descrive i metadati del NFT.
I metadati di un NFT sono davvero ciò che lo porta in vita, consentendogli di avere proprietà, quali nome, descrizione, immagine (o altre risorse digitali) e altri attributi. Ecco un esempio di un tokenURI(opens in a new tab), contenente i metadati di un NFT.
In questo tutorial, ci concentreremo sulla parte 2: chiamare una funzione di conio dello smart contract del NFT esistente usando la nostra UI di React.
Ecco un link(opens in a new tab) allo smart contract del NFT dell'ERC-721 che chiameremo in questo tutorial. Se sei interessato a imparare come lo abbiamo creato, consigliamo vivamente di dare un'occhiata al nostro tutorial, "Come creare un NFT"(opens in a new tab).
Forte! Ora che sappiamo come funziona la creazione di un NFT, cloniamo i nostri file iniziali!
Clonare i file iniziali
Prima, vai al repository di GitHub nft-minter-tutorial(opens in a new tab) per ottenere i file iniziali per questo progetto. Clona questo repository nel tuo ambiente locale.
Quando apri questo repository nft-minter-tutorial
clonato, noterai che contiene due cartelle: minter-starter-files
e nft-minter
.
minter-starter-files
contiene i file iniziali (essenzialmente l'UI di React) per questo progetto. In questo tutorial, lavoreremo in questa cartella, mentre impari a dar vita a questa UI connettendola al tuo portafoglio di Ethereum e a uno smart contract di NFT.nft-minter
contiene l'intero tutorial completato e serve come riferimento se dovessi bloccarti.
Apri quindi la tua copia di minter-starter-files
nel tuo editor di codice e poi vai alla cartella src
.
Tutto il codice che scriveremo sarà sotto la cartella src
. Modificheremo il componente Minter.js
e scriveremo altri file in JavaScript per dare funzionalità al nostro progetto Web3.
Fase 2: dai un'occhiata ai nostri file iniziali
Prima di iniziare a programmare, è importante dare un'occhiata a ciò che è già disponibile nei file iniziali.
Metti in funzione il tuo progetto di React
Iniziamo eseguendo il progetto di React nel browser. La bellezza di React è che una volta eseguito il nostro progetto nel browser, ogni modifica che salviamo sarà aggiornata dal vivo nel browser.
Per mettere il progetto in funzione, vai alla cartella di root della cartella minter-starter-files
ed esegui npm install
nel terminale per installare le dipendenze del progetto:
cd minter-starter-filesnpm install
Una volta terminata l'installazione, esegui npm start
nel terminale:
npm start
Così facendo, dovrebbe aprirsi http://localhost:3000/ nel browser, dove vedrai il frontend per il nostro progetto. Dovrebbe consistere in 3 campi: un luogo per inserire un link alla tua risorsa NFT, uno per inserire il nome e uno per fornire una descrizione.
Se provi a cliccare i pulsanti "Connetti Portafoglio" o "Conia NFT", noterai che non funzionano, questo perché devi ancora programmarne la funzionalità! :)
Il componente Minter.js
NOTA: Assicurati di essere nella cartella minter-starter-files
e non nella cartella nft-minter
!
Torniamo alla cartella src
nell'editor e apriamo il file Minter.js
. È davvero importante comprendere tutto il contenuto di questo file, che è il componente principale di React su cui lavoreremo.
In cima al nostro file, abbiamo le nostre variabili di stato che aggiorneremo dopo eventi specifici.
1//State variables2const [walletAddress, setWallet] = useState("")3const [status, setStatus] = useState("")4const [name, setName] = useState("")5const [description, setDescription] = useState("")6const [url, setURL] = useState("")
Mai sentito parlare di variabili di stato di React o di hook di stato? Dai un'occhiata a questa(opens in a new tab) documentazione.
Ecco cosa rappresenta ognuna delle variabili:
walletAddress
- una stringa che memorizza l'indirizzo del portafoglio dell'utentestatus
- una stringa contenente un messaggio da mostrare in fondo all'UIname
- una stringa che memorizza il nome del NFTdescription
- una stringa che memorizza la descrizione del NFTurl
- una stringa che rappresenta un link alla risorsa digitale del NFT
Dopo le variabili di stato, vedrai tre funzioni non implementate: useEffect
, connectWalletPressed
e onMintPressed
. Noterai che tutte queste funzioni sono async
, perché al loro interno effettueremo chiamate asincrone all'API! I nomi sono indicativi delle loro funzionalità:
1useEffect(async () => {2 //TODO: implement3}, [])45const connectWalletPressed = async () => {6 //TODO: implement7}89const onMintPressed = async () => {10 //TODO: implement11}Mostra tutto
useEffect
(opens in a new tab) - questo è un hook di React chiamato dopo il rendering del tuo componente. Poiché in essa viene passato un array vuoto[]
(vedi la riga 3), sarà chiamata solo al primo rendering del componente. Qui chiameremo il listener del nostro portafoglio e un'altra funzione del portafoglio per aggiornare la nostra UI affinché rifletta se un portafoglio è già collegato.connectWalletPressed
- questa funzione sarà chiamata per connettere il portafoglio di MetaMask dell'utente alla nostra dapp.onMintPressed
- questa funzione sarà chiamata per coniare il NFT dell'utente.
Vicino alla fine di questo file, abbiamo l'UI del nostro componente. Se esamini attentamente questo codice, noterai che aggiorniamo le nostre variabili di stato url
, name
e description
, quando l'input nei relativi campi di testo cambia.
Vedrai anche che connectWalletPressed
e onMintPressed
vengono chiamate rispettivamente quando viene fatto clic sui pulsanti con ID mintButton
e walletButton
.
1//the UI of our component2return (3 <div className="Minter">4 <button id="walletButton" onClick={connectWalletPressed}>5 {walletAddress.length > 0 ? (6 "Connected: " +7 String(walletAddress).substring(0, 6) +8 "..." +9 String(walletAddress).substring(38)10 ) : (11 <span>Connect Wallet</span>12 )}13 </button>1415 <br></br>16 <h1 id="title">🧙♂️ Alchemy NFT Minter</h1>17 <p>18 Simply add your asset's link, name, and description, then press "Mint."19 </p>20 <form>21 <h2>🖼 Link to asset: </h2>22 <input23 type="text"24 placeholder="e.g. https://gateway.pinata.cloud/ipfs/<hash>"25 onChange={(event) => setURL(event.target.value)}26 />27 <h2>🤔 Name: </h2>28 <input29 type="text"30 placeholder="e.g. My first NFT!"31 onChange={(event) => setName(event.target.value)}32 />33 <h2>✍️ Description: </h2>34 <input35 type="text"36 placeholder="e.g. Even cooler than cryptokitties ;)"37 onChange={(event) => setDescription(event.target.value)}38 />39 </form>40 <button id="mintButton" onClick={onMintPressed}>41 Mint NFT42 </button>43 <p id="status">{status}</p>44 </div>45)Mostra tutto
Infine, vediamo dove viene aggiunto questo componente del Coniatore.
Se vai al file App.js
, che è il componente principale su React e che agisce come contenitore per tutti gli altri componenti, vedrai che il nostro componente del Coniatore è inserito alla riga 7.
In questo tutorial, modificheremo solo il file Minter.js
e aggiungeremo i file alla nostra cartella src
.
Ora che ci è chiaro con cosa stiamo lavorando, configuriamo il portafoglio di Ethereum!
Configura il tuo wallet Ethereum
Per poter interagire con il tuo smart contract, gli utenti dovranno connettere il proprio portafoglio di Ethereum alla tua dapp.
Scarica MetaMask
Per questo tutorial, utilizzeremo MetaMask, un portafoglio virtuale nel browser, utilizzato per gestire l'indirizzo del tuo conto di Ethereum. Se vuoi capire di più su come funzionano le transazioni su Ethereum, dai un'occhiata a questa pagina.
Puoi scaricare e creare gratuitamente un conto di MetaMask qui(opens in a new tab). Quando stai creando un conto, o se ne hai già uno, assicurati di passare alla "Rete di Prova di Ropsten" in alto a destra (così da non avere a che fare con denaro reale).
Aggiungere ether da un Faucet
Per coniare i nostri NFT (o firmare qualsiasi transazione sulla blockchain di Ethereum), avremo bisogno di qualche finto Eth. Per ottenere degli Eth puoi andare al faucet di Ropsten(opens in a new tab) e inserire l'indirizzo del tuo conto di Ropsten, poi cliccare “Invia Eth a Ropsten.” Poco dopo, dovresti vedere gli Eth nel tuo conto di MetaMask!
Controlla il tuo saldo
Per ricontrollare che ci sia il saldo, facciamo una richiesta eth_getBalance(opens in a new tab) usando lo strumento compositore di Alchemy(opens in a new tab). Questo restituirà l'importo di Eth nel tuo portafoglio. Dopo aver inserito l'indirizzo del tuo conto di MetaMask e aver cliccato "Invia Richiesta", dovresti visualizzare una risposta simile alla seguente:
1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}
NOTA: Questo risultato è in wei non in eth. Wei è usato come taglio più piccolo dell'ether. La conversione da wei a eth è: 1 eth = 10¹⁸ wei. Quindi se convertiamo 0xde0b6b3a7640000 in decimali, otteniamo 1*10¹⁸, pari a 1 eth.
Meno male! I nostri soldi finti ci sono tutti!
Connettere MetaMask alla UI
Ora che il nostro portafoglio di MetaMask è configurato, connettiamo la nostra dapp!
Poiché vogliamo prescrivere al paradigma del MVC(opens in a new tab), creeremo un file separato che contiene le nostre funzioni per gestire la logica, i dati e le regole della nostra dapp e poi passeremo tali funzioni al nostro frontend (il nostro componente Minter.js).
La funzione connectWallet
Per farlo, creiamo una nuova cartella chiamata utils
nella nostra cartella src
e aggiungiamo al suo interno un file chiamato interact.js
, che conterrà tutte le funzioni d'interazione del nostro portafoglio e del nostro smart contract.
Nel nostro file interact.js
, scriveremo una funzione connectWallet
, che poi importeremo e chiameremo nel nostro componente Minter.js
.
Nel tuo file interact.js
, aggiungi quanto segue
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: "👆🏽 Write a message in the text-field above.",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.html`}>26 You must install MetaMask, a virtual Ethereum wallet, in your27 browser.28 </a>29 </p>30 </span>31 ),32 }33 }34}Mostra tutto
Analizziamo cosa fa questo codice:
Per prima cosa, la nostra funzione verifica se window.ethereum
è abilitato nel browser.
window.ethereum
è un'API globale, iniettata da MetaMask e altri fornitori di portafogli, che consente ai siti web di richiedere i conti di Ethereum degli utenti. Se approvata, può leggere i dati dalle blockchain a cui è connesso l'utente e suggerire all'utente di firmare messaggi e transazioni. Dai un'occhiata alla documentazione di MetaMask(opens in a new tab) per ulteriori informazioni!
Se window.ethereum
non è presente, significa che MetaMask non è installato. Verrà quindi restituito un oggetto JSON in cui l'address
restituito è una stringa vuota e l'oggetto JSX di status
indica che l'utente deve installare MetaMask.
Gran parte delle funzioni che scriveremo restituiranno oggetti JSON che possiamo usare per aggiornare le nostre variabili di stato e l'UI.
Ora, se window.ethereum
è presente, le cose cominciano a farsi interessanti.
Utilizzando un ciclo try/catch, proveremo a connetterci a MetaMask chiamando [window.ethereum.request({ method: "eth_requestAccounts" });](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts)
. Chiamare questa funzione aprirà MetaMask nel browser, dove sarà richiesto all'utente di connettere il proprio portafoglio alla tua dapp.
- Se l'utente sceglie di connettersi,
method: "eth_requestAccounts"
restituirà un insieme contenente tutti gli indirizzi del conto dell'utente, connessi alla dapp. Nel complesso, la nostra funzioneconnectWallet
restituirà un oggetto JSON contenente il primoaddress
in questo array (vedi la riga 9) e un messaggio distatus
che richiede all'utente di scrivere un messaggio nello smart contract. - Se l'utente rifiuta la connessione, allora l'oggetto JSON conterrà una stringa vuota per l'
address
restituito e un messaggio distatus
che indica che l'utente ha rifiutato la connessione.
Aggiungi la funzione connectWallet al tuo componente UI Minter.js
Ora che abbiamo scritto questa funzione connectWallet
, connettiamola al nostro componente Minter.js.
.
Prima, dovremo importare la nostra funzione nel file Minter.js
, aggiungendo import { connectWallet } from "./utils/interact.js";
in cima al file Minter.js
. Le tue prime 11 righe di Minter.js
dovrebbero somigliare a questo:
1import { useEffect, useState } from "react";2import { connectWallet } from "./utils/interact.js";34const Minter = (props) => {56 //State variables7 const [walletAddress, setWallet] = useState("");8 const [status, setStatus] = useState("");9 const [name, setName] = useState("");10 const [description, setDescription] = useState("");11 const [url, setURL] = useState("");Mostra tutto
Poi, nella nostra funzione connectWalletPressed
, chiameremo la funzione connectWallet
importata, come quella che segue:
1const connectWalletPressed = async () => {2 const walletResponse = await connectWallet()3 setStatus(walletResponse.status)4 setWallet(walletResponse.address)5}
Nota come gran parte della nostra funzionalità è esterna al nostro componente Minter.js
dal file interact.js
? Questo perché stiamo seguendo il modello M-V-C!
In connectWalletPressed
, creiamo semplicemente una chiamata d'attesa alla nostra funzione connectWallet
importata e, usando la sua risposta, aggiorniamo le nostre variabili status
e walletAddress
tramite i loro hook di stato.
Ora, salviamo entrambi i file Minter.js
e interact.js
e testiamo la nostra UI.
Apri il browser su localhost:3000 e premi il pulsante "Connetti Portafoglio" in alto a destra alla pagina.
Se hai MetaMask installato, ti dovrebbe essere richiesto di connettere il tuo portafoglio alla tua dapp. Accetta l'invito a connetterti.
Dovresti vedere ora che il pulsante del portafoglio indica che l'indirizzo è connesso.
Prova quindi a ricaricare la pagina... questo è strano. Il nostro pulsante del portafoglio ci sta richiedendo di connetterci a MetaMask, anche se è già connesso...
Non preoccuparti! Possiamo risolverlo facilmente implementando una funzione chiamata getCurrentWalletConnected
, che verificherà se un indirizzo è già connesso alla nostra dapp e aggiornerà l'UI di conseguenza!
La funzione getCurrentWalletConnected
Nel file interact.js
, aggiungi la seguente funzione getCurrentWalletConnected
:
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: "👆🏽 Write a message in the text-field above.",11 }12 } else {13 return {14 address: "",15 status: "🦊 Connect to MetaMask using the top right button.",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.html`}>32 You must install MetaMask, a virtual Ethereum wallet, in your33 browser.34 </a>35 </p>36 </span>37 ),38 }39 }40}Mostra tutto
Questo codice è molto simile alla funzione connectWallet
che abbiamo scritto poco fa.
La differenza principale è che, invece di chiamare il metodo eth_requestAccounts
, che apre MetaMask perché l'utente connetta il proprio portafoglio, qui chiamiamo il metodo eth_accounts
che, semplicemente, restituisce un insieme contenente gli indirizzi di MetaMask correntemente connessi alla nostra dapp.
Per vedere questa funzione in azione, chiamiamola nella funzione useEffect
del nostro componente Minter.js
.
Come abbiamo fatto per connectWallet
, dobbiamo importare questa funzione dal file interact.js
al file Minter.js
, come segue:
1import { useEffect, useState } from "react"2import {3 connectWallet,4 getCurrentWalletConnected, //import here5} from "./utils/interact.js"
Ora, semplicemente, chiamiamola nella nostra funzione useEffect
:
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)5}, [])
Nota che stiamo usando la risposta alla nostra chiamata a getCurrentWalletConnected
per aggiornare le nostre variabili di stato walletAddress
e status
.
Una volta aggiunto questo codice, prova a ricaricare la nostra finestra del browser. Il pulsante dovrebbe dire che sei connesso e mostrare un'anteprima dell'indirizzo del tuo portafoglio connesso, anche dopo un refresh!
Implementare addWalletListener
Il passaggio finale della configurazione del portafoglio della nostra dapp è implementare l'ascoltatore del portafoglio, così che la nostra UI si aggiorni al cambiamento dello stato del nostro portafoglio, ad esempio, quando l'utente si disconnette o cambia conto.
Nel file Minter.js
, aggiungi una funzione addWalletListener
, simile a quanto segue:
1function addWalletListener() {2 if (window.ethereum) {3 window.ethereum.on("accountsChanged", (accounts) => {4 if (accounts.length > 0) {5 setWallet(accounts[0])6 setStatus("👆🏽 Write a message in the text-field above.")7 } else {8 setWallet("")9 setStatus("🦊 Connect to MetaMask using the top right button.")10 }11 })12 } else {13 setStatus(14 <p>15 {" "}16 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>17 You must install MetaMask, a virtual Ethereum wallet, in your browser.18 </a>19 </p>20 )21 }22}Mostra tutto
Esaminiamo rapidamente cosa sta succedendo qui:
- Per prima cosa, la nostra funzione verifica se
window.ethereum
è abilitata (cioè se MetaMask è installato).- Se non lo è, impostiamo semplicemente la nostra variabile di stato
status
a una stringa JSX che richiede all'utente di installare MetaMask. - Se è abilitato, configuriamo l'ascoltatore
window.ethereum.on("accountsChanged")
alla riga 3, affinché ascolti i cambiamenti di stato nel portafoglio di MetaMask, tra cui, quando l'utente connette un ulteriore conto alla dapp, cambia conto, o ne disconnette uno. Se è connesso almeno un conto, la variabile di statowalletAddress
è aggiornata come primo conto nell'insiemeaccounts
, restituito dall'ascoltatore. Altrimenti,walletAddress
è impostato come una stringa vuota.
- Se non lo è, impostiamo semplicemente la nostra variabile di stato
Infine, dobbiamo chiamarlo nella nostra funzione useEffect
:
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)56 addWalletListener()7}, [])
E voilà! Abbiamo completato la programmazione di tutte le funzionalità del nostro portafoglio! Ora che il nostro portafoglio è configurato, cerchiamo di capire come coniare il nostro NFT!
Guida di base ai Metadati del NFT
Ricorda quindi che i metadati del NFT di cui abbiamo appena parlato al Passaggio 0 di questo tutorial, portano in vita un NFT, consentendogli di avere proprietà quali una risorsa digitale, un nome, una descrizione e altri attributi.
Dovremo configurare questi metadati come un oggetto JSON e memorizzarli, quindi potremo passarli come parametro tokenURI
, chiamando la nostra funzione mintNFT
dello smart contract.
Il testo nei campi "Link to Asset", "Name", "Description" comprenderà le diverse proprietà dei metadati del nostro NFT. Formatteremo questi metadati come un oggetto JSON, ma esistono un paio di opzioni per dove possiamo memorizzare questo oggetto:
- Potremmo memorizzarlo sulla blockchain di Ethereum; ma farlo sarebbe molto costoso.
- Potremmo memorizzarlo su un server centralizzato, come AWS o Firebase. Ma questo sarebbe contrario alla nostra etica di decentralizzazione.
- Potremmo usare IPFS, un protocollo decentralizzato e rete peer-to-peer per memorizzare e condividere dati in un sistema di file distribuito. Poiché questo protocollo è decentralizzato e libero, è la nostra opzione preferita!
Per memorizzare i nostri metadati su IPFS, useremo Pinata(opens in a new tab), una comoda API e un toolkit per IPFS. Al prossimo passaggio, spiegheremo esattamente come farlo!
Utilizza Pinata per fissare i tuoi metadati su IPFS
Se non hai un conto di Pinata(opens in a new tab), registrane gratuitamente uno qui(opens in a new tab) e completa i passaggi per verificare la tua email e il tuo conto.
Crea la tua chiave API di Pinata
Vai alla pagina https://pinata.cloud/keys(opens in a new tab), quindi seleziona il pulsante "Nuova Chiave" in alto, abilita il widget Admin e assegna un nome alla tua chiave.
Ti sarà poi mostrato un popup con le informazioni sulla tua API. Assicurati di conservarle da qualche parte al sicuro.
Ora che la nostra chiave è configurata, aggiungiamola al nostro progetto così da poterla usare.
Crea un file .env
Possiamo memorizzare in sicurezza la nostra chiave e il codice segreto di Pinata in un file di ambiente. Installiamo il pacchetto dotenv(opens in a new tab) nella cartella del progetto.
Apri una nuova scheda nel terminale (separata da quella che sta eseguendo l'host locale) e assicurati di essere nella cartella minter-starter-files
, poi esegui il seguente comando nel terminale:
1npm install dotenv --save
Crea quindi un file .env
nella cartella di root del tuo minter-starter-files
inserendo quanto segue a riga di comando:
1vim.env
Questo aprirà il file .env
in vim (un editor di testo). Per salvarlo, clicca "esc" + ":" + "q" sulla tua tastiera in questa sequenza.
Poi, su VSCode, vai al file .env
e aggiungi al suo interno la tua chiave API di Pinata e il codice segreto dell'API, come segue:
1REACT_APP_PINATA_KEY = <pinata-api-key>2REACT_APP_PINATA_SECRET = <pinata-api-secret>
Salva il file: sei pronto ora per scrivere la funzione per caricare i tuoi metadati di JSON su IPFS!
Implementa pinJSONToIPFS
Per nostra fortuna, Pinata ha un'API specifica per caricare i dati JSON su IPFS(opens in a new tab) e un comodo JavaScript con esempio di axios che possiamo usare, con alcune lievi modifiche.
Nella cartella utils
creiamo un altro file denominato pinata.js
e poi importiamo il nostro codice segreto di Pinata e la chiave dal file .env, come segue:
1require("dotenv").config()2const key = process.env.REACT_APP_PINATA_KEY3const secret = process.env.REACT_APP_PINATA_SECRET
Incolla quindi il codice aggiuntivo seguente nel file pinata.js
. Non preoccuparti, analizzeremo per bene cosa significa!
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 //making axios POST request to 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}Mostra tutto
Quindi, cosa fa esattamente questo codice?
Prima di tutto, importa axios(opens in a new tab), un client HTTP basato su Promise per il browser e node.js, che useremo per creare una richiesta a Pinata.
Poi abbiamo la nostra funzione asincrona pinJSONToIPFS
, che prende un JSONBody
come input e la chiave API e il codice segreto di Pinata nell'intestazione, tutto per creare una richiesta di POST all'API pinJSONToIPFS
.
- Se questa richiesta di POST riesce, allora la nostra funzione restituisce un oggetto JSON con il booleano
success
impostato a true e ilpinataUrl
in cui i nostri metadati sono stati fissati. Useremo ilpinataUrl
restituito come l'input deltokenURI
alla funzione di conio del nostro smart contract. - Se questa richiesta di POST fallisce, allora la nostra funzione restituisce un oggetto JSON con il booleano
success
impostato false e una stringamessage
che comunica l'errore.
Come con i tipi restituiti dalla nostra funzione connectWallet
, stiamo restituendo oggetti JSON, così da poterne usare i parametri per aggiornare le nostre variabili di stato e l'UI.
Carica il tuo smart contract
Ora che abbiamo un modo per caricare i metadati del nostro NFT su IPFS tramite la nostra funzione pinJSONToIPFS
, avremo bisogno di un modo per caricare un'istanza del nostro smart contract, così da poterne chiamare la funzione mintNFT
.
Come menzionato prima, in questo tutorial useremo questo smart contract NFT esistente(opens in a new tab); se invece sei interessato a sapere come lo abbiamo creato, o se vuoi crearne uno tuo, consigliamo vivamente di dare un'occhiata all'altro nostro tutorial, "Come Creare un NFT."(opens in a new tab).
L'ABI del contratto
Se hai esaminato attentamente i nostri file, avrai notato che nella nostra cartella src
si trova un file contract-abi.json
. Un'ABI serve per specificare quale funzione invocherà un contratto, oltre che per garantire che la funzione restituirà i dati nel formato previsto.
Avremo anche bisogno di una chiave API di Alchemy e dell'API Alchemy Web3 per connetterci alla blockchain di Ethereum e caricare il nostro smart contract.
Crea la tua chiave API di Alchemy
Se non hai già un conto di Alchemy, registrane gratuitamente uno qui.(opens in a new tab)
Una volta creato un conto di Alchemy, puoi generare una chiave API creando un'app. Questo ci consentirà di effettuare richieste alla rete di prova di Ropsten.
Vai alla pagina “Crea App” nella tua dashboard di Alchemy passando su “App” nella barra di navigazione e cliccando “Crea App”.
Dai un nome alla tua app (noi abbiamo scelto “Il mio primo NFT!", aggiungi una breve descrizione, seleziona “Staging” come Ambiente) serve per la contabilità della tua app e scegli "Ropsten" come rete.
Clicca “Crea app” ed è tutto! La tua app dovrebbe apparire nella tabella seguente.
Fantastico, ora che abbiamo creato il nostro URL dell'API di Alchemy HTTP, copiamolo negli appunti...
…e poi aggiungiamolo al nostro file .env
. Nel complesso, il file .env dovrebbe somigliare a questo:
1REACT_APP_PINATA_KEY = <pinata-key>2REACT_APP_PINATA_SECRET = <pinata-secret>3REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/<alchemy-key>
Ora che abbiamo l'ABI del nostro contratto e la nostra chiave API di Alchemy, siamo pronti a caricare il nostro smart contract usando Alchemy Web3(opens in a new tab).
Configura l'endpoint e il contratto di Web3 di Alchemy
Prima di tutto, se non lo hai già fatto, dovrai installare Alchemy Web3(opens in a new tab) navigando alla cartella home: nft-minter-tutorial
nel terminale:
1cd ..2npm install @alch/alchemy-web3
Torniamo quindi al nostro file interact.js
. In cima al file, aggiungi il seguente codice per importare la tua chiave di Alchemy dal file .env e configurare il tuo endpoint di 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) è un wrapper intorno aWeb3.js(opens in a new tab) che fornisce metodi API migliorati e altri benefici fondamentale per semplificare la tua vita a uno sviluppatore web3. È progettato per richiedere una configurazione minima, così da poter iniziare a usarlo immediatamente nella tua app!
In seguito, aggiungiamo l'ABI del nostro contratto e l'indirizzo del contratto al nostro file.
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"
Una volta che abbiamo entrambi, siamo pronti a iniziare a programmare la nostra funzione di conio!
Implementa la funzione mintNFT
Nel file interact.js
, definiamo la nostra funzione, mintNFT
, che conierà il nostro omonimo NFT.
Poiché effettueremo numerose chiamate asincrone (a Pinata per fissare i nostri metadati su IPFS, a Alchemy Web3 per caricare il nostro smart contract e a MetaMask per firmare le nostre transazioni), anche la nostra funzione sarà asincrona.
I tre input alla nostra funzione saranno l'url
della nostra risorsa digitale, il name
e la description
. Aggiungi la seguente firma della funzione sotto la funzione connectWallet
:
1export const mintNFT = async (url, name, description) => {}
Gestione degli errori d'input
Naturalmente, è utile avere una certa gestione degli errori di input all'inizio della funzione, uscendo dalla funzione se i nostri parametri di input sono errati. Nella nostra funzione, aggiungiamo il seguente codice:
1export const mintNFT = async (url, name, description) => {2 //error handling3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Please make sure all fields are completed before minting.",7 }8 }9}Mostra tutto
Essenzialmente, se uno qualsiasi dei parametri d'input è una stringa vuota, restituiamo un oggetto JSON in cui il booleano success
è false e la stringa status
indica che tutti i campi nella nostra UI devono esser completi.
Carica i metadati su IPFS
Una volta che sappiamo che i nostri metadati sono correttamente formattati, il prossimo passaggio è avvolgerli in un oggetto JSON e caricarli su IPFS tramite il pinJSONToIPFS
che abbiamo scritto!
Per farlo, prima dobbiamo importare la funzione pinJSONToIPFS
nel nostro file interact.js
. In cima al interact.js
, aggiungiamo:
1import { pinJSONToIPFS } from "./pinata.js"
Ricorda che pinJSONToIPFS
riceve in un body JSON. Quindi, prima di effettuare una chiamata a esso, dovremo formattare i nostri parametri url
, name
e description
in un oggetto JSON.
Aggiorniamo il nostro codice per creare un oggetto JSON chiamato metadata
e poi effettuiamo una chiamata a pinJSONToIPFS
con questo parametro metadata
:
1export const mintNFT = async (url, name, description) => {2 //error handling3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Please make sure all fields are completed before minting.",7 }8 }910 //make metadata11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description1516 //make pinata call17 const pinataResponse = await pinJSONToIPFS(metadata)18 if (!pinataResponse.success) {19 return {20 success: false,21 status: "😢 Something went wrong while uploading your tokenURI.",22 }23 }24 const tokenURI = pinataResponse.pinataUrl25}Mostra tutto
Nota che memorizziamo la risposta della nostra chiamata a pinJSONToIPFS(metadata)
nell'oggetto pinataResponse
. Analizziamo quindi questo oggetto alla ricerca di eventuali errori.
Se è presente un errore, restituiamo un oggetto JSON in cui il booleano success
è impostato a false e la nostra stringa status
indica che la nostra chiamata non è andata a buon fine. Altrimenti, estraiamo pinataURL
dal pinataResponse
e lo memorizziamo come la nostra variabile tokenURI
.
È arrivato il momento di caricare il nostro smart contract usando l'API Alchemy Web3 che abbiamo inizializzato in cima al nostro file. Aggiungi la seguente riga di codice in fondo alla funzione mintNFT
per impostare il contratto alla variabile globale window.contract
:
1window.contract = await new web3.eth.Contract(contractABI, contractAddress)
L'ultima cosa da aggiungere alla nostra funzione mintNFT
è la nostra transazione di Ethereum:
1//set up your Ethereum transaction2const transactionParameters = {3 to: contractAddress, // Required except during contract publications.4 from: window.ethereum.selectedAddress, // must match user's active address.5 data: window.contract.methods6 .mintNFT(window.ethereum.selectedAddress, tokenURI)7 .encodeABI(), //make call to NFT smart contract8}910//sign the 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 "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" +20 txHash,21 }22} catch (error) {23 return {24 success: false,25 status: "😥 Something went wrong: " + error.message,26 }27}Mostra tutto
Se conosci già le transazioni di Ethereum, noterai che la struttura è abbastanza simile a quella che hai visto.
- Prima, configuriamo i parametri delle nostre transazioni.
to
specifica l'indirizzo del destinatario (il nostro smart contract)from
specifica il firmatario della transazione (l'indirizzo dell'utente connesso a MetaMask:window.ethereum.selectedAddress
)data
contiene la chiamata al metodomintNFT
del nostro smart contract, che riceve come input il nostrotokenURI
e l'indirizzo del portafoglio dell'utente,window.ethereum.selectedAddress
.
- Creiamo quindi una chiamata d'attesa,
window.ethereum.request,
in cui chiediamo a MetaMask di firmare la transazione. Nota che, in questa richiesta, stiamo specificando il nostro metodo eth (eth_SentTransaction) e passando il nostrotransactionParameters
. A questo punto, MetaMask si aprirà nel browser e richiederà all'utente di firmare o rifiutare la transazione.- Se la transazione va a buon fine, la funzione restituirà un oggetto JSON in cui il booleano
success
è impostato a true e la stringastatus
richiede all'utente di controllare Etherscan per ulteriori informazioni sulla sua transazione. - Se la transazione non va a buon fine, la funzione restituirà un oggetto JSON in cui il booleano
success
è impostato a false e la stringastatus
trasmette il messaggio d'errore.
- Se la transazione va a buon fine, la funzione restituirà un oggetto JSON in cui il booleano
Nel complesso, la nostra funzione mintNFT
dovrebbe somigliare a questa:
1export const mintNFT = async (url, name, description) => {2 //error handling3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Please make sure all fields are completed before minting.",7 }8 }910 //make metadata11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description1516 //pinata pin request17 const pinataResponse = await pinJSONToIPFS(metadata)18 if (!pinataResponse.success) {19 return {20 success: false,21 status: "😢 Something went wrong while uploading your tokenURI.",22 }23 }24 const tokenURI = pinataResponse.pinataUrl2526 //load smart contract27 window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract();2829 //set up your Ethereum transaction30 const transactionParameters = {31 to: contractAddress, // Required except during contract publications.32 from: window.ethereum.selectedAddress, // must match user's active address.33 data: window.contract.methods34 .mintNFT(window.ethereum.selectedAddress, tokenURI)35 .encodeABI(), //make call to NFT smart contract36 }3738 //sign 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 "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" +48 txHash,49 }50 } catch (error) {51 return {52 success: false,53 status: "😥 Something went wrong: " + error.message,54 }55 }56}Mostra tutto
Questa è una funzione gigante! Ora, dobbiamo solo connettere la nostra funzione mintNFT
al nostro componente Minter.js
...
Connetti mintNFT al nostro frontend di Minter.js
Apri il file Minter.js
e aggiorna la riga import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js";
in alto affinché sia:
1import {2 connectWallet,3 getCurrentWalletConnected,4 mintNFT,5} from "./utils/interact.js"
Infine, implementa la funzione onMintPressed
per effettuare la chiamata d'attesa alla tua funzione mintNFT
importata e aggiornare la variabile di stato status
affinché rifletta se la nostra transazione è andata o meno a buon fine:
1const onMintPressed = async () => {2 const { status } = await mintNFT(url, name, description)3 setStatus(status)4}
Distribuisci il tuo NFT a un sito web live
Pronto a portare in vita il tuo progetto affinché gli utenti vi interagiscano? Dai un'occhiata a questo tutorial(opens in a new tab) per distribuire il tuo Coniatore su un sito web live.
Un ultimo passaggio...
Prendi d'assalto il mondo della blockchain
Stiamo scherzando, sei arrivato alla fine del tutorial!
Per ricapitolare, creando un coniatore di NFT, hai imparato correttamente come:
- Connetterti a MetaMask tramite il progetto del tuo frontend
- Chiamare i metodi dello smart contract dal tuo frontend
- Firmare le transazioni usando MetaMask
Molto probabilmente vorrai mostrare gli NFT coniati tramite la tua dapp nel tuo portafoglio, dai quindi un'occhiata al nostro rapido tutorial Come visualizzare il tuo NFT nel tuo Portafoglio(opens in a new tab)!
E, come sempre, se hai qualsiasi domanda, siamo qui per aiutare sul Discord di Alchemy(opens in a new tab). Non vediamo l'ora di vedere come applicherai i concetti di questo tutorial ai tuoi progetti futuri!
Ultima modifica: @wackerow(opens in a new tab), 7 maggio 2024