Vai al contenuto principale

Tutorial sul minter di NFT

Solidity
NFT
Alchemy
contratti intelligenti
frontend
Pinata
erc-721
Intermedio
smudgil
6 ottobre 2021
29 minuti di lettura

Una delle sfide più grandi per gli sviluppatori provenienti da un background Web2 è capire come collegare il proprio contratto intelligente a un progetto frontend e interagirvi.

Costruendo un minter di NFT — una semplice interfaccia utente in cui puoi inserire un link alla tua risorsa digitale, un titolo e una descrizione — imparerai come:

  • Connetterti a MetaMask tramite il tuo progetto frontend
  • Chiamare i metodi del contratto intelligente dal tuo frontend
  • Firmare le transazioni usando MetaMask

In questo tutorial, utilizzeremo React (opens in a new tab) come nostro framework frontend. Poiché questo tutorial è incentrato principalmente sullo sviluppo Web3, non dedicheremo molto tempo ad analizzare i fondamenti di React. Ci concentreremo invece sull'aggiunta di funzionalità al nostro progetto.

Come prerequisito, dovresti avere una comprensione di livello base di React: sapere come funzionano i componenti, le prop, useState/useEffect e le chiamate di funzioni di base. Se non hai mai sentito nessuno di questi termini prima, potresti voler dare un'occhiata a questo Tutorial introduttivo a React (opens in a new tab). Per chi apprende in modo più visivo, consigliamo vivamente questa eccellente serie di video Full Modern React Tutorial (opens in a new tab) di Net Ninja.

E se non lo hai già fatto, avrai sicuramente bisogno di un account Alchemy per completare questo tutorial e per costruire qualsiasi cosa sulla blockchain. Registrati per un account gratuito qui (opens in a new tab).

Senza ulteriori indugi, iniziamo!

Creare NFT 101

Prima ancora di iniziare a guardare il codice, è importante capire come funziona la creazione di un NFT. Prevede due passaggi:

Pubblicare un contratto intelligente per NFT sulla blockchain di Ethereum

La più grande differenza tra i due standard di contratti intelligenti per NFT è che l'ERC-1155 è uno standard multi-token e include funzionalità in batch, mentre l'ERC-721 è uno standard a token singolo e pertanto supporta solo il trasferimento di un token alla volta.

Chiamare la funzione per coniare

Di solito, questa funzione per coniare richiede di passare due variabili come parametri: primo, il recipient, che specifica l'indirizzo che riceverà il tuo NFT appena coniato, e secondo, il tokenURI dell'NFT, una stringa che si risolve in un documento JSON che descrive i metadati dell'NFT.

I metadati di un NFT sono in realtà ciò che gli dà vita, consentendogli di avere proprietà, come un nome, una descrizione, un'immagine (o una diversa risorsa digitale) e altri attributi. Ecco un esempio di tokenURI (opens in a new tab), che contiene i metadati di un NFT.

In questo tutorial, ci concentreremo sulla parte 2, chiamando la funzione per coniare di un contratto intelligente di un NFT esistente utilizzando la nostra interfaccia utente React.

Ecco un link (opens in a new tab) al contratto intelligente dell'NFT ERC-721 che chiameremo in questo tutorial. Se desideri imparare come lo abbiamo realizzato, ti consigliamo vivamente di dare un'occhiata al nostro altro tutorial, "Come creare un NFT" (opens in a new tab).

Fantastico, ora che abbiamo capito come funziona la creazione di un NFT, cloniamo i nostri file di partenza!

Clonare i file di partenza

Per prima cosa, vai al repository GitHub nft-minter-tutorial (opens in a new tab) per ottenere i file di partenza 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 di partenza (essenzialmente l'interfaccia utente React) per questo progetto. In questo tutorial, lavoreremo in questa directory, mentre impari come dare vita a questa interfaccia utente collegandola al tuo portafoglio Ethereum e a un contratto intelligente per NFT.
  • nft-minter contiene l'intero tutorial completato ed è lì per te come riferimento se rimani bloccato.

Successivamente, apri la tua copia di minter-starter-files nel tuo editor di codice, e poi naviga nella tua cartella src.

Tutto il codice che scriveremo risiederà nella cartella src. Modificheremo il componente Minter.js e scriveremo file javascript aggiuntivi per dare al nostro progetto funzionalità Web3.

Passaggio 2: Dai un'occhiata ai nostri file di partenza

Prima di iniziare a programmare, è importante dare un'occhiata a ciò che ci è già stato fornito nei file di partenza.

Avviare il tuo progetto react

Iniziamo eseguendo il progetto React nel nostro browser. Il bello di React è che una volta che abbiamo il nostro progetto in esecuzione nel browser, qualsiasi modifica salvata verrà aggiornata in tempo reale nel browser.

Per avviare il progetto, naviga nella directory principale della cartella minter-starter-files ed esegui npm install nel tuo terminale per installare le dipendenze del progetto:

cd minter-starter-files
npm install

Una volta terminata l'installazione, esegui npm start nel tuo terminale:

npm start

Così facendo dovrebbe aprirsi http://localhost:3000/ (opens in a new tab) nel tuo browser, dove vedrai il frontend per il nostro progetto. Dovrebbe consistere in 3 campi: un posto dove inserire un link alla risorsa del tuo NFT, inserire il nome del tuo NFT e fornire una descrizione.

Se provi a cliccare sui pulsanti "Connect Wallet" o "Mint NFT", noterai che non funzionano: questo perché dobbiamo ancora programmare la loro funzionalità! :)

Il componente Minter.js

NOTA: Assicurati di essere nella cartella minter-starter-files e non nella cartella nft-minter!

Torniamo nella cartella src nel nostro editor e apriamo il file Minter.js. È importantissimo comprendere tutto in questo file, poiché è il componente React principale su cui lavoreremo.

All'inizio di questo file, abbiamo le nostre variabili di stato che aggiorneremo dopo eventi specifici.

1// Variabili di stato
2const [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 o hook di stato di React? Dai un'occhiata a questa (opens in a new tab) documentazione.

Ecco cosa rappresenta ciascuna delle variabili:

  • walletAddress - una stringa che memorizza l'indirizzo del portafoglio dell'utente
  • status - una stringa che contiene un messaggio da visualizzare nella parte inferiore dell'interfaccia utente
  • name - una stringa che memorizza il nome dell'NFT
  • description - una stringa che memorizza la descrizione dell'NFT
  • url - una stringa che è un link alla risorsa digitale dell'NFT

Dopo le variabili di stato, vedrai tre funzioni non implementate: useEffect, connectWalletPressed e onMintPressed. Noterai che tutte queste funzioni sono async, questo perché effettueremo chiamate API asincrone al loro interno! I loro nomi sono eponimi delle loro funzionalità:

1useEffect(async () => {
2 // TODO: implementare
3}, [])
4
5const connectWalletPressed = async () => {
6 // TODO: implementare
7}
8
9const onMintPressed = async () => {
10 // TODO: implementare
11}
  • useEffect (opens in a new tab) - questo è un hook di React che viene chiamato dopo il rendering del tuo componente. Poiché gli viene passata una prop array vuota [] (vedi riga 3), verrà chiamato solo al primo rendering del componente. Qui chiameremo il nostro listener del portafoglio e un'altra funzione del portafoglio per aggiornare la nostra interfaccia utente in modo da riflettere se un portafoglio è già connesso.
  • connectWalletPressed - questa funzione verrà chiamata per connettere il portafoglio MetaMask dell'utente alla nostra dApp.
  • onMintPressed - questa funzione verrà chiamata per coniare l'NFT dell'utente.

Verso la fine di questo file, abbiamo l'interfaccia utente del nostro componente. Se esamini attentamente questo codice, noterai che aggiorniamo le nostre variabili di stato url, name e description quando cambia l'input nei rispettivi campi di testo.

Vedrai anche che connectWalletPressed e onMintPressed vengono chiamate quando si fa clic rispettivamente sui pulsanti con ID mintButton e walletButton.

1// la UI del nostro componente
2return (
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>
14
15 <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 <input
23 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 <input
29 type="text"
30 placeholder="e.g., My first NFT!"
31 onChange={(event) => setName(event.target.value)}
32 />
33 <h2>✍️ Description: </h2>
34 <input
35 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 NFT
42 </button>
43 <p id="status">{status}</p>
44</div>
45)

Infine, vediamo dove viene aggiunto questo componente Minter.

Se vai al file App.js, che è il componente principale in React che funge da contenitore per tutti gli altri componenti, vedrai che il nostro componente Minter è iniettato alla riga 7.

In questo tutorial, modificheremo solo il file Minter.js e aggiungeremo file nella nostra cartella src.

Ora che abbiamo capito con cosa stiamo lavorando, configuriamo il nostro portafoglio Ethereum!

Configurare il tuo portafoglio Ethereum

Affinché gli utenti possano interagire con il tuo contratto intelligente, dovranno connettere il loro portafoglio Ethereum alla tua dApp.

Scaricare MetaMask

Per questo tutorial, useremo MetaMask, un portafoglio virtuale nel browser utilizzato per gestire l'indirizzo del tuo account Ethereum. Se vuoi capire meglio come funzionano le transazioni su Ethereum, dai un'occhiata a questa pagina.

Puoi scaricare e creare un account MetaMask gratuitamente qui (opens in a new tab). Quando crei un account, o se ne hai già uno, assicurati di passare alla "Rete di test Ropsten" in alto a destra (in modo da non avere a che fare con denaro reale).

Aggiungere ether da un rubinetto

Per coniare i nostri NFT (o firmare qualsiasi transazione sulla blockchain di Ethereum), avremo bisogno di alcuni ETH finti. Per ottenere ETH puoi andare al rubinetto Ropsten (opens in a new tab) e inserire l'indirizzo del tuo account Ropsten, quindi cliccare su "Send Ropsten Eth". Dovresti vedere gli ETH nel tuo account MetaMask poco dopo!

Controllare il tuo saldo

Per verificare che il nostro saldo sia presente, facciamo una richiesta eth_getBalance (opens in a new tab) utilizzando lo strumento composer di Alchemy (opens in a new tab). Questo restituirà la quantità di ETH nel nostro portafoglio. Dopo aver inserito l'indirizzo del tuo account MetaMask e cliccato su "Send Request", dovresti vedere una risposta come questa:

1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}

NOTA: Questo risultato è in wei, non in eth. Il wei è utilizzato come la più piccola denominazione di ether. La conversione da wei a eth è: 1 eth = 10¹⁸ wei. Quindi, se convertiamo 0xde0b6b3a7640000 in decimale, otteniamo 1*10¹⁸ che equivale a 1 eth.

Fiuu! I nostri soldi finti ci sono tutti!

Connettere MetaMask alla tua interfaccia utente

Ora che il nostro portafoglio MetaMask è configurato, connettiamo la nostra dApp ad esso!

Poiché vogliamo attenerci al paradigma 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, per poi passare quelle funzioni al nostro frontend (il nostro componente Minter.js).

La funzione connectWallet

Per farlo, creiamo una nuova cartella chiamata utils nella tua directory src e aggiungiamo un file chiamato interact.js al suo interno, che conterrà tutte le nostre funzioni di interazione con il portafoglio e il contratto intelligente.

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 obj
12 } 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 You must install MetaMask, a virtual Ethereum wallet, in your
27 browser.
28 </a>
29 </p>
30 </span>
31 ),
32 }
33 }
34}

Analizziamo cosa fa questo codice:

Per prima cosa, la nostra funzione controlla se window.ethereum è abilitato nel tuo browser.

window.ethereum è un'API globale iniettata da MetaMask e da altri provider di portafogli che consente ai siti web di richiedere gli account Ethereum degli utenti. Se approvata, può leggere i dati dalle blockchain a cui l'utente è connesso e suggerire all'utente di firmare messaggi e transazioni. Dai un'occhiata alla documentazione di MetaMask (opens in a new tab) per maggiori informazioni!

Se window.ethereum non è presente, significa che MetaMask non è installato. Ciò si traduce nella restituzione di un oggetto JSON, in cui l'address restituito è una stringa vuota e l'oggetto JSX status comunica che l'utente deve installare MetaMask.

La maggior parte delle funzioni che scriveremo restituirà oggetti JSON che possiamo usare per aggiornare le nostre variabili di stato e l'interfaccia utente.

Ora, se window.ethereum è presente, è qui che le cose si fanno interessanti.

Utilizzando un blocco try/catch, proveremo a connetterci a MetaMask chiamando window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). La chiamata a questa funzione aprirà MetaMask nel browser, per cui all'utente verrà richiesto di connettere il proprio portafoglio alla tua dApp.

  • Se l'utente sceglie di connettersi, method: "eth_requestAccounts" restituirà un array che contiene tutti gli indirizzi degli account dell'utente connessi alla dApp. Nel complesso, la nostra funzione connectWallet restituirà un oggetto JSON che contiene il primo address in questo array (vedi riga 9) e un messaggio di status che invita l'utente a scrivere un messaggio al contratto intelligente.
  • Se l'utente rifiuta la connessione, l'oggetto JSON conterrà una stringa vuota per l'address restituito e un messaggio di status che riflette il fatto che l'utente ha rifiutato la connessione.

Aggiungere la funzione connectWallet al tuo componente dell'interfaccia utente Minter.js

Ora che abbiamo scritto questa funzione connectWallet, connettiamola al nostro componente Minter.js..

Per prima cosa, dovremo importare la nostra funzione nel nostro file Minter.js aggiungendo import { connectWallet } from "./utils/interact.js"; all'inizio del file Minter.js. Le tue prime 11 righe di Minter.js dovrebbero ora apparire così:

1import { useEffect, useState } from "react";
2import { connectWallet } from "./utils/interact.js";
3
4const Minter = (props) => {
5
6 // Variabili di stato
7 const [walletAddress, setWallet] = useState("");
8 const [status, setStatus] = useState("");
9 const [name, setName] = useState("");
10 const [description, setDescription] = useState("");
11 const [url, setURL] = useState("");

Quindi, all'interno della nostra funzione connectWalletPressed, chiameremo la nostra funzione connectWallet importata, in questo modo:

1const connectWalletPressed = async () => {
2 const walletResponse = await connectWallet()
3 setStatus(walletResponse.status)
4 setWallet(walletResponse.address)
5}

Noti come la maggior parte delle nostre funzionalità sia astratta dal nostro componente Minter.js nel file interact.js? Questo per rispettare il paradigma M-V-C!

In connectWalletPressed, effettuiamo semplicemente una chiamata await alla nostra funzione connectWallet importata e, utilizzando 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 interfaccia utente finora.

Apri il tuo browser su localhost:3000 e premi il pulsante "Connect Wallet" in alto a destra nella pagina.

Se hai installato MetaMask, ti dovrebbe essere richiesto di connettere il tuo portafoglio alla tua dApp. Accetta l'invito a connetterti.

Dovresti vedere che il pulsante del portafoglio ora riflette che il tuo indirizzo è connesso.

Successivamente, prova ad aggiornare la pagina... questo è strano. Il nostro pulsante del portafoglio ci chiede di connettere MetaMask, anche se è già connesso...

Non preoccuparti però! Possiamo risolverlo facilmente implementando una funzione chiamata getCurrentWalletConnected, che controllerà se un indirizzo è già connesso alla nostra dApp e aggiornerà la nostra interfaccia utente di conseguenza!

La funzione getCurrentWalletConnected

Nel tuo 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`}>
32 You must install MetaMask, a virtual Ethereum wallet, in your
33 browser.
34 </a>
35 </p>
36 </span>
37 ),
38 }
39 }
40}

Questo codice è molto simile alla funzione connectWallet che abbiamo appena scritto in precedenza.

La differenza principale è che invece di chiamare il metodo eth_requestAccounts, che apre MetaMask affinché l'utente connetta il proprio portafoglio, qui chiamiamo il metodo eth_accounts, che restituisce semplicemente un array contenente gli indirizzi MetaMask attualmente 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 nostro file interact.js nel nostro file Minter.js in questo modo:

1import { useEffect, useState } from "react"
2import {
3 connectWallet,
4 getCurrentWalletConnected, // importa qui
5} from "./utils/interact.js"

Ora, la chiamiamo semplicemente nella nostra funzione useEffect:

1useEffect(async () => {
2 const { address, status } = await getCurrentWalletConnected()
3 setWallet(address)
4 setStatus(status)
5}, [])

Nota, usiamo la risposta della nostra chiamata a getCurrentWalletConnected per aggiornare le nostre variabili di stato walletAddress e status.

Una volta aggiunto questo codice, prova ad aggiornare la finestra del browser. Il pulsante dovrebbe dire che sei connesso e mostrare un'anteprima dell'indirizzo del tuo portafoglio connesso, anche dopo l'aggiornamento!

Implementare addWalletListener

Il passaggio finale nella configurazione del portafoglio della nostra dApp è l'implementazione del listener del portafoglio in modo che la nostra interfaccia utente si aggiorni quando lo stato del nostro portafoglio cambia, ad esempio quando l'utente si disconnette o cambia account.

Nel tuo file Minter.js, aggiungi una funzione addWalletListener che assomigli alla seguente:

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`}>
17 You must install MetaMask, a virtual Ethereum wallet, in your browser.
18 </a>
19 </p>
20 )
21 }
22}

Analizziamo rapidamente cosa sta succedendo qui:

  • Per prima cosa, la nostra funzione controlla se window.ethereum è abilitato (cioè, MetaMask è installato).
    • Se non lo è, impostiamo semplicemente la nostra variabile di stato status su una stringa JSX che invita l'utente a installare MetaMask.
    • Se è abilitato, impostiamo il listener window.ethereum.on("accountsChanged") alla riga 3 che ascolta i cambiamenti di stato nel portafoglio MetaMask, che includono quando l'utente connette un account aggiuntivo alla dApp, cambia account o disconnette un account. Se c'è almeno un account connesso, la variabile di stato walletAddress viene aggiornata come il primo account nell'array accounts restituito dal listener. Altrimenti, walletAddress viene impostato come una stringa vuota.

Infine, dobbiamo chiamarla nella nostra funzione useEffect:

1useEffect(async () => {
2 const { address, status } = await getCurrentWalletConnected()
3 setWallet(address)
4 setStatus(status)
5
6 addWalletListener()
7}, [])

E voilà! Abbiamo completato la programmazione di tutte le funzionalità del nostro portafoglio! Ora che il nostro portafoglio è configurato, scopriamo come coniare il nostro NFT!

Metadati NFT 101

Quindi ricorda i metadati dell'NFT di cui abbiamo appena parlato nel Passaggio 0 di questo tutorial: danno vita a un NFT, consentendogli di avere proprietà, come una risorsa digitale, un nome, una descrizione e altri attributi.

Dovremo configurare questi metadati come un oggetto JSON e memorizzarli, in modo da poterli passare come parametro tokenURI quando chiamiamo la funzione mintNFT del nostro contratto intelligente.

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 ci sono un paio di opzioni su dove possiamo memorizzare questo oggetto JSON:

  • Potremmo memorizzarlo sulla blockchain di Ethereum; tuttavia, farlo sarebbe molto costoso.
  • Potremmo memorizzarlo su un server centralizzato, come AWS o Firebase. Ma ciò vanificherebbe la nostra etica di decentralizzazione.
  • Potremmo usare IPFS, un protocollo decentralizzato e una rete peer-to-peer per l'archiviazione e la condivisione di dati in un file system distribuito. Poiché questo protocollo è decentralizzato e gratuito, è la nostra migliore opzione!

Per memorizzare i nostri metadati su IPFS, useremo Pinata (opens in a new tab), una comoda API e toolkit IPFS. Nel prossimo passaggio, spiegheremo esattamente come farlo!

Usare Pinata per fissare i tuoi metadati su IPFS

Se non hai un account Pinata (opens in a new tab), registrati per un account gratuito qui (opens in a new tab) e completa i passaggi per verificare la tua email e il tuo account.

Creare la tua chiave API Pinata

Naviga alla pagina https://pinata.cloud/keys (opens in a new tab), quindi seleziona il pulsante "New Key" in alto, imposta il widget Admin come abilitato e dai un nome alla tua chiave.

Ti verrà quindi mostrato un popup con le informazioni della tua API. Assicurati di metterle in un posto sicuro.

Ora che la nostra chiave è configurata, aggiungiamola al nostro progetto in modo da poterla usare.

Creare un file .env

Possiamo memorizzare in modo sicuro la nostra chiave e il segreto Pinata in un file di ambiente. Installiamo il pacchetto dotenv (opens in a new tab) nella directory del tuo progetto.

Apri una nuova scheda nel tuo terminale (separata da quella che esegue il localhost) e assicurati di essere nella cartella minter-starter-files, quindi esegui il seguente comando nel tuo terminale:

1npm install dotenv --save

Successivamente, crea un file .env nella directory principale dei tuoi minter-starter-files inserendo quanto segue nella riga di comando:

1vim.env

Questo aprirà il tuo file .env in vim (un editor di testo). Per salvarlo premi "esc" + ":" + "q" sulla tua tastiera in quest'ordine.

Successivamente, in VSCode, naviga al tuo file .env e aggiungi la tua chiave API e il segreto API Pinata ad esso, in questo modo:

1REACT_APP_PINATA_KEY = <pinata-api-key>
2REACT_APP_PINATA_SECRET = <pinata-api-secret>

Salva il file e poi sei pronto per iniziare a scrivere la funzione per caricare i tuoi metadati JSON su IPFS!

Implementare pinJSONToIPFS

Fortunatamente per noi, Pinata ha un'API specifica per caricare dati JSON su IPFS (opens in a new tab) e un comodo esempio JavaScript con axios che possiamo usare, con alcune lievi modifiche.

Nella tua cartella utils, creiamo un altro file chiamato pinata.js e poi importiamo il nostro segreto e la chiave Pinata dal file .env in questo modo:

1require("dotenv").config()
2const key = process.env.REACT_APP_PINATA_KEY
3const secret = process.env.REACT_APP_PINATA_SECRET

Successivamente, incolla il codice aggiuntivo qui sotto nel tuo file pinata.js. Non preoccuparti, analizzeremo cosa significa tutto!

1require("dotenv").config()
2const key = process.env.REACT_APP_PINATA_KEY
3const secret = process.env.REACT_APP_PINATA_SECRET
4
5const axios = require("axios")
6
7export const pinJSONToIPFS = async (JSONBody) => {
8 const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`
9 // effettuando la richiesta POST con axios a Pinata ⬇️
10 return axios
11 .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}

Quindi cosa fa esattamente questo codice?

Per prima cosa, importa axios (opens in a new tab), un client HTTP basato su promise per il browser e node.js, che useremo per fare una richiesta a Pinata.

Poi abbiamo la nostra funzione asincrona pinJSONToIPFS, che prende un JSONBody come input e la chiave e il segreto api Pinata nel suo header, il tutto per fare una richiesta POST alla loro API pinJSONToIPFS.

  • Se questa richiesta POST ha successo, la nostra funzione restituisce un oggetto JSON con il booleano success come true e il pinataUrl in cui sono stati fissati i nostri metadati. Useremo questo pinataUrl restituito come input tokenURI per la funzione per coniare del nostro contratto intelligente.
  • Se questa richiesta post fallisce, la nostra funzione restituisce un oggetto JSON con il booleano success come false e una stringa message che comunica il nostro errore.

Come per i tipi di ritorno della nostra funzione connectWallet, stiamo restituendo oggetti JSON in modo da poter usare i loro parametri per aggiornare le nostre variabili di stato e l'interfaccia utente.

Caricare il tuo contratto intelligente

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 contratto intelligente in modo da poter chiamare la sua funzione mintNFT.

Come abbiamo menzionato in precedenza, in questo tutorial useremo questo contratto intelligente per NFT esistente (opens in a new tab); tuttavia, se desideri imparare come lo abbiamo realizzato, o crearne uno tu stesso, ti consigliamo vivamente di dare un'occhiata al nostro altro 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 directory src c'è un file contract-abi.json. Un'ABI è necessaria per specificare quale funzione invocherà un contratto, oltre a garantire che la funzione restituirà i dati nel formato che ti aspetti.

Avremo anche bisogno di una chiave API Alchemy e dell'API Alchemy Web3 per connetterci alla blockchain di Ethereum e caricare il nostro contratto intelligente.

Creare la tua chiave API Alchemy

Se non hai già un account Alchemy, registrati gratuitamente qui. (opens in a new tab)

Una volta creato un account Alchemy, puoi generare una chiave API creando un'app. Questo ci consentirà di fare richieste alla rete di test Ropsten.

Naviga alla pagina "Create App" nella tua Dashboard Alchemy passando il mouse su "Apps" nella barra di navigazione e cliccando su "Create App".

Dai un nome alla tua app, noi abbiamo scelto "My First NFT!", offri una breve descrizione, seleziona "Staging" per l'Ambiente utilizzato per la contabilità della tua app e scegli "Ropsten" per la tua rete.

Clicca su "Create app" e il gioco è fatto! La tua app dovrebbe apparire nella tabella sottostante.

Fantastico, quindi ora che abbiamo creato il nostro URL API HTTP Alchemy, copialo negli appunti...

…e poi aggiungiamolo al nostro file .env. Nel complesso, il tuo file .env dovrebbe apparire così:

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 Alchemy, siamo pronti per caricare il nostro contratto intelligente utilizzando Alchemy Web3 (opens in a new tab).

Configurare il tuo endpoint Alchemy Web3 e il contratto

Per prima cosa, se non lo hai già, dovrai installare Alchemy Web3 (opens in a new tab) navigando nella directory home: nft-minter-tutorial nel terminale:

1cd ..
2npm install @alch/alchemy-web3

Successivamente torniamo al nostro file interact.js. All'inizio del file, aggiungi il seguente codice per importare la tua chiave Alchemy dal tuo file .env e configurare il tuo endpoint Alchemy Web3:

1require("dotenv").config()
2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
4const web3 = createAlchemyWeb3(alchemyKey)

Alchemy Web3 (opens in a new tab) è un wrapper attorno a Web3.js (opens in a new tab), che fornisce metodi API migliorati e altri vantaggi cruciali per semplificarti la vita come sviluppatore web3. È progettato per richiedere una configurazione minima in modo da poter iniziare a usarlo subito nella tua app!

Successivamente, aggiungiamo l'ABI del nostro contratto e l'indirizzo del contratto al nostro file.

1require("dotenv").config()
2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
4const web3 = createAlchemyWeb3(alchemyKey)
5
6const contractABI = require("../contract-abi.json")
7const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE"

Una volta che li abbiamo entrambi, siamo pronti per iniziare a programmare la nostra funzione per coniare!

Implementare la funzione mintNFT

All'interno del tuo file interact.js, definiamo la nostra funzione, mintNFT, che eponimamente conierà il nostro NFT.

Poiché effettueremo numerose chiamate asincrone (a Pinata per fissare i nostri metadati su IPFS, ad Alchemy Web3 per caricare il nostro contratto intelligente e a MetaMask per firmare le nostre transazioni), anche la nostra funzione sarà asincrona.

I tre input per la 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 di input

Naturalmente, ha senso avere una sorta di gestione degli errori di input all'inizio della funzione, in modo da uscire da questa funzione se i nostri parametri di input non sono corretti. All'interno della nostra funzione, aggiungiamo il seguente codice:

1export const mintNFT = async (url, name, description) => {
2 // gestione degli errori
3 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}

Essenzialmente, se uno qualsiasi dei parametri di input è una stringa vuota, restituiamo un oggetto JSON in cui il booleano success è false e la stringa status comunica che tutti i campi nella nostra interfaccia utente devono essere completati.

Caricare i metadati su IPFS

Una volta che sappiamo che i nostri metadati sono formattati correttamente, il passaggio successivo è racchiuderli in un oggetto JSON e caricarli su IPFS tramite la funzione pinJSONToIPFS che abbiamo scritto!

Per farlo, dobbiamo prima importare la funzione pinJSONToIPFS nel nostro file interact.js. Proprio all'inizio di interact.js, aggiungiamo:

1import { pinJSONToIPFS } from "./pinata.js"

Ricorda che pinJSONToIPFS accetta un corpo JSON. Quindi, prima di effettuare una chiamata ad essa, 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 // gestione degli errori
3 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
10 // crea i metadati
11 const metadata = new Object()
12 metadata.name = name
13 metadata.image = url
14 metadata.description = description
15
16 // effettua la chiamata a pinata
17 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.pinataUrl
25}

Nota, memorizziamo la risposta della nostra chiamata a pinJSONToIPFS(metadata) nell'oggetto pinataResponse. Quindi, analizziamo questo oggetto per eventuali errori.

Se c'è un errore, restituiamo un oggetto JSON in cui il booleano success è false e la nostra stringa status comunica che la nostra chiamata è fallita. Altrimenti, estraiamo il pinataURL dalla pinataResponse e lo memorizziamo come nostra variabile tokenURI.

Ora è il momento di caricare il nostro contratto intelligente utilizzando l'API Alchemy Web3 che abbiamo inizializzato all'inizio del nostro file. Aggiungi la seguente riga di codice in fondo alla funzione mintNFT per impostare il contratto nella variabile globale window.contract:

1window.contract = await new web3.eth.Contract(contractABI, contractAddress)

L'ultima cosa da aggiungere nella nostra funzione mintNFT è la nostra transazione Ethereum:

1// imposta la tua transazione Ethereum
2const transactionParameters = {
3 to: contractAddress, // Obbligatorio tranne durante le pubblicazioni del contratto.
4 from: window.ethereum.selectedAddress, // deve corrispondere all'indirizzo attivo dell'utente.
5 data: window.contract.methods
6 .mintNFT(window.ethereum.selectedAddress, tokenURI)
7 .encodeABI(), // effettua la chiamata allo smart contract NFT
8}
9
10// firma la transazione tramite MetaMask
11try {
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}

Se hai già familiarità con le transazioni Ethereum, noterai che la struttura è abbastanza simile a quella che hai visto.

  • Per prima cosa, impostiamo i parametri delle nostre transazioni.
    • to specifica l'indirizzo del destinatario (il nostro contratto intelligente)
    • from specifica il firmatario della transazione (l'indirizzo dell'utente connesso a MetaMask: window.ethereum.selectedAddress)
    • data contiene la chiamata al metodo mintNFT del nostro contratto intelligente, che riceve il nostro tokenURI e l'indirizzo del portafoglio dell'utente, window.ethereum.selectedAddress, come input
  • Quindi, effettuiamo una chiamata await, window.ethereum.request, in cui chiediamo a MetaMask di firmare la transazione. Nota, in questa richiesta, stiamo specificando il nostro metodo eth (eth_SentTransaction) e passando i nostri transactionParameters. A questo punto, MetaMask si aprirà nel browser e chiederà all'utente di firmare o rifiutare la transazione.
    • Se la transazione ha successo, la funzione restituirà un oggetto JSON in cui il booleano success è impostato su true e la stringa status invita l'utente a controllare Etherscan per maggiori informazioni sulla sua transazione.
    • Se la transazione fallisce, la funzione restituirà un oggetto JSON in cui il booleano success è impostato su false e la stringa status comunica il messaggio di errore.

Nel complesso, la nostra funzione mintNFT dovrebbe apparire così:

1export const mintNFT = async (url, name, description) => {
2 // gestione degli errori
3 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
10 // crea i metadati
11 const metadata = new Object()
12 metadata.name = name
13 metadata.image = url
14 metadata.description = description
15
16 // richiesta di pin a pinata
17 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.pinataUrl
25
26 // carica lo smart contract
27 window.contract = await new web3.eth.Contract(contractABI, contractAddress) // loadContract();
28
29 // imposta la tua transazione Ethereum
30 const transactionParameters = {
31 to: contractAddress, // Obbligatorio tranne durante le pubblicazioni del contratto.
32 from: window.ethereum.selectedAddress, // deve corrispondere all'indirizzo attivo dell'utente.
33 data: window.contract.methods
34 .mintNFT(window.ethereum.selectedAddress, tokenURI)
35 .encodeABI(), // effettua la chiamata allo smart contract NFT
36 }
37
38 // firma la transazione tramite MetaMask
39 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}

È una funzione gigantesca! Ora, dobbiamo solo connettere la nostra funzione mintNFT al nostro componente Minter.js...

Connettere mintNFT al nostro frontend Minter.js

Apri il tuo file Minter.js e aggiorna la riga import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js"; in alto in modo che sia:

1import {
2 connectWallet,
3 getCurrentWalletConnected,
4 mintNFT,
5} from "./utils/interact.js"

Infine, implementa la funzione onMintPressed per effettuare una chiamata await alla tua funzione mintNFT importata e aggiornare la variabile di stato status per riflettere se la nostra transazione ha avuto successo o è fallita:

1const onMintPressed = async () => {
2 const { status } = await mintNFT(url, name, description)
3 setStatus(status)
4}

Distribuire il tuo NFT su un sito web live

Pronto a portare il tuo progetto live affinché gli utenti possano interagirvi? Dai un'occhiata a questo tutorial (opens in a new tab) per distribuire il tuo Minter su un sito web live.

Un ultimo passaggio...

Prendi d'assalto il mondo della blockchain

Scherzavo, sei arrivato alla fine del tutorial!

Per riassumere, costruendo un minter di NFT, hai imparato con successo come:

  • Connetterti a MetaMask tramite il tuo progetto frontend
  • Chiamare i metodi del contratto intelligente dal tuo frontend
  • Firmare le transazioni usando MetaMask

Presumibilmente, ti piacerebbe poter mostrare gli NFT coniati tramite la tua dApp nel tuo portafoglio — quindi assicurati di dare un'occhiata al nostro rapido tutorial Come visualizzare il tuo NFT nel tuo portafoglio (opens in a new tab)!

E, come sempre, se hai domande, siamo qui per aiutarti nel 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!

Ultimo aggiornamento della pagina: 3 marzo 2026

Questo tutorial è stato utile?