NFT-Minter-Tutorial
Eine der größten Herausforderungen für Entwickler mit Web2-Hintergrund besteht darin, herauszufinden, wie man seinen Smart Contract mit einem Frontend-Projekt verbindet und mit ihm interagiert.
Indem du einen NFT-Minter baust – eine einfache Benutzeroberfläche, in der du einen Link zu deinem digitalen Asset, einen Titel und eine Beschreibung eingeben kannst – lernst du Folgendes:
- Verbindung zu MetaMask über dein Frontend-Projekt herstellen
- Smart-Contract-Methoden von deinem Frontend aus aufrufen
- Transaktionen mit MetaMask signieren
In diesem Tutorial verwenden wir React (opens in a new tab) als unser Frontend-Framework. Da sich dieses Tutorial in erster Linie auf die Web3-Entwicklung konzentriert, werden wir nicht viel Zeit damit verbringen, die Grundlagen von React aufzuschlüsseln. Stattdessen konzentrieren wir uns darauf, Funktionalität in unser Projekt zu bringen.
Als Voraussetzung solltest du ein grundlegendes Verständnis von React haben – wissen, wie Komponenten, Props, useState/useEffect und grundlegende Funktionsaufrufe funktionieren. Wenn du noch nie von einem dieser Begriffe gehört hast, solltest du dir dieses Einführungstutorial zu React (opens in a new tab) ansehen. Für visuelle Lerner empfehlen wir diese hervorragende Videoserie Full Modern React Tutorial (opens in a new tab) von Net Ninja.
Und falls du es noch nicht getan hast, benötigst du definitiv ein Alchemy-Konto, um dieses Tutorial abzuschließen und etwas auf der Blockchain zu bauen. Melde dich hier (opens in a new tab) für ein kostenloses Konto an.
Ohne weitere Umschweife, fangen wir an!
NFTs erstellen 101
Bevor wir uns überhaupt Code ansehen, ist es wichtig zu verstehen, wie das Erstellen eines NFTs funktioniert. Es umfasst zwei Schritte:
Einen NFT-Smart-Contract auf der Ethereum-Blockchain veröffentlichen
Der größte Unterschied zwischen den beiden NFT-Smart-Contract-Standards besteht darin, dass ERC-1155 ein Multi-Token-Standard ist und Batch-Funktionalität beinhaltet, während ERC-721 ein Single-Token-Standard ist und daher nur die Übertragung eines Tokens auf einmal unterstützt.
Die Prägefunktion aufrufen
Normalerweise erfordert diese Prägefunktion, dass du zwei Variablen als Parameter übergibst: erstens den recipient (Empfänger), der die Adresse angibt, die dein frisch geprägtes NFT erhalten wird, und zweitens die tokenURI des NFTs, eine Zeichenfolge, die auf ein JSON-Dokument verweist, das die Metadaten des NFTs beschreibt.
Die Metadaten eines NFTs erwecken es erst richtig zum Leben und ermöglichen es ihm, Eigenschaften wie einen Namen, eine Beschreibung, ein Bild (oder ein anderes digitales Asset) und andere Attribute zu haben. Hier ist ein Beispiel für eine tokenURI (opens in a new tab), die die Metadaten eines NFTs enthält.
In diesem Tutorial konzentrieren wir uns auf Teil 2: den Aufruf der Prägefunktion eines bestehenden NFT-Smart-Contracts über unsere React-Benutzeroberfläche.
Hier ist ein Link (opens in a new tab) zu dem ERC-721-NFT-Smart-Contract, den wir in diesem Tutorial aufrufen werden. Wenn du erfahren möchtest, wie wir ihn erstellt haben, empfehlen wir dir dringend, dir unser anderes Tutorial anzusehen: „Wie man ein NFT erstellt“ (opens in a new tab).
Cool, jetzt, da wir verstehen, wie das Erstellen eines NFTs funktioniert, lass uns unsere Startdateien klonen!
Die Startdateien klonen
Gehe zunächst zum nft-minter-tutorial GitHub-Repository (opens in a new tab), um die Startdateien für dieses Projekt zu erhalten. Klone dieses Repository in deine lokale Umgebung.
Wenn du dieses geklonte nft-minter-tutorial-Repository öffnest, wirst du feststellen, dass es zwei Ordner enthält: minter-starter-files und nft-minter.
minter-starter-filesenthält die Startdateien (im Wesentlichen die React-Benutzeroberfläche) für dieses Projekt. In diesem Tutorial werden wir in diesem Verzeichnis arbeiten, während du lernst, wie du diese Benutzeroberfläche zum Leben erweckst, indem du sie mit deiner Ethereum-Wallet und einem NFT-Smart-Contract verbindest.nft-minterenthält das gesamte abgeschlossene Tutorial und dient dir als Referenz, falls du nicht weiterkommst.
Öffne als Nächstes deine Kopie von minter-starter-files in deinem Code-Editor und navigiere dann in deinen src-Ordner.
Der gesamte Code, den wir schreiben werden, befindet sich im src-Ordner. Wir werden die Komponente Minter.js bearbeiten und zusätzliche JavaScript-Dateien schreiben, um unserem Projekt Web3-Funktionalität zu verleihen.
Schritt 2: Unsere Startdateien ansehen
Bevor wir mit dem Programmieren beginnen, ist es wichtig, sich anzusehen, was uns in den Startdateien bereits zur Verfügung gestellt wird.
Dein React-Projekt zum Laufen bringen
Beginnen wir damit, das React-Projekt in unserem Browser auszuführen. Das Schöne an React ist, dass alle Änderungen, die wir speichern, live in unserem Browser aktualisiert werden, sobald unser Projekt im Browser läuft.
Um das Projekt zum Laufen zu bringen, navigiere zum Stammverzeichnis des Ordners minter-starter-files und führe npm install in deinem Terminal aus, um die Abhängigkeiten des Projekts zu installieren:
cd minter-starter-filesnpm installSobald die Installation abgeschlossen ist, führe npm start in deinem Terminal aus:
npm startDadurch sollte sich http://localhost:3000/ (opens in a new tab) in deinem Browser öffnen, wo du das Frontend für unser Projekt siehst. Es sollte aus 3 Feldern bestehen: einem Platz zur Eingabe eines Links zum Asset deines NFTs, zur Eingabe des Namens deines NFTs und zur Angabe einer Beschreibung.
Wenn du versuchst, auf die Schaltflächen „Connect Wallet“ oder „Mint NFT“ zu klicken, wirst du feststellen, dass sie nicht funktionieren – das liegt daran, dass wir ihre Funktionalität erst noch programmieren müssen! :)
Die Komponente Minter.js
HINWEIS: Stelle sicher, dass du dich im Ordner minter-starter-files und nicht im Ordner nft-minter befindest!
Gehen wir in unserem Editor zurück in den src-Ordner und öffnen die Datei Minter.js. Es ist extrem wichtig, dass wir alles in dieser Datei verstehen, da es die primäre React-Komponente ist, an der wir arbeiten werden.
Oben in dieser Datei haben wir unsere Zustandsvariablen, die wir nach bestimmten Ereignissen aktualisieren werden.
1// Zustandsvariablen2const [walletAddress, setWallet] = useState("")3const [status, setStatus] = useState("")4const [name, setName] = useState("")5const [description, setDescription] = useState("")6const [url, setURL] = useState("")Noch nie von React-Zustandsvariablen oder State-Hooks gehört? Sieh dir diese (opens in a new tab) Dokumentation an.
Hier ist, was jede der Variablen darstellt:
walletAddress- eine Zeichenfolge, die die Wallet-Adresse des Benutzers speichertstatus- eine Zeichenfolge, die eine Nachricht enthält, die unten in der Benutzeroberfläche angezeigt werden sollname- eine Zeichenfolge, die den Namen des NFTs speichertdescription- eine Zeichenfolge, die die Beschreibung des NFTs speicherturl- eine Zeichenfolge, die ein Link zum digitalen Asset des NFTs ist
Nach den Zustandsvariablen siehst du drei nicht implementierte Funktionen: useEffect, connectWalletPressed und onMintPressed. Du wirst feststellen, dass alle diese Funktionen async sind, da wir in ihnen asynchrone API-Aufrufe durchführen werden! Ihre Namen sind namensgebend für ihre Funktionalitäten:
1useEffect(async () => {2 // TODO: implementieren3}, [])4
5const connectWalletPressed = async () => {6 // TODO: implementieren7}8
9const onMintPressed = async () => {10 // TODO: implementieren11}useEffect(opens in a new tab) - dies ist ein React-Hook, der aufgerufen wird, nachdem deine Komponente gerendert wurde. Da ihm ein leeres Array[]als Prop übergeben wird (siehe Zeile 3), wird er nur beim ersten Rendern der Komponente aufgerufen. Hier rufen wir unseren Wallet-Listener und eine weitere Wallet-Funktion auf, um unsere Benutzeroberfläche zu aktualisieren und anzuzeigen, ob bereits eine Wallet verbunden ist.connectWalletPressed- diese Funktion wird aufgerufen, um die MetaMask-Wallet des Benutzers mit unserer Dapp zu verbinden.onMintPressed- diese Funktion wird aufgerufen, um das NFT des Benutzers zu prägen.
Gegen Ende dieser Datei haben wir die Benutzeroberfläche unserer Komponente. Wenn du diesen Code sorgfältig durchliest, wirst du feststellen, dass wir unsere Zustandsvariablen url, name und description aktualisieren, wenn sich die Eingabe in den entsprechenden Textfeldern ändert.
Du wirst auch sehen, dass connectWalletPressed und onMintPressed aufgerufen werden, wenn die Schaltflächen mit den IDs mintButton bzw. walletButton angeklickt werden.
1// die UI unserer Komponente2return (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 <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 45</div>46)Lass uns abschließend klären, wo diese Minter-Komponente hinzugefügt wird.
Wenn du zur Datei App.js gehst, der Hauptkomponente in React, die als Container für alle anderen Komponenten fungiert, wirst du sehen, dass unsere Minter-Komponente in Zeile 7 eingefügt wird.
In diesem Tutorial werden wir nur die Datei Minter.js bearbeiten und Dateien in unserem src-Ordner hinzufügen.
Jetzt, da wir verstehen, womit wir arbeiten, lass uns unsere Ethereum-Wallet einrichten!
Deine Ethereum-Wallet einrichten
Damit Benutzer mit deinem Smart Contract interagieren können, müssen sie ihre Ethereum-Wallet mit deiner Dapp verbinden.
MetaMask herunterladen
Für dieses Tutorial verwenden wir MetaMask, eine virtuelle Wallet im Browser, die zur Verwaltung deiner Ethereum-Kontoadresse verwendet wird. Wenn du mehr darüber erfahren möchtest, wie Transaktionen auf Ethereum funktionieren, sieh dir diese Seite an.
Du kannst MetaMask hier (opens in a new tab) kostenlos herunterladen und ein Konto erstellen. Wenn du ein Konto erstellst oder bereits eines hast, stelle sicher, dass du oben rechts zum „Ropsten Test Network“ wechselst (damit wir nicht mit echtem Geld hantieren).
Ether von einem Faucet hinzufügen
Um unsere NFTs zu prägen (oder Transaktionen auf der Ethereum-Blockchain zu signieren), benötigen wir etwas falsches ETH. Um ETH zu erhalten, kannst du zum Ropsten-Faucet (opens in a new tab) gehen, deine Ropsten-Kontoadresse eingeben und dann auf „Send Ropsten Eth“ klicken. Kurz darauf solltest du ETH in deinem MetaMask-Konto sehen!
Deinen Kontostand überprüfen
Um sicherzugehen, dass unser Guthaben vorhanden ist, stellen wir eine eth_getBalance (opens in a new tab)-Anfrage mit dem Composer-Tool von Alchemy (opens in a new tab). Dies gibt die Menge an ETH in unserer Wallet zurück. Nachdem du deine MetaMask-Kontoadresse eingegeben und auf „Send Request“ geklickt hast, solltest du eine Antwort wie diese sehen:
1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}HINWEIS: Dieses Ergebnis ist in Wei, nicht in ETH. Wei wird als kleinste Stückelung von Ether verwendet. Die Umrechnung von Wei in ETH lautet: 1 ETH = 10¹⁸ Wei. Wenn wir also 0xde0b6b3a7640000 in eine Dezimalzahl umwandeln, erhalten wir 1*10¹⁸, was 1 ETH entspricht.
Puh! Unser falsches Geld ist komplett da!
MetaMask mit deiner Benutzeroberfläche verbinden
Jetzt, da unsere MetaMask-Wallet eingerichtet ist, lass uns unsere Dapp damit verbinden!
Da wir uns an das MVC (opens in a new tab)-Paradigma halten wollen, werden wir eine separate Datei erstellen, die unsere Funktionen zur Verwaltung der Logik, Daten und Regeln unserer Dapp enthält, und diese Funktionen dann an unser Frontend (unsere Komponente Minter.js) übergeben.
Die Funktion connectWallet
Dazu erstellen wir einen neuen Ordner namens utils in deinem src-Verzeichnis und fügen darin eine Datei namens interact.js hinzu, die alle unsere Interaktionsfunktionen für Wallet und Smart Contract enthalten wird.
In unserer Datei interact.js werden wir eine Funktion connectWallet schreiben, die wir dann in unsere Komponente Minter.js importieren und aufrufen.
Füge in deiner Datei interact.js Folgendes hinzu:
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`}>26 You must install MetaMask, a virtual Ethereum wallet, in your27 browser.28 </a>29 </p>30 </span>31 ),32 }33 }34}Lass uns aufschlüsseln, was dieser Code macht:
Zuerst prüft unsere Funktion, ob window.ethereum in deinem Browser aktiviert ist.
window.ethereum ist eine globale API, die von MetaMask und anderen Wallet-Anbietern injiziert wird und es Websites ermöglicht, die Ethereum-Konten der Benutzer anzufordern. Wenn dies genehmigt wird, kann sie Daten von den Blockchains lesen, mit denen der Benutzer verbunden ist, und vorschlagen, dass der Benutzer Nachrichten und Transaktionen signiert. Weitere Informationen findest du in der MetaMask-Dokumentation (opens in a new tab)!
Wenn window.ethereum nicht vorhanden ist, bedeutet das, dass MetaMask nicht installiert ist. Dies führt dazu, dass ein JSON-Objekt zurückgegeben wird, bei dem die zurückgegebene address eine leere Zeichenfolge ist und das JSX-Objekt status meldet, dass der Benutzer MetaMask installieren muss.
Die meisten Funktionen, die wir schreiben, geben JSON-Objekte zurück, die wir verwenden können, um unsere Zustandsvariablen und die Benutzeroberfläche zu aktualisieren.
Wenn window.ethereum vorhanden ist, wird es interessant.
Mithilfe einer try/catch-Schleife versuchen wir, eine Verbindung zu MetaMask herzustellen, indem wir window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab) aufrufen. Der Aufruf dieser Funktion öffnet MetaMask im Browser, wodurch der Benutzer aufgefordert wird, seine Wallet mit deiner Dapp zu verbinden.
- Wenn der Benutzer sich für eine Verbindung entscheidet, gibt
method: "eth_requestAccounts"ein Array zurück, das alle Kontoadressen des Benutzers enthält, die mit der Dapp verbunden sind. Insgesamt gibt unsere FunktionconnectWalletein JSON-Objekt zurück, das die ersteaddressin diesem Array (siehe Zeile 9) und einestatus-Nachricht enthält, die den Benutzer auffordert, eine Nachricht an den Smart Contract zu schreiben. - Wenn der Benutzer die Verbindung ablehnt, enthält das JSON-Objekt eine leere Zeichenfolge für die zurückgegebene
addressund einestatus-Nachricht, die widerspiegelt, dass der Benutzer die Verbindung abgelehnt hat.
Die Funktion connectWallet zu deiner Minter.js-UI-Komponente hinzufügen
Nachdem wir nun diese Funktion connectWallet geschrieben haben, verbinden wir sie mit unserer Komponente Minter.js.
Zuerst müssen wir unsere Funktion in unsere Datei Minter.js importieren, indem wir import { connectWallet } from "./utils/interact.js"; oben in der Datei Minter.js hinzufügen. Deine ersten 11 Zeilen von Minter.js sollten nun so aussehen:
1import { useEffect, useState } from "react";2import { connectWallet } from "./utils/interact.js";3
4const Minter = (props) => {5
6 // Zustandsvariablen7 const [walletAddress, setWallet] = useState("");8 const [status, setStatus] = useState("");9 const [name, setName] = useState("");10 const [description, setDescription] = useState("");11 const [url, setURL] = useState("");Dann rufen wir innerhalb unserer Funktion connectWalletPressed unsere importierte Funktion connectWallet wie folgt auf:
1const connectWalletPressed = async () => {2 const walletResponse = await connectWallet()3 setStatus(walletResponse.status)4 setWallet(walletResponse.address)5}Fällt dir auf, wie der Großteil unserer Funktionalität aus unserer Komponente Minter.js in die Datei interact.js abstrahiert wird? Das tun wir, um dem M-V-C-Paradigma zu entsprechen!
In connectWalletPressed machen wir einfach einen await-Aufruf an unsere importierte Funktion connectWallet und aktualisieren mit deren Antwort unsere Variablen status und walletAddress über ihre State-Hooks.
Lass uns nun beide Dateien Minter.js und interact.js speichern und unsere bisherige Benutzeroberfläche testen.
Öffne deinen Browser unter localhost:3000 und drücke oben rechts auf der Seite auf die Schaltfläche „Connect Wallet“.
Wenn du MetaMask installiert hast, solltest du aufgefordert werden, deine Wallet mit deiner Dapp zu verbinden. Akzeptiere die Einladung zur Verbindung.
Du solltest sehen, dass die Wallet-Schaltfläche nun anzeigt, dass deine Adresse verbunden ist.
Versuche als Nächstes, die Seite zu aktualisieren ... das ist seltsam. Unsere Wallet-Schaltfläche fordert uns auf, MetaMask zu verbinden, obwohl es bereits verbunden ist ...
Aber keine Sorge! Wir können das leicht beheben, indem wir eine Funktion namens getCurrentWalletConnected implementieren, die prüft, ob bereits eine Adresse mit unserer Dapp verbunden ist, und unsere Benutzeroberfläche entsprechend aktualisiert!
Die Funktion getCurrentWalletConnected
Füge in deiner Datei interact.js die folgende Funktion getCurrentWalletConnected hinzu:
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 your33 browser.34 </a>35 </p>36 </span>37 ),38 }39 }40}Dieser Code ist der Funktion connectWallet, die wir gerade geschrieben haben, sehr ähnlich.
Der Hauptunterschied besteht darin, dass wir hier nicht die Methode eth_requestAccounts aufrufen, die MetaMask öffnet, damit der Benutzer seine Wallet verbinden kann, sondern die Methode eth_accounts, die einfach ein Array zurückgibt, das die MetaMask-Adressen enthält, die derzeit mit unserer Dapp verbunden sind.
Um diese Funktion in Aktion zu sehen, rufen wir sie in der Funktion useEffect unserer Komponente Minter.js auf.
Wie bei connectWallet müssen wir diese Funktion aus unserer Datei interact.js wie folgt in unsere Datei Minter.js importieren:
1import { useEffect, useState } from "react"2import {3 connectWallet,4 getCurrentWalletConnected, // hier importieren5} from "./utils/interact.js"Nun rufen wir sie einfach in unserer Funktion useEffect auf:
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)5}, [])Beachte, dass wir die Antwort unseres Aufrufs von getCurrentWalletConnected verwenden, um unsere Zustandsvariablen walletAddress und status zu aktualisieren.
Sobald du diesen Code hinzugefügt hast, versuche, unser Browserfenster zu aktualisieren. Die Schaltfläche sollte anzeigen, dass du verbunden bist, und eine Vorschau der Adresse deiner verbundenen Wallet anzeigen – auch nach dem Aktualisieren!
addWalletListener implementieren
Der letzte Schritt bei der Einrichtung unserer Dapp-Wallet ist die Implementierung des Wallet-Listeners, damit unsere Benutzeroberfläche aktualisiert wird, wenn sich der Zustand unserer Wallet ändert, z. B. wenn der Benutzer die Verbindung trennt oder das Konto wechselt.
Füge in deiner Datei Minter.js eine Funktion addWalletListener hinzu, die wie folgt aussieht:
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}Lass uns kurz aufschlüsseln, was hier passiert:
- Zuerst prüft unsere Funktion, ob
window.ethereumaktiviert ist (d. h. MetaMask ist installiert).- Wenn nicht, setzen wir unsere Zustandsvariable
statuseinfach auf eine JSX-Zeichenfolge, die den Benutzer auffordert, MetaMask zu installieren. - Wenn es aktiviert ist, richten wir in Zeile 3 den Listener
window.ethereum.on("accountsChanged")ein, der auf Zustandsänderungen in der MetaMask-Wallet lauscht, z. B. wenn der Benutzer ein zusätzliches Konto mit der Dapp verbindet, Konten wechselt oder die Verbindung zu einem Konto trennt. Wenn mindestens ein Konto verbunden ist, wird die ZustandsvariablewalletAddressals erstes Konto im vom Listener zurückgegebenen Arrayaccountsaktualisiert. Andernfalls wirdwalletAddressals leere Zeichenfolge festgelegt.
- Wenn nicht, setzen wir unsere Zustandsvariable
Schließlich müssen wir sie in unserer Funktion useEffect aufrufen:
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)5
6 addWalletListener()7}, [])Und voilà! Wir haben die Programmierung unserer gesamten Wallet-Funktionalität abgeschlossen! Jetzt, da unsere Wallet eingerichtet ist, lass uns herausfinden, wie wir unser NFT prägen können!
NFT-Metadaten 101
Erinnerst du dich an die NFT-Metadaten, über die wir gerade in Schritt 0 dieses Tutorials gesprochen haben? Sie erwecken ein NFT zum Leben und ermöglichen es ihm, Eigenschaften wie ein digitales Asset, einen Namen, eine Beschreibung und andere Attribute zu haben.
Wir müssen diese Metadaten als JSON-Objekt konfigurieren und speichern, damit wir sie als Parameter tokenURI übergeben können, wenn wir die Funktion mintNFT unseres Smart Contracts aufrufen.
Der Text in den Feldern „Link to Asset“, „Name“ und „Description“ umfasst die verschiedenen Eigenschaften der Metadaten unseres NFTs. Wir formatieren diese Metadaten als JSON-Objekt, aber es gibt ein paar Optionen, wo wir dieses JSON-Objekt speichern können:
- Wir könnten es auf der Ethereum-Blockchain speichern; dies wäre jedoch sehr teuer.
- Wir könnten es auf einem zentralisierten Server wie AWS oder Firebase speichern. Aber das würde unserem Dezentralisierungs-Ethos widersprechen.
- Wir könnten IPFS verwenden, ein dezentralisiertes Protokoll und Peer-to-Peer-Netzwerk zum Speichern und Teilen von Daten in einem verteilten Dateisystem. Da dieses Protokoll dezentralisiert und kostenlos ist, ist es unsere beste Option!
Um unsere Metadaten auf IPFS zu speichern, verwenden wir Pinata (opens in a new tab), eine praktische IPFS-API und ein Toolkit. Im nächsten Schritt erklären wir genau, wie das geht!
Pinata verwenden, um deine Metadaten an IPFS anzuheften
Wenn du kein Pinata (opens in a new tab)-Konto hast, melde dich hier (opens in a new tab) für ein kostenloses Konto an und führe die Schritte zur Verifizierung deiner E-Mail-Adresse und deines Kontos durch.
Deinen Pinata-API-Schlüssel erstellen
Navigiere zur Seite https://pinata.cloud/keys (opens in a new tab), wähle dann oben die Schaltfläche „New Key“ (Neuer Schlüssel), aktiviere das Admin-Widget und benenne deinen Schlüssel.
Dir wird dann ein Popup mit deinen API-Informationen angezeigt. Stelle sicher, dass du diese an einem sicheren Ort aufbewahrst.
Jetzt, da unser Schlüssel eingerichtet ist, fügen wir ihn unserem Projekt hinzu, damit wir ihn verwenden können.
Eine .env-Datei erstellen
Wir können unseren Pinata-Schlüssel und unser Secret sicher in einer Umgebungsdatei speichern. Lass uns das dotenv-Paket (opens in a new tab) in deinem Projektverzeichnis installieren.
Öffne einen neuen Tab in deinem Terminal (getrennt von dem, auf dem der lokale Host läuft) und stelle sicher, dass du dich im Ordner minter-starter-files befindest. Führe dann den folgenden Befehl in deinem Terminal aus:
1npm install dotenv --saveErstelle als Nächstes eine .env-Datei im Stammverzeichnis deiner minter-starter-files, indem du Folgendes in deine Befehlszeile eingibst:
1vim.envDadurch wird deine .env-Datei in vim (einem Texteditor) geöffnet. Um sie zu speichern, drücke in dieser Reihenfolge „Esc“ + „:“ + „q“ auf deiner Tastatur.
Navigiere als Nächstes in VSCode zu deiner .env-Datei und füge deinen Pinata-API-Schlüssel und dein API-Secret wie folgt hinzu:
1REACT_APP_PINATA_KEY = <pinata-api-key>2REACT_APP_PINATA_SECRET = <pinata-api-secret>Speichere die Datei, und dann bist du bereit, die Funktion zum Hochladen deiner JSON-Metadaten auf IPFS zu schreiben!
pinJSONToIPFS implementieren
Zum Glück für uns hat Pinata eine API speziell zum Hochladen von JSON-Daten auf IPFS (opens in a new tab) und ein praktisches JavaScript-Beispiel mit axios, das wir mit einigen leichten Modifikationen verwenden können.
Lass uns in deinem utils-Ordner eine weitere Datei namens pinata.js erstellen und dann unser Pinata-Secret und unseren Schlüssel wie folgt aus der .env-Datei importieren:
1require("dotenv").config()2const key = process.env.REACT_APP_PINATA_KEY3const secret = process.env.REACT_APP_PINATA_SECRETFüge als Nächstes den zusätzlichen Code von unten in deine Datei pinata.js ein. Keine Sorge, wir werden aufschlüsseln, was alles bedeutet!
1require("dotenv").config()2const key = process.env.REACT_APP_PINATA_KEY3const secret = process.env.REACT_APP_PINATA_SECRET4
5const axios = require("axios")6
7export const pinJSONToIPFS = async (JSONBody) => {8 const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`9 // Axios-POST-Anfrage an Pinata durchführen ⬇️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}Was genau macht dieser Code also?
Zuerst importiert er axios (opens in a new tab), einen Promise-basierten HTTP-Client für den Browser und node.js, den wir verwenden werden, um eine Anfrage an Pinata zu stellen.
Dann haben wir unsere asynchrone Funktion pinJSONToIPFS, die einen JSONBody als Eingabe und den Pinata-API-Schlüssel und das Secret in ihrem Header nimmt, um eine POST-Anfrage an ihre pinJSONToIPFS-API zu stellen.
- Wenn diese POST-Anfrage erfolgreich ist, gibt unsere Funktion ein JSON-Objekt zurück, bei dem der boolesche Wert
successauf true gesetzt ist und diepinataUrlenthält, an der unsere Metadaten angeheftet wurden. Wir werden diese zurückgegebenepinataUrlalstokenURI-Eingabe für die Prägefunktion unseres Smart Contracts verwenden. - Wenn diese POST-Anfrage fehlschlägt, gibt unsere Funktion ein JSON-Objekt zurück, bei dem der boolesche Wert
successauf false gesetzt ist und einemessage-Zeichenfolge unseren Fehler meldet.
Wie bei den Rückgabetypen unserer Funktion connectWallet geben wir JSON-Objekte zurück, damit wir ihre Parameter verwenden können, um unsere Zustandsvariablen und die Benutzeroberfläche zu aktualisieren.
Deinen Smart Contract laden
Jetzt, da wir eine Möglichkeit haben, unsere NFT-Metadaten über unsere Funktion pinJSONToIPFS auf IPFS hochzuladen, benötigen wir eine Möglichkeit, eine Instanz unseres Smart Contracts zu laden, damit wir seine Funktion mintNFT aufrufen können.
Wie bereits erwähnt, werden wir in diesem Tutorial diesen bestehenden NFT-Smart-Contract (opens in a new tab) verwenden; wenn du jedoch erfahren möchtest, wie wir ihn erstellt haben, oder selbst einen erstellen möchtest, empfehlen wir dir dringend, dir unser anderes Tutorial anzusehen: „Wie man ein NFT erstellt“ (opens in a new tab).
Die Contract-ABI
Wenn du unsere Dateien genau untersucht hast, wirst du festgestellt haben, dass sich in unserem src-Verzeichnis eine Datei contract-abi.json befindet. Eine ABI ist notwendig, um anzugeben, welche Funktion ein Contract aufrufen wird, und um sicherzustellen, dass die Funktion Daten in dem Format zurückgibt, das du erwartest.
Wir benötigen außerdem einen Alchemy-API-Schlüssel und die Alchemy-Web3-API, um uns mit der Ethereum-Blockchain zu verbinden und unseren Smart Contract zu laden.
Deinen Alchemy-API-Schlüssel erstellen
Wenn du noch kein Alchemy-Konto hast, melde dich hier kostenlos an. (opens in a new tab)
Sobald du ein Alchemy-Konto erstellt hast, kannst du einen API-Schlüssel generieren, indem du eine App erstellst. Dies ermöglicht es uns, Anfragen an das Ropsten-Testnet zu stellen.
Navigiere zur Seite „Create App“ (App erstellen) in deinem Alchemy-Dashboard, indem du mit der Maus über „Apps“ in der Navigationsleiste fährst und auf „Create App“ klickst.
Benenne deine App (wir haben „My First NFT!“ gewählt), biete eine kurze Beschreibung an, wähle „Staging“ für die Umgebung, die für die Buchhaltung deiner App verwendet wird, und wähle „Ropsten“ für dein Netzwerk.
Klicke auf „Create app“ und das war's! Deine App sollte in der Tabelle unten erscheinen.
Großartig, jetzt, da wir unsere HTTP-Alchemy-API-URL erstellt haben, kopiere sie in deine Zwischenablage ...
… und fügen wir sie dann unserer .env-Datei hinzu. Insgesamt sollte deine .env-Datei so aussehen:
1REACT_APP_PINATA_KEY = <pinata-key>2REACT_APP_PINATA_SECRET = <pinata-secret>3REACT_APP_ALCHEMY_KEY = https: // eth-ropsten.alchemyapi.io/v2/<alchemy-key>Jetzt, da wir unsere Contract-ABI und unseren Alchemy-API-Schlüssel haben, sind wir bereit, unseren Smart Contract mit Alchemy Web3 (opens in a new tab) zu laden.
Deinen Alchemy-Web3-Endpunkt und Contract einrichten
Wenn du es noch nicht hast, musst du zuerst Alchemy Web3 (opens in a new tab) installieren, indem du im Terminal zum Home-Verzeichnis nft-minter-tutorial navigierst:
1cd ..2npm install @alch/alchemy-web3Gehen wir als Nächstes zurück zu unserer Datei interact.js. Füge oben in der Datei den folgenden Code hinzu, um deinen Alchemy-Schlüssel aus deiner .env-Datei zu importieren und deinen Alchemy-Web3-Endpunkt einzurichten:
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) ist ein Wrapper um Web3.js (opens in a new tab), der erweiterte API-Methoden und andere entscheidende Vorteile bietet, um dir das Leben als Web3-Entwickler zu erleichtern. Es ist so konzipiert, dass es nur minimale Konfiguration erfordert, sodass du es sofort in deiner App verwenden kannst!
Fügen wir als Nächstes unsere Contract-ABI und Contract-Adresse zu unserer Datei hinzu.
1require("dotenv").config()2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")4const web3 = createAlchemyWeb3(alchemyKey)5
6const contractABI = require("../contract-abi.json")7const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE"Sobald wir beides haben, sind wir bereit, mit der Programmierung unserer Prägefunktion zu beginnen!
Die Funktion mintNFT implementieren
Lass uns in deiner Datei interact.js unsere Funktion mintNFT definieren, die namensgebend unser NFT prägen wird.
Da wir zahlreiche asynchrone Aufrufe durchführen werden (an Pinata, um unsere Metadaten an IPFS anzuheften, an Alchemy Web3, um unseren Smart Contract zu laden, und an MetaMask, um unsere Transaktionen zu signieren), wird unsere Funktion ebenfalls asynchron sein.
Die drei Eingaben für unsere Funktion sind die url unseres digitalen Assets, der name und die description. Füge die folgende Funktionssignatur unter der Funktion connectWallet hinzu:
1export const mintNFT = async (url, name, description) => {}Eingabefehlerbehandlung
Natürlich ist es sinnvoll, zu Beginn der Funktion eine Art Eingabefehlerbehandlung zu haben, damit wir diese Funktion verlassen, wenn unsere Eingabeparameter nicht korrekt sind. Fügen wir innerhalb unserer Funktion den folgenden Code hinzu:
1export const mintNFT = async (url, name, description) => {2 // Fehlerbehandlung3 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}Im Wesentlichen geben wir, wenn einer der Eingabeparameter eine leere Zeichenfolge ist, ein JSON-Objekt zurück, bei dem der boolesche Wert success auf false gesetzt ist und die Zeichenfolge status meldet, dass alle Felder in unserer Benutzeroberfläche ausgefüllt sein müssen.
Die Metadaten auf IPFS hochladen
Sobald wir wissen, dass unsere Metadaten richtig formatiert sind, besteht der nächste Schritt darin, sie in ein JSON-Objekt zu verpacken und über die von uns geschriebene Funktion pinJSONToIPFS auf IPFS hochzuladen!
Dazu müssen wir zunächst die Funktion pinJSONToIPFS in unsere Datei interact.js importieren. Fügen wir ganz oben in der interact.js Folgendes hinzu:
1import { pinJSONToIPFS } from "./pinata.js"Erinnere dich daran, dass pinJSONToIPFS einen JSON-Body aufnimmt. Bevor wir sie also aufrufen, müssen wir unsere Parameter url, name und description in ein JSON-Objekt formatieren.
Aktualisieren wir unseren Code, um ein JSON-Objekt namens metadata zu erstellen, und rufen dann pinJSONToIPFS mit diesem Parameter metadata auf:
1export const mintNFT = async (url, name, description) => {2 // Fehlerbehandlung3 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 // Metadaten erstellen11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description15
16 // Pinata-Aufruf durchführen17 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}Beachte, dass wir die Antwort unseres Aufrufs von pinJSONToIPFS(metadata) im Objekt pinataResponse speichern. Dann parsen wir dieses Objekt auf eventuelle Fehler.
Wenn ein Fehler vorliegt, geben wir ein JSON-Objekt zurück, bei dem der boolesche Wert success auf false gesetzt ist und unsere Zeichenfolge status meldet, dass unser Aufruf fehlgeschlagen ist. Andernfalls extrahieren wir die pinataURL aus der pinataResponse und speichern sie als unsere Variable tokenURI.
Jetzt ist es an der Zeit, unseren Smart Contract mit der Alchemy-Web3-API zu laden, die wir oben in unserer Datei initialisiert haben. Füge die folgende Codezeile unten in der Funktion mintNFT hinzu, um den Contract in der globalen Variablen window.contract festzulegen:
1window.contract = await new web3.eth.Contract(contractABI, contractAddress)Das Letzte, was wir in unserer Funktion mintNFT hinzufügen müssen, ist unsere Ethereum-Transaktion:
1// Ethereum-Transaktion einrichten2const transactionParameters = {3 to: contractAddress, // Erforderlich, außer bei Vertragsveröffentlichungen.4 from: window.ethereum.selectedAddress, // muss mit der aktiven Adresse des Benutzers übereinstimmen.5 data: window.contract.methods6 .mintNFT(window.ethereum.selectedAddress, tokenURI)7 .encodeABI(), // NFT-Smart-Contract aufrufen8}9
10// die Transaktion über MetaMask signieren11try {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}Wenn du bereits mit Ethereum-Transaktionen vertraut bist, wirst du feststellen, dass die Struktur dem, was du gesehen hast, ziemlich ähnlich ist.
- Zuerst richten wir unsere Transaktionsparameter ein.
togibt die Empfängeradresse an (unseren Smart Contract)fromgibt den Unterzeichner der Transaktion an (die mit MetaMask verbundene Adresse des Benutzers:window.ethereum.selectedAddress)dataenthält den Aufruf der MethodemintNFTunseres Smart Contracts, die unseretokenURIund die Wallet-Adresse des Benutzers,window.ethereum.selectedAddress, als Eingaben erhält
- Dann machen wir einen await-Aufruf,
window.ethereum.request, bei dem wir MetaMask bitten, die Transaktion zu signieren. Beachte, dass wir in dieser Anfrage unsere eth-Methode (eth_sendTransaction) angeben und unseretransactionParametersübergeben. An diesem Punkt öffnet sich MetaMask im Browser und fordert den Benutzer auf, die Transaktion zu signieren oder abzulehnen.- Wenn die Transaktion erfolgreich ist, gibt die Funktion ein JSON-Objekt zurück, bei dem der boolesche Wert
successauf true gesetzt ist und die Zeichenfolgestatusden Benutzer auffordert, Etherscan für weitere Informationen zu seiner Transaktion zu überprüfen. - Wenn die Transaktion fehlschlägt, gibt die Funktion ein JSON-Objekt zurück, bei dem der boolesche Wert
successauf false gesetzt ist und die Zeichenfolgestatusdie Fehlermeldung weiterleitet.
- Wenn die Transaktion erfolgreich ist, gibt die Funktion ein JSON-Objekt zurück, bei dem der boolesche Wert
Insgesamt sollte unsere Funktion mintNFT so aussehen:
1export const mintNFT = async (url, name, description) => {2 // Fehlerbehandlung3 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 // Metadaten erstellen11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description15
16 // Pinata-Pin-Anfrage17 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
26 // Smart Contract laden27 window.contract = await new web3.eth.Contract(contractABI, contractAddress) // loadContract();28
29 // Ethereum-Transaktion einrichten30 const transactionParameters = {31 to: contractAddress, // Erforderlich, außer bei Vertragsveröffentlichungen.32 from: window.ethereum.selectedAddress, // muss mit der aktiven Adresse des Benutzers übereinstimmen.33 data: window.contract.methods34 .mintNFT(window.ethereum.selectedAddress, tokenURI)35 .encodeABI(), // NFT-Smart-Contract aufrufen36 }37
38 // Transaktion über MetaMask signieren39 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}Das ist eine riesige Funktion! Jetzt müssen wir nur noch unsere Funktion mintNFT mit unserer Komponente Minter.js verbinden ...
mintNFT mit unserem Minter.js-Frontend verbinden
Öffne deine Datei Minter.js und aktualisiere die Zeile import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js"; oben wie folgt:
1import {2 connectWallet,3 getCurrentWalletConnected,4 mintNFT,5} from "./utils/interact.js"Implementiere schließlich die Funktion onMintPressed, um einen await-Aufruf an deine importierte Funktion mintNFT durchzuführen und die Zustandsvariable status zu aktualisieren, um widerzuspiegeln, ob unsere Transaktion erfolgreich war oder fehlgeschlagen ist:
1const onMintPressed = async () => {2 const { status } = await mintNFT(url, name, description)3 setStatus(status)4}Dein NFT auf einer Live-Website bereitstellen
Bist du bereit, dein Projekt live zu schalten, damit Benutzer damit interagieren können? Sieh dir dieses Tutorial (opens in a new tab) an, um deinen Minter auf einer Live-Website bereitzustellen.
Ein letzter Schritt ...
Die Blockchain-Welt im Sturm erobern
Nur ein Scherz, du hast es bis zum Ende des Tutorials geschafft!
Zusammenfassend hast du durch den Bau eines NFT-Minters erfolgreich gelernt, wie man:
- Eine Verbindung zu MetaMask über dein Frontend-Projekt herstellt
- Smart-Contract-Methoden von deinem Frontend aus aufruft
- Transaktionen mit MetaMask signiert
Vermutlich möchtest du die über deine Dapp geprägten NFTs in deiner Wallet präsentieren können – sieh dir also unbedingt unser kurzes Tutorial Wie du dein NFT in deiner Wallet anzeigst (opens in a new tab) an!
Und wie immer, wenn du Fragen hast, sind wir hier, um im Alchemy Discord (opens in a new tab) zu helfen. Wir können es kaum erwarten zu sehen, wie du die Konzepte aus diesem Tutorial auf deine zukünftigen Projekte anwendest!
Letzte Aktualisierung der Seite: 3. März 2026