Samouczek mintowania NFT
Jednym z największych wyzwań dla deweloperów wywodzących się ze środowiska Web2 jest zorientowanie się, jak połączyć swój inteligentny kontrakt z projektem frontendu i wejść z nim w interakcję.
Tworząc minter NFT — prosty interfejs użytkownika, w którym można wprowadzić link do zasobu cyfrowego, tytuł i opis — dowiesz się, jak:
- Połączyć się z MetaMask za pośrednictwem projektu frontendowego
- Wywoływać metody inteligentnego kontraktu z poziomu frontendu
- Podpisywać transakcje za pomocą MetaMask
W tym samouczku jako frameworku frontendowego będziemy używać React (opens in a new tab). Ponieważ ten samouczek skupia się głównie na rozwoju Web3, nie będziemy poświęcać wiele czasu na analizowanie podstaw React. Zamiast tego skupimy się na wprowadzeniu funkcjonalności do naszego projektu.
Warunkiem wstępnym jest posiadanie podstawowej wiedzy na temat React — jak działają komponenty, propsy, useState/useEffect i podstawowe wywoływanie funkcji. Jeśli te terminy są dla Ciebie nowe, możesz zapoznać się z tym samouczkiem Wprowadzenie do React (opens in a new tab). Wzrokowcom gorąco polecamy tę doskonałą serię filmów Pełny nowoczesny samouczek React (opens in a new tab) autorstwa Net Ninja.
A jeśli jeszcze go nie masz, na pewno będziesz potrzebować konta Alchemy, aby ukończyć ten samouczek, a także aby cokolwiek budować na blockchainie. Zarejestruj się, aby otrzymać darmowe konto tutaj (opens in a new tab).
Bez zbędnych ceregieli, zaczynajmy!
Tworzenie NFT 101
Zanim w ogóle zaczniemy przyglądać się jakiemukolwiek kodowi, ważne jest, aby zrozumieć, jak działa tworzenie NFT. Składa się ono z dwóch kroków:
Opublikuj inteligentny kontrakt NFT na blockchainie Ethereum
Największa różnica między dwoma standardami inteligentnych kontraktów NFT polega na tym, że ERC-1155 jest standardem wielotokenowym i zawiera funkcjonalność partii, podczas gdy ERC-721 jest standardem jednotokenowym i w związku z tym obsługuje tylko transfer jednego tokena na raz.
Wywołaj funkcję mintowania
Zazwyczaj ta funkcja mintowania wymaga podania dwóch zmiennych jako parametrów: po pierwsze recipient, który określa adres, na który trafi nowo wybite NFT, a po drugie tokenURI NFT, czyli ciąg znaków, który odwołuje się do dokumentu JSON opisującego metadane NFT.
Metadane NFT to tak naprawdę to, co ożywia go, pozwalając na posiadanie takich właściwości jak nazwa, opis, obraz (lub inny zasób cyfrowy) i inne atrybuty. Oto przykład tokenURI (opens in a new tab), który zawiera metadane NFT.
W tym samouczku skupimy się na części 2, wywołaniu istniejącej funkcji mintowania inteligentnego kontraktu NFT za pomocą naszego interfejsu użytkownika React.
Oto link (opens in a new tab) do inteligentnego kontraktu ERC-721 NFT, który będziemy wywoływać w tym samouczku. Jeśli chcesz dowiedzieć się, jak go stworzyliśmy, gorąco polecamy zapoznanie się z naszym innym samouczkiem, „Jak stworzyć NFT” (opens in a new tab).
Super, teraz, gdy rozumiemy, jak działa tworzenie NFT, sklonujmy nasze pliki startowe!
Klonowanie plików startowych
Najpierw przejdź do repozytorium GitHub nft-minter-tutorial (opens in a new tab), aby pobrać pliki startowe dla tego projektu. Sklonuj to repozytorium do swojego lokalnego środowiska.
Po otwarciu sklonowanego repozytorium nft-minter-tutorial zauważysz, że zawiera ono dwa foldery: minter-starter-files i nft-minter.
minter-starter-fileszawiera pliki startowe (zasadniczo interfejs użytkownika React) dla tego projektu. W tym samouczku będziemy pracować w tym katalogu, ponieważ dowiesz się, jak ożywić ten interfejs użytkownika, łącząc go z portfelem Ethereum i inteligentnym kontraktem NFT.nft-minterzawiera cały ukończony samouczek i jest dostępny jako odniesienie, jeśli utkniesz.
Następnie otwórz swoją kopię minter-starter-files w edytorze kodu, a następnie przejdź do folderu src.
Cały kod, który napiszemy, będzie znajdować się w folderze src. Będziemy edytować komponent Minter.js i pisać dodatkowe pliki javascript, aby nadać naszemu projektowi funkcjonalność Web3.
Krok 2: Sprawdź nasze pliki startowe
Zanim zaczniemy kodować, warto sprawdzić, co już zostało dla nas przygotowane w plikach startowych.
Uruchomienie projektu React
Zacznijmy od uruchomienia projektu React w naszej przeglądarce. Piękno React polega na tym, że gdy nasz projekt jest już uruchomiony w przeglądarce, wszelkie zapisane przez nas zmiany będą na bieżąco aktualizowane w przeglądarce.
Aby uruchomić projekt, przejdź do katalogu głównego folderu minter-starter-files, a następnie uruchom npm install w terminalu, aby zainstalować zależności projektu:
cd minter-starter-filesnpm installPo zakończeniu instalacji uruchom npm start w terminalu:
npm startPowinno to otworzyć adres http://localhost:3000/ (opens in a new tab) w przeglądarce, gdzie zobaczysz frontend naszego projektu. Powinien on składać się z 3 pól: miejsca na wprowadzenie linku do zasobu NFT, wprowadzenie nazwy NFT i podanie opisu.
Jeśli spróbujesz kliknąć przyciski „Połącz portfel” lub „Mintuj NFT”, zauważysz, że nie działają – to dlatego, że wciąż musimy zaprogramować ich funkcjonalność! :)
Komponent Minter.js
UWAGA: Upewnij się, że jesteś w folderze minter-starter-files, a nie w folderze nft-minter!
Wróćmy do folderu src w naszym edytorze i otwórzmy plik Minter.js. Jest bardzo ważne, abyśmy zrozumieli wszystko w tym pliku, ponieważ jest to główny komponent React, nad którym będziemy pracować.
Na górze tego pliku mamy zmienne stanu, które będziemy aktualizować po określonych zdarzeniach.
1//Zmienne stanu2const [walletAddress, setWallet] = useState("")3const [status, setStatus] = useState("")4const [name, setName] = useState("")5const [description, setDescription] = useState("")6const [url, setURL] = useState("")Nigdy nie słyszałeś o zmiennych stanu React lub hookach stanu? Sprawdź tę (opens in a new tab) dokumentację.
Oto, co reprezentuje każda ze zmiennych:
walletAddress- ciąg znaków, który przechowuje adres portfela użytkownikastatus- ciąg znaków, który zawiera wiadomość do wyświetlenia na dole interfejsu użytkownikaname- ciąg znaków, który przechowuje nazwę NFTdescription- ciąg znaków, który przechowuje opis NFTurl- ciąg znaków, który jest linkiem do zasobu cyfrowego NFT
Po zmiennych stanu zobaczysz trzy niezimplementowane funkcje: useEffect, connectWalletPressed i onMintPressed. Zauważysz, że wszystkie te funkcje są async, ponieważ będziemy w nich wykonywać asynchroniczne wywołania API! Ich nazwy są tożsame z ich funkcjonalnością:
1useEffect(async () => {2 //TODO: zaimplementuj3}, [])45const connectWalletPressed = async () => {6 //TODO: zaimplementuj7}89const onMintPressed = async () => {10 //TODO: zaimplementuj11}Pokaż wszystkouseEffect(opens in a new tab) - to hook React, który jest wywoływany po wyrenderowaniu komponentu. Ponieważ ma przekazany pusty prop tablicy[](patrz linia 3), zostanie wywołany tylko przy pierwszym renderowaniu komponentu. Tutaj wywołamy nasz nasłuchiwacz portfela i inną funkcję portfela, aby zaktualizować nasz interfejs użytkownika, aby odzwierciedlić, czy portfel jest już podłączony.connectWalletPressed- ta funkcja zostanie wywołana, aby połączyć portfel MetaMask użytkownika z naszą dapką.onMintPressed- ta funkcja zostanie wywołana w celu mintowania NFT użytkownika.
Pod koniec tego pliku mamy interfejs użytkownika naszego komponentu. Jeśli uważnie przeskanujesz ten kod, zauważysz, że aktualizujemy nasze zmienne stanu url, name i description, gdy zmienia się dane wejściowe w odpowiadających im polach tekstowych.
Zobaczysz również, że connectWalletPressed i onMintPressed są wywoływane odpowiednio po kliknięciu przycisków o identyfikatorach mintButton i walletButton.
1//interfejs użytkownika naszego komponentu2return (3 <div className="Minter">4 <button id="walletButton" onClick={connectWalletPressed}>5 {walletAddress.length > 0 ? (6 "Połączono: " +7 String(walletAddress).substring(0, 6) +8 "..." +9 String(walletAddress).substring(38)10 ) : (11 <span>Połącz portfel</span>12 )}13 </button>1415 <br></br>16 <h1 id="title">🧙♂️ Minter NFT od Alchemy</h1>17 <p>18 Wystarczy dodać link do zasobu, nazwę i opis, a następnie nacisnąć „Mintuj”.19 </p>20 <form>21 <h2>🖼 Link do zasobu: </h2>22 <input23 type="text"24 placeholder="np. https://gateway.pinata.cloud/ipfs/<hash>"25 onChange={(event) => setURL(event.target.value)}26 />27 <h2>🤔 Nazwa: </h2>28 <input29 type="text"30 placeholder="np. Moje pierwsze NFT!"31 onChange={(event) => setName(event.target.value)}32 />33 <h2>✍️ Opis: </h2>34 <input35 type="text"36 placeholder="np. Jeszcze fajniejsze niż cryptokitties ;)"37 onChange={(event) => setDescription(event.target.value)}38 />39 </form>40 <button id="mintButton" onClick={onMintPressed}>41 Mintuj NFT42 </button>43 <p id="status">{status}</p>44</div>45)Pokaż wszystkoNa koniec zajmijmy się tym, gdzie ten komponent Minter jest dodawany.
Jeśli przejdziesz do pliku App.js, który jest głównym komponentem w React, działającym jako kontener dla wszystkich innych komponentów, zobaczysz, że nasz komponent Minter jest wstawiony w linii 7.
W tym samouczku będziemy edytować tylko plik Minter.js i dodawać pliki w naszym folderze src.
Teraz, gdy rozumiemy, z czym pracujemy, skonfigurujmy nasz portfel Ethereum!
Skonfiguruj swój portfel Ethereum
Aby użytkownicy mogli wchodzić w interakcję z Twoim inteligentnym kontraktem, będą musieli połączyć swój portfel Ethereum z Twoją dapką.
Pobierz MetaMask
W tym samouczku użyjemy MetaMask, wirtualnego portfela w przeglądarce, który służy do zarządzania adresem konta Ethereum. Jeśli chcesz dowiedzieć się więcej o tym, jak działają transakcje w Ethereum, sprawdź tę stronę.
Możesz pobrać i utworzyć konto MetaMask za darmo tutaj (opens in a new tab). Podczas tworzenia konta, lub jeśli już je masz, upewnij się, że przełączyłeś się na „sieć testową Ropsten” w prawym górnym rogu (abyśmy nie mieli do czynienia z prawdziwymi pieniędzmi).
Dodaj ether z Faucet
Aby mintować nasze NFT (lub podpisywać jakiekolwiek transakcje na blockchainie Ethereum), będziemy potrzebować trochę fałszywego Eth. Aby uzyskać Eth, możesz przejść do Ropsten faucet (opens in a new tab) i wprowadzić adres swojego konta Ropsten, a następnie kliknąć „Wyślij Ropsten Eth”. Wkrótce powinieneś zobaczyć Eth na swoim koncie MetaMask!
Sprawdź swoje saldo
Aby sprawdzić, czy nasze saldo jest na miejscu, wykonajmy żądanie eth_getBalance (opens in a new tab) za pomocą narzędzia kompozytora Alchemy (opens in a new tab). Zwróci to ilość Eth w naszym portfelu. Po wprowadzeniu adresu konta MetaMask i kliknięciu „Wyślij żądanie” powinieneś zobaczyć następującą odpowiedź:
1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}UWAGA: ten wynik jest w wei, a nie w eth. Wei jest używany jako najmniejsza jednostka etheru. Konwersja z wei na eth to: 1 eth = 10¹⁸ wei. Więc jeśli przekonwertujemy 0xde0b6b3a7640000 na system dziesiętny, otrzymamy 1*10¹⁸, co równa się 1 eth.
Uff! Nasze fałszywe pieniądze są na miejscu!
Połącz MetaMask ze swoim interfejsem użytkownika
Teraz, gdy nasz portfel MetaMask jest skonfigurowany, połączmy z nim naszą dapką!
Ponieważ chcemy stosować paradygmat MVC (opens in a new tab), stworzymy osobny plik, który będzie zawierał nasze funkcje do zarządzania logiką, danymi i zasadami naszej dapki, a następnie przekażemy te funkcje do naszego frontendu (naszego komponentu Minter.js).
Funkcja connectWallet
W tym celu utwórzmy nowy folder o nazwie utils w katalogu src i dodajmy do niego plik o nazwie interact.js, który będzie zawierał wszystkie nasze funkcje interakcji z portfelem i inteligentnym kontraktem.
W naszym pliku interact.js napiszemy funkcję connectWallet, którą następnie zaimportujemy i wywołamy w naszym komponencie Minter.js.
W pliku interact.js dodaj następujący kod:
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: "👆🏽 Wpisz wiadomość w polu tekstowym powyżej.",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 Musisz zainstalować MetaMask, wirtualny portfel Ethereum, w swojej27 przeglądarce.28 </a>29 </p>30 </span>31 ),32 }33 }34}Pokaż wszystkoPrzeanalizujmy, co robi ten kod:
Najpierw nasza funkcja sprawdza, czy window.ethereum jest włączone w Twojej przeglądarce.
window.ethereum to globalny interfejs API wstrzykiwany przez MetaMask i innych dostawców portfeli, który pozwala stronom internetowym na żądanie dostępu do kont Ethereum użytkowników. Po zatwierdzeniu może odczytywać dane z blockchainów, z którymi użytkownik jest połączony, i sugerować, aby użytkownik podpisywał wiadomości i transakcje. Sprawdź dokumentację MetaMask (opens in a new tab), aby uzyskać więcej informacji!
Jeśli window.ethereum nie jest obecne, oznacza to, że MetaMask nie jest zainstalowany. Powoduje to zwrócenie obiektu JSON, w którym zwrócony adres jest pustym ciągiem, a obiekt status JSX informuje, że użytkownik musi zainstalować MetaMask.
Większość funkcji, które piszemy, będzie zwracać obiekty JSON, których możemy użyć do aktualizacji naszych zmiennych stanu i interfejsu użytkownika.
Teraz, jeśli window.ethereum jest obecne, to wtedy robi się ciekawie.
Używając pętli try/catch, spróbujemy połączyć się z MetaMask, wywołując window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). Wywołanie tej funkcji otworzy MetaMask w przeglądarce, gdzie użytkownik zostanie poproszony o podłączenie swojego portfela do Twojej dapki.
- Jeśli użytkownik zdecyduje się połączyć,
metoda: "eth_requestAccounts"zwróci tablicę zawierającą wszystkie adresy kont użytkownika, które są połączone z dapką. W sumie nasza funkcjaconnectWalletzwróci obiekt JSON, który zawiera pierwszyadresw tej tablicy (patrz linia 9) oraz wiadomośćstatus, która prosi użytkownika o napisanie wiadomości do inteligentnego kontraktu. - Jeśli użytkownik odrzuci połączenie, obiekt JSON będzie zawierał pusty ciąg dla zwróconego
adresuoraz komunikatstatus, który odzwierciedla, że użytkownik odrzucił połączenie.
Dodaj funkcję connectWallet do komponentu interfejsu użytkownika Minter.js
Teraz, gdy napisaliśmy tę funkcję connectWallet, połączmy ją z naszym komponentem Minter.js.
Najpierw będziemy musieli zaimportować naszą funkcję do naszego pliku Minter.js, dodając import { connectWallet } from "./utils/interact.js"; na górze pliku Minter.js. Twoje pierwsze 11 linii Minter.js powinno teraz wyglądać tak:
1import { useEffect, useState } from "react";2import { connectWallet } from "./utils/interact.js";34const Minter = (props) => {56 //Zmienne stanu7 const [walletAddress, setWallet] = useState("");8 const [status, setStatus] = useState("");9 const [name, setName] = useState("");10 const [description, setDescription] = useState("");11 const [url, setURL] = useState("");Pokaż wszystkoNastępnie w naszej funkcji connectWalletPressed wywołamy naszą zaimportowaną funkcję connectWallet, w ten sposób:
1const connectWalletPressed = async () => {2 const walletResponse = await connectWallet()3 setStatus(walletResponse.status)4 setWallet(walletResponse.address)5}Zauważ, jak większość naszej funkcjonalności jest wyabstrahowana z naszego komponentu Minter.js z pliku interact.js? Robimy tak, aby zachować zgodność z paradygmatem M-V-C!
W connectWalletPressed po prostu wykonujemy wywołanie await do naszej zaimportowanej funkcji connectWallet, a za pomocą jej odpowiedzi aktualizujemy nasze zmienne status i walletAddress za pomocą ich hooków stanu.
Teraz zapiszmy oba pliki Minter.js i interact.js i przetestujmy nasz interfejs użytkownika.
Otwórz przeglądarkę pod adresem localhost:3000 i naciśnij przycisk „Połącz portfel” w prawym górnym rogu strony.
Jeśli masz zainstalowany MetaMask, powinieneś zostać poproszony o podłączenie swojego portfela do Twojej dapki. Zaakceptuj zaproszenie do połączenia.
Powinieneś zobaczyć, że przycisk portfela teraz odzwierciedla, że Twój adres jest podłączony.
Następnie spróbuj odświeżyć stronę... to jest dziwne. Nasz przycisk portfela prosi nas o podłączenie MetaMask, mimo że jest już podłączony...
Ale nie martw się! Możemy to łatwo naprawić, implementując funkcję o nazwie getCurrentWalletConnected, która sprawdzi, czy adres jest już podłączony do naszej dapki i odpowiednio zaktualizuje nasz interfejs użytkownika!
Funkcja getCurrentWalletConnected
W pliku interact.js dodaj następującą funkcję 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: "👆🏽 Wpisz wiadomość w polu tekstowym powyżej.",11 }12 } else {13 return {14 address: "",15 status: "🦊 Połącz się z MetaMask za pomocą przycisku w prawym górnym rogu.",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 Musisz zainstalować MetaMask, wirtualny portfel Ethereum, w swojej33 przeglądarce.34 </a>35 </p>36 </span>37 ),38 }39 }40}Pokaż wszystkoTen kod jest bardzo podobny do funkcji connectWallet, którą napisaliśmy wcześniej.
Główna różnica polega na tym, że zamiast wywoływać metodę eth_requestAccounts, która otwiera MetaMask, aby użytkownik mógł połączyć swój portfel, tutaj wywołujemy metodę eth_accounts, która po prostu zwraca tablicę zawierającą adresy MetaMask aktualnie połączone z naszą dapką.
Aby zobaczyć tę funkcję w akcji, wywołajmy ją w funkcji useEffect naszego komponentu Minter.js.
Tak jak zrobiliśmy to dla connectWallet, musimy zaimportować tę funkcję z naszego pliku interact.js do naszego pliku Minter.js w ten sposób:
1import { useEffect, useState } from "react"2import {3 connectWallet,4 getCurrentWalletConnected, //importuj tutaj5} from "./utils/interact.js"Teraz po prostu wywołujemy ją w naszej funkcji useEffect:
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)5}, [])Zauważ, że używamy odpowiedzi z naszego wywołania getCurrentWalletConnected, aby zaktualizować nasze zmienne stanu walletAddress i status.
Po dodaniu tego kodu spróbuj odświeżyć okno przeglądarki. Przycisk powinien informować, że jesteś połączony i pokazywać podgląd adresu podłączonego portfela - nawet po odświeżeniu!
Zaimplementuj addWalletListener
Ostatnim krokiem w konfiguracji portfela naszej dapki jest zaimplementowanie nasłuchiwacza portfela, aby nasz interfejs użytkownika aktualizował się, gdy zmieni się stan naszego portfela, na przykład gdy użytkownik się rozłączy lub zmieni konto.
W pliku Minter.js dodaj funkcję addWalletListener, która wygląda następująco:
1function addWalletListener() {2 if (window.ethereum) {3 window.ethereum.on("accountsChanged", (accounts) => {4 if (accounts.length > 0) {5 setWallet(accounts[0])6 setStatus("👆🏽 Wpisz wiadomość w polu tekstowym powyżej.")7 } else {8 setWallet("")9 setStatus("🦊 Połącz się z MetaMask za pomocą przycisku w prawym górnym rogu.")10 }11 })12 } else {13 setStatus(14 <p>15 {" "}16 🦊 <a target="_blank" href={`https://metamask.io/download`}>17 Musisz zainstalować MetaMask, wirtualny portfel Ethereum, w swojej przeglądarce.18 </a>19 </p>20 )21 }22}Pokaż wszystkoPrzeanalizujmy szybko, co się tutaj dzieje:
- Najpierw nasza funkcja sprawdza, czy
window.ethereumjest włączone (tj. MetaMask jest zainstalowany).- Jeśli nie jest, po prostu ustawiamy naszą zmienną stanu
statusna ciąg JSX, który prosi użytkownika o zainstalowanie MetaMask. - Jeśli jest włączone, ustawiamy nasłuchiwacz
window.ethereum.on("accountsChanged")w linii 3, który nasłuchuje zmian stanu w portfelu MetaMask, co obejmuje sytuacje, gdy użytkownik podłącza dodatkowe konto do dapki, zmienia konta lub odłącza konto. Jeśli co najmniej jedno konto jest połączone, zmienna stanuwalletAddressjest aktualizowana jako pierwsze konto w tablicykontazwróconej przez nasłuchiwacz. W przeciwnym raziewalletAddressjest ustawiany jako pusty ciąg.
- Jeśli nie jest, po prostu ustawiamy naszą zmienną stanu
Na koniec musimy wywołać go w naszej funkcji useEffect:
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)56 addWalletListener()7}, [])I voila! Ukończyliśmy programowanie całej funkcjonalności naszego portfela! Teraz, gdy nasz portfel jest skonfigurowany, dowiedzmy się, jak mintować nasze NFT!
Metadane NFT 101
Pamiętasz więc metadane NFT, o których właśnie mówiliśmy w kroku 0 tego samouczka – ożywiają one NFT, pozwalając mu mieć właściwości, takie jak zasób cyfrowy, nazwa, opis i inne atrybuty.
Będziemy musieli skonfigurować te metadane jako obiekt JSON i przechować go, abyśmy mogli przekazać go jako parametr tokenURI podczas wywoływania funkcji mintNFT naszego inteligentnego kontraktu.
Tekst w polach „Link do zasobu”, „Nazwa”, „Opis” będzie składał się z różnych właściwości metadanych naszego NFT. Sformatujemy te metadane jako obiekt JSON, ale istnieje kilka opcji, gdzie możemy przechowywać ten obiekt JSON:
- Moglibyśmy przechowywać go na blockchainie Ethereum; jednak byłoby to bardzo kosztowne.
- Moglibyśmy przechowywać go na scentralizowanym serwerze, takim jak AWS lub Firebase. Ale to zniweczyłoby nasz etos decentralizacji.
- Moglibyśmy użyć IPFS, zdecentralizowanego protokołu i sieci peer-to-peer do przechowywania i udostępniania danych w rozproszonym systemie plików. Ponieważ ten protokół jest zdecentralizowany i bezpłatny, jest to nasza najlepsza opcja!
Aby przechowywać nasze metadane w IPFS, użyjemy Pinata (opens in a new tab), wygodnego API i zestawu narzędzi IPFS. W następnym kroku wyjaśnimy dokładnie, jak to zrobić!
Użyj Pinata, aby przypiąć swoje metadane do IPFS
Jeśli nie masz konta Pinata (opens in a new tab), zarejestruj się, aby uzyskać bezpłatne konto tutaj (opens in a new tab) i wykonaj kroki, aby zweryfikować swój adres e-mail i konto.
Utwórz swój klucz API Pinata
Przejdź na stronę https://pinata.cloud/keys (opens in a new tab), a następnie wybierz przycisk „New Key” u góry, ustaw widżet Admin jako włączony i nazwij swój klucz.
Następnie pojawi się wyskakujące okienko z informacjami o Twoim API. Upewnij się, że umieściłeś to w bezpiecznym miejscu.
Teraz, gdy nasz klucz jest skonfigurowany, dodajmy go do naszego projektu, abyśmy mogli go użyć.
Utwórz plik .env
Możemy bezpiecznie przechowywać nasz klucz Pinata i sekret w pliku środowiskowym. Zainstalujmy pakiet dotenv (opens in a new tab) w katalogu projektu.
Otwórz nową kartę w terminalu (oddzielną od tej, na której działa host lokalny) i upewnij się, że jesteś w folderze minter-starter-files, a następnie uruchom następujące polecenie w terminalu:
1npm install dotenv --saveNastępnie utwórz plik .env w katalogu głównym minter-starter-files, wpisując w wierszu poleceń:
1vim.envTo otworzy twój plik .env w vim (edytorze tekstu). Aby go zapisać, naciśnij kolejno „esc” + „:” + „q” na klawiaturze.
Następnie w VSCode przejdź do pliku .env i dodaj do niego swój klucz API Pinata i sekret API, w ten sposób:
1REACT_APP_PINATA_KEY = <pinata-api-key>2REACT_APP_PINATA_SECRET = <pinata-api-secret>Zapisz plik, a następnie jesteś gotowy do rozpoczęcia pisania funkcji przesyłania metadanych JSON do IPFS!
Zaimplementuj pinJSONToIPFS
Na szczęście dla nas Pinata ma API specjalnie do przesyłania danych JSON do IPFS (opens in a new tab) i wygodny przykład JavaScript z axios, którego możemy użyć, z niewielkimi modyfikacjami.
W folderze utils utwórzmy kolejny plik o nazwie pinata.js, a następnie zaimportujmy nasz sekret i klucz Pinata z pliku .env w ten sposób:
1require("dotenv").config()2const key = process.env.REACT_APP_PINATA_KEY3const secret = process.env.REACT_APP_PINATA_SECRETNastępnie wklej dodatkowy kod z poniższego przykładu do pliku pinata.js. Nie martw się, wyjaśnimy, co wszystko oznacza!
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 //wykonywanie żądania POST axios do 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}Pokaż wszystkoWięc co dokładnie robi ten kod?
Po pierwsze, importuje axios (opens in a new tab), klienta HTTP opartego na obietnicach dla przeglądarki i node.js, którego użyjemy do złożenia żądania do Pinata.
Następnie mamy naszą asynchroniczną funkcję pinJSONToIPFS, która przyjmuje JSONBody jako dane wejściowe oraz klucz API i sekret Pinata w nagłówku, wszystko po to, aby wykonać żądanie POST do ich API pinJSONToIPFS.
- Jeśli to żądanie POST zakończy się pomyślnie, nasza funkcja zwraca obiekt JSON z wartością logiczną
successustawioną na true ipinataUrl, pod którym zostały przypięte nasze metadane. Użyjemy tego zwróconegopinataUrljako wejściatokenURIdo funkcji mintowania naszego inteligentnego kontraktu. - Jeśli to żądanie post nie powiedzie się, nasza funkcja zwróci obiekt JSON z wartością logiczną
successustawioną na false i ciągiemmessage, który przekaże nasz błąd.
Podobnie jak w przypadku typów zwrotnych funkcji connectWallet, zwracamy obiekty JSON, abyśmy mogli użyć ich parametrów do aktualizacji naszych zmiennych stanu i interfejsu użytkownika.
Wczytaj swój inteligentny kontrakt
Teraz, gdy mamy sposób na przesyłanie metadanych NFT do IPFS za pomocą naszej funkcji pinJSONToIPFS, będziemy potrzebować sposobu na załadowanie instancji naszego inteligentnego kontraktu, abyśmy mogli wywołać jego funkcję mintNFT.
Jak już wspomnieliśmy, w tym samouczku będziemy używać tego istniejącego inteligentnego kontraktu NFT (opens in a new tab); jeśli jednak chcesz dowiedzieć się, jak go stworzyliśmy, lub stworzyć własny, gorąco polecamy zapoznanie się z naszym innym samouczkiem, „Jak stworzyć NFT” (opens in a new tab).
ABI kontraktu
Jeśli dokładnie przeanalizowałeś nasze pliki, zauważysz, że w naszym katalogu src znajduje się plik contract-abi.json. ABI jest niezbędne do określenia, którą funkcję kontrakt wywoła, a także do zapewnienia, że funkcja zwróci dane w oczekiwanym formacie.
Będziemy również potrzebować klucza API Alchemy i API Alchemy Web3, aby połączyć się z blockchainem Ethereum i załadować nasz inteligentny kontrakt.
Utwórz klucz API Alchemy
Jeśli nie masz jeszcze konta Alchemy, zarejestruj się za darmo tutaj. (opens in a new tab)
Po utworzeniu konta Alchemy możesz wygenerować klucz API, tworząc aplikację. Pozwoli nam to na składanie żądań do sieci testowej Ropsten.
Przejdź na stronę „Create App” w swoim panelu Alchemy, najeżdżając kursorem na „Apps” w pasku nawigacyjnym i klikając „Create App”.
Nazwij swoją aplikację — my wybraliśmy „My First NFT!”, podaj krótki opis, wybierz „Staging” dla środowiska używanego do księgowania aplikacji i wybierz „Ropsten” dla swojej sieci.
Kliknij „Utwórz aplikację” i to wszystko! Twoja aplikacja powinna pojawić się w poniższej tabeli.
Wspaniale, teraz, gdy stworzyliśmy nasz adres URL HTTP API Alchemy, skopiuj go do schowka...
…a następnie dodajmy go do naszego pliku .env. W sumie twój plik .env powinien wyglądać tak:
1REACT_APP_PINATA_KEY = <pinata-key>2REACT_APP_PINATA_SECRET = <pinata-secret>3REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/<alchemy-key>Teraz, gdy mamy już ABI naszego kontraktu i klucz API Alchemy, jesteśmy gotowi do załadowania naszego inteligentnego kontraktu za pomocą Alchemy Web3 (opens in a new tab).
Skonfiguruj punkt końcowy Alchemy Web3 i kontrakt
Po pierwsze, jeśli jeszcze go nie masz, musisz zainstalować Alchemy Web3 (opens in a new tab), przechodząc do katalogu domowego: nft-minter-tutorial w terminalu:
1cd ..2npm install @alch/alchemy-web3Następnie wróćmy do naszego pliku interact.js. Na górze pliku dodaj następujący kod, aby zaimportować swój klucz Alchemy z pliku .env i skonfigurować swój punkt końcowy 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) to nakładka na Web3.js (opens in a new tab), zapewniająca ulepszone metody API i inne kluczowe korzyści, które ułatwiają życie dewelopera web3. Został zaprojektowany tak, aby wymagał minimalnej konfiguracji, dzięki czemu możesz od razu zacząć go używać w swojej aplikacji!
Następnie dodajmy do naszego pliku ABI kontraktu i adres kontraktu.
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"Gdy mamy już obie te rzeczy, jesteśmy gotowi do rozpoczęcia kodowania naszej funkcji mintowania!
Zaimplementuj funkcję mintNFT
W pliku interact.js zdefiniujmy naszą funkcję, mintNFT, która, jak sama nazwa wskazuje, będzie mintować nasze NFT.
Ponieważ będziemy wykonywać liczne wywołania asynchroniczne (do Pinata, aby przypiąć nasze metadane do IPFS, Alchemy Web3, aby załadować nasz inteligentny kontrakt, i MetaMask, aby podpisać nasze transakcje), nasza funkcja również będzie asynchroniczna.
Trzy dane wejściowe do naszej funkcji to url naszego zasobu cyfrowego, name i description. Dodaj następującą sygnaturę funkcji pod funkcją connectWallet:
1export const mintNFT = async (url, name, description) => {}Obsługa błędów wejściowych
Naturalnie, sensowne jest posiadanie jakiejś formy obsługi błędów wejściowych na początku funkcji, abyśmy opuścili tę funkcję, jeśli nasze parametry wejściowe nie są poprawne. Wewnątrz naszej funkcji dodajmy następujący kod:
1export const mintNFT = async (url, name, description) => {2 //obsługa błędów3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Upewnij się, że wszystkie pola są wypełnione przed mintowaniem.",7 }8 }9}Pokaż wszystkoZasadniczo, jeśli którykolwiek z parametrów wejściowych jest pustym ciągiem, zwracamy obiekt JSON, w którym wartość logiczna success jest fałszywa, a ciąg status informuje, że wszystkie pola w naszym interfejsie użytkownika muszą być wypełnione.
Przesyłanie metadanych do IPFS
Gdy już wiemy, że nasze metadane są poprawnie sformatowane, następnym krokiem jest opakowanie ich w obiekt JSON i przesłanie go do IPFS za pomocą funkcji pinJSONToIPFS, którą napisaliśmy!
W tym celu musimy najpierw zaimportować funkcję pinJSONToIPFS do naszego pliku interact.js. Na samym początku interact.js dodajmy:
1import { pinJSONToIPFS } from "./pinata.js"Przypomnijmy, że pinJSONToIPFS przyjmuje treść JSON. Więc zanim do niego zadzwonimy, będziemy musieli sformatować nasze parametry url, name i description w obiekt JSON.
Zaktualizujmy nasz kod, aby utworzyć obiekt JSON o nazwie metadata, a następnie wykonajmy wywołanie pinJSONToIPFS z tym parametrem metadata:
1export const mintNFT = async (url, name, description) => {2 //obsługa błędów3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Upewnij się, że wszystkie pola są wypełnione przed mintowaniem.",7 }8 }910 //tworzenie metadanych11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description1516 //wywołanie pinata17 const pinataResponse = await pinJSONToIPFS(metadata)18 if (!pinataResponse.success) {19 return {20 success: false,21 status: "😢 Coś poszło nie tak podczas przesyłania Twojego tokenURI.",22 }23 }24 const tokenURI = pinataResponse.pinataUrl25}Pokaż wszystkoZauważ, że przechowujemy odpowiedź naszego wywołania do pinJSONToIPFS(metadata) w obiekcie pinataResponse. Następnie analizujemy ten obiekt pod kątem ewentualnych błędów.
Jeśli wystąpi błąd, zwracamy obiekt JSON, w którym wartość logiczna success jest fałszywa, a nasz ciąg status informuje, że nasze wywołanie nie powiodło się. W przeciwnym razie wyodrębniamy pinataURL z pinataResponse i przechowujemy go jako naszą zmienną tokenURI.
Teraz nadszedł czas, aby załadować nasz inteligentny kontrakt za pomocą Alchemy Web3 API, które zainicjowaliśmy na początku naszego pliku. Dodaj następującą linię kodu na dole funkcji mintNFT, aby ustawić kontrakt w globalnej zmiennej window.contract:
1window.contract = await new web3.eth.Contract(contractABI, contractAddress)Ostatnią rzeczą do dodania w naszej funkcji mintNFT jest nasza transakcja Ethereum:
1//ustaw swoją transakcję Ethereum2const transactionParameters = {3 to: contractAddress, // Wymagane, z wyjątkiem publikacji kontraktów.4 from: window.ethereum.selectedAddress, // musi pasować do aktywnego adresu użytkownika.5 data: window.contract.methods6 .mintNFT(window.ethereum.selectedAddress, tokenURI)7 .encodeABI(), //wykonaj wywołanie do inteligentnego kontraktu NFT8}910//podpisz transakcję przez MetaMask11try {12 const txHash = await window.ethereum.request({13 method: "eth_sendTransaction",14 params: [transactionParameters],15 })16 return {17 success: true,18 status:19 "✅ Sprawdź swoją transakcję na Etherscan: https://ropsten.etherscan.io/tx/" +20 txHash,21 }22} catch (error) {23 return {24 success: false,25 status: "😥 Coś poszło nie tak: " + error.message,26 }27}Pokaż wszystkoJeśli jesteś już zaznajomiony z transakcjami Ethereum, zauważysz, że struktura jest dość podobna do tego, co widziałeś.
- Po pierwsze, ustawiamy nasze parametry transakcji.
tookreśla adres odbiorcy (nasz inteligentny kontrakt)fromokreśla sygnatariusza transakcji (połączony adres użytkownika z MetaMask:window.ethereum.selectedAddress)datazawiera wywołanie metodymintNFTnaszego inteligentnego kontraktu, która jako dane wejściowe otrzymuje nasztokenURIi adres portfela użytkownika,window.ethereum.selectedAddress
- Następnie wykonujemy wywołanie await,
window.ethereum.request,, w którym prosimy MetaMask o podpisanie transakcji. Zauważ, że w tym żądaniu określamy naszą metodę eth (eth_SentTransaction) i przekazujemy naszetransactionParameters. W tym momencie w przeglądarce otworzy się MetaMask i poprosi użytkownika o podpisanie lub odrzucenie transakcji.- Jeśli transakcja zakończy się powodzeniem, funkcja zwróci obiekt JSON, w którym wartość logiczna
successjest ustawiona na true, a ciągstatusprosi użytkownika o sprawdzenie Etherscan w celu uzyskania dalszych informacji o transakcji. - Jeśli transakcja się nie powiedzie, funkcja zwróci obiekt JSON, w którym wartość logiczna
successjest ustawiona na false, a ciągstatusprzekazuje komunikat o błędzie.
- Jeśli transakcja zakończy się powodzeniem, funkcja zwróci obiekt JSON, w którym wartość logiczna
W sumie nasza funkcja mintNFT powinna wyglądać tak:
1export const mintNFT = async (url, name, description) => {2 //obsługa błędów3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Upewnij się, że wszystkie pola są wypełnione przed mintowaniem.",7 }8 }910 //tworzenie metadanych11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description1516 //żądanie przypięcia pinata17 const pinataResponse = await pinJSONToIPFS(metadata)18 if (!pinataResponse.success) {19 return {20 success: false,21 status: "😢 Coś poszło nie tak podczas przesyłania Twojego tokenURI.",22 }23 }24 const tokenURI = pinataResponse.pinataUrl2526 //wczytaj inteligentny kontrakt27 window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract();2829 //ustaw swoją transakcję Ethereum30 const transactionParameters = {31 to: contractAddress, // Wymagane, z wyjątkiem publikacji kontraktów.32 from: window.ethereum.selectedAddress, // musi pasować do aktywnego adresu użytkownika.33 data: window.contract.methods34 .mintNFT(window.ethereum.selectedAddress, tokenURI)35 .encodeABI(), //wykonaj wywołanie do inteligentnego kontraktu NFT36 }3738 //podpisz transakcję przez 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 "✅ Sprawdź swoją transakcję na Etherscan: https://ropsten.etherscan.io/tx/" +48 txHash,49 }50 } catch (error) {51 return {52 success: false,53 status: "😥 Coś poszło nie tak: " + error.message,54 }55 }56}Pokaż wszystkoTo jest jedna gigantyczna funkcja! Teraz musimy tylko podłączyć naszą funkcję mintNFT do naszego komponentu Minter.js...
Podłącz mintNFT do naszego frontendu Minter.js
Otwórz plik Minter.js i zaktualizuj linię import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js"; na górze, aby wyglądała tak:
1import {2 connectWallet,3 getCurrentWalletConnected,4 mintNFT,5} from "./utils/interact.js"Na koniec zaimplementuj funkcję onMintPressed, aby wykonać wywołanie await do zaimportowanej funkcji mintNFT i zaktualizować zmienną stanu status, aby odzwierciedlić, czy nasza transakcja powiodła się, czy nie:
1const onMintPressed = async () => {2 const { status } = await mintNFT(url, name, description)3 setStatus(status)4}Wdróż swoje NFT na działającej stronie internetowej
Gotowy na udostępnienie swojego projektu na żywo, aby użytkownicy mogli z nim wchodzić w interakcję? Sprawdź ten samouczek (opens in a new tab) dotyczący wdrażania Twojego Minera na działającej stronie internetowej.
Jeszcze jeden krok...
Szturmem zdobądź świat blockchaina
Żartuję, dotarłeś do końca samouczka!
Podsumowując, budując minter NFT, z powodzeniem nauczyłeś się, jak:
- Połączyć się z MetaMask za pośrednictwem projektu frontendowego
- Wywoływać metody inteligentnego kontraktu z poziomu frontendu
- Podpisywać transakcje za pomocą MetaMask
Prawdopodobnie chcesz mieć możliwość pochwalenia się NFT wygenerowanymi za pośrednictwem Twojej dapki w swoim portfelu — więc koniecznie sprawdź nasz krótki samouczek „Jak wyświetlić swoje NFT w portfelu” (opens in a new tab)!
I, jak zawsze, jeśli masz jakieś pytania, jesteśmy tutaj, aby pomóc na Discordzie Alchemy (opens in a new tab). Nie możemy się doczekać, aby zobaczyć, jak zastosujesz koncepcje z tego samouczka w swoich przyszłych projektach!
Strona ostatnio zaktualizowana: 25 lutego 2026