Lompat ke konten utama

Tutorial Minter NFT

Solidity
NFT
Alchemy
kontrak pintar
frontend
Pinata
erc-721
Menengah
smudgil
6 Oktober 2021
27 menit baca

Salah satu tantangan terbesar bagi pengembang yang berasal dari latar belakang Web2 adalah mencari tahu cara menghubungkan kontrak pintar Anda ke proyek frontend dan berinteraksi dengannya.

Dengan membangun minter NFT — UI sederhana di mana Anda dapat memasukkan tautan ke aset digital Anda, judul, dan deskripsi — Anda akan belajar cara:

  • Terhubung ke MetaMask melalui proyek frontend Anda
  • Memanggil metode kontrak pintar dari frontend Anda
  • Menandatangani transaksi menggunakan MetaMask

Dalam tutorial ini, kita akan menggunakan React (opens in a new tab) sebagai kerangka kerja frontend kita. Karena tutorial ini terutama difokuskan pada pengembangan Web3, kita tidak akan menghabiskan banyak waktu untuk menguraikan dasar-dasar React. Sebaliknya, kita akan fokus pada membawa fungsionalitas ke proyek kita.

Sebagai prasyarat, Anda harus memiliki pemahaman tingkat pemula tentang React—mengetahui cara kerja komponen, props, useState/useEffect, dan pemanggilan fungsi dasar. Jika Anda belum pernah mendengar istilah-istilah tersebut sebelumnya, Anda mungkin ingin memeriksa Tutorial Pengantar React (opens in a new tab) ini. Untuk pembelajar yang lebih visual, kami sangat merekomendasikan seri video Tutorial React Modern Lengkap (opens in a new tab) yang sangat bagus ini oleh Net Ninja.

Dan jika Anda belum melakukannya, Anda pasti akan membutuhkan akun Alchemy untuk menyelesaikan tutorial ini serta membangun apa pun di blockchain. Daftar untuk akun gratis di sini (opens in a new tab).

Tanpa basa-basi lagi, mari kita mulai!

Dasar-dasar Membuat NFT

Sebelum kita mulai melihat kode apa pun, penting untuk memahami cara kerja pembuatan NFT. Ini melibatkan dua langkah:

Menerbitkan kontrak pintar NFT di blockchain Ethereum

Perbedaan terbesar antara kedua standar kontrak pintar NFT adalah bahwa ERC-1155 merupakan standar multi-token dan mencakup fungsionalitas batch, sedangkan ERC-721 adalah standar token tunggal dan oleh karena itu hanya mendukung transfer satu token pada satu waktu.

Memanggil fungsi minting

Biasanya, fungsi minting ini mengharuskan Anda untuk meneruskan dua variabel sebagai parameter, pertama recipient, yang menentukan alamat yang akan menerima NFT yang baru saja Anda mint, dan kedua tokenURI NFT, sebuah string yang mengarah ke dokumen JSON yang mendeskripsikan metadata NFT.

Metadata NFT adalah hal yang benar-benar menghidupkannya, memungkinkannya memiliki properti, seperti nama, deskripsi, gambar (atau aset digital yang berbeda), dan atribut lainnya. Berikut adalah contoh tokenURI (opens in a new tab), yang berisi metadata NFT.

Dalam tutorial ini, kita akan fokus pada bagian 2, memanggil fungsi minting kontrak pintar NFT yang sudah ada menggunakan UI React kita.

Berikut adalah tautan (opens in a new tab) ke kontrak pintar NFT ERC-721 yang akan kita panggil dalam tutorial ini. Jika Anda ingin mempelajari cara kami membuatnya, kami sangat menyarankan Anda untuk memeriksa tutorial kami yang lain, "Cara Membuat NFT" (opens in a new tab).

Keren, sekarang setelah kita memahami cara kerja pembuatan NFT, mari kita kloning file awal kita!

Kloning file awal

Pertama, buka repositori GitHub nft-minter-tutorial (opens in a new tab) untuk mendapatkan file awal untuk proyek ini. Kloning repositori ini ke lingkungan lokal Anda.

Saat Anda membuka repositori nft-minter-tutorial yang dikloning ini, Anda akan melihat bahwa repositori ini berisi dua folder: minter-starter-files dan nft-minter.

  • minter-starter-files berisi file awal (pada dasarnya UI React) untuk proyek ini. Dalam tutorial ini, kita akan bekerja di direktori ini, saat Anda belajar cara menghidupkan UI ini dengan menghubungkannya ke dompet Ethereum Anda dan kontrak pintar NFT.
  • nft-minter berisi seluruh tutorial yang telah selesai dan ada untuk Anda sebagai referensi jika Anda mengalami kebuntuan.

Selanjutnya, buka salinan minter-starter-files Anda di editor kode Anda, lalu navigasikan ke folder src Anda.

Semua kode yang akan kita tulis akan berada di bawah folder src. Kita akan mengedit komponen Minter.js dan menulis file javascript tambahan untuk memberikan fungsionalitas Web3 pada proyek kita.

Langkah 2: Periksa file awal kita

Sebelum kita mulai membuat kode, penting untuk memeriksa apa yang sudah disediakan untuk kita di file awal.

Jalankan proyek react Anda

Mari kita mulai dengan menjalankan proyek React di browser kita. Keindahan React adalah setelah proyek kita berjalan di browser, setiap perubahan yang kita simpan akan diperbarui secara langsung di browser kita.

Untuk menjalankan proyek, navigasikan ke direktori root dari folder minter-starter-files, dan jalankan npm install di terminal Anda untuk menginstal dependensi proyek:

cd minter-starter-files
npm install

Setelah selesai menginstal, jalankan npm start di terminal Anda:

npm start

Melakukan hal itu akan membuka http://localhost:3000/ (opens in a new tab) di browser Anda, di mana Anda akan melihat frontend untuk proyek kita. Ini harus terdiri dari 3 bidang: tempat untuk memasukkan tautan ke aset NFT Anda, memasukkan nama NFT Anda, dan memberikan deskripsi.

Jika Anda mencoba mengklik tombol "Connect Wallet" atau "Mint NFT", Anda akan melihat bahwa tombol tersebut tidak berfungsi—itu karena kita masih perlu memprogram fungsionalitasnya! :)

Komponen Minter.js

CATATAN: Pastikan Anda berada di folder minter-starter-files dan bukan di folder nft-minter!

Mari kembali ke folder src di editor kita dan buka file Minter.js. Sangat penting bagi kita untuk memahami semua yang ada di file ini, karena ini adalah komponen React utama yang akan kita kerjakan.

Di bagian atas file ini, kita memiliki variabel status yang akan kita perbarui setelah peristiwa tertentu.

1//State variables // Variabel state
2const [walletAddress, setWallet] = useState("")
3const [status, setStatus] = useState("")
4const [name, setName] = useState("")
5const [description, setDescription] = useState("")
6const [url, setURL] = useState("")

Belum pernah mendengar tentang variabel status React atau hook status? Periksa dokumentasi ini (opens in a new tab).

Berikut adalah apa yang diwakili oleh masing-masing variabel:

  • walletAddress - sebuah string yang menyimpan alamat dompet pengguna
  • status - sebuah string yang berisi pesan untuk ditampilkan di bagian bawah UI
  • name - sebuah string yang menyimpan nama NFT
  • description - sebuah string yang menyimpan deskripsi NFT
  • url - sebuah string yang merupakan tautan ke aset digital NFT

Setelah variabel status, Anda akan melihat tiga fungsi yang belum diimplementasikan: useEffect, connectWalletPressed, dan onMintPressed. Anda akan melihat bahwa semua fungsi ini adalah async, itu karena kita akan melakukan panggilan API asinkron di dalamnya! Nama-nama mereka sesuai dengan fungsionalitasnya:

1useEffect(async () => {
2 //TODO: implement // TODO: implementasikan
3}, [])
4
5const connectWalletPressed = async () => {
6 //TODO: implement // TODO: implementasikan
7}
8
9const onMintPressed = async () => {
10 //TODO: implement // TODO: implementasikan
11}
Tampilkan semua
  • useEffect (opens in a new tab) - ini adalah hook React yang dipanggil setelah komponen Anda dirender. Karena ia memiliki prop array kosong [] yang diteruskan ke dalamnya (lihat baris 3), ia hanya akan dipanggil pada render pertama komponen. Di sini kita akan memanggil pendengar dompet kita dan fungsi dompet lainnya untuk memperbarui UI kita guna mencerminkan apakah dompet sudah terhubung.
  • connectWalletPressed - fungsi ini akan dipanggil untuk menghubungkan dompet MetaMask pengguna ke dapp kita.
  • onMintPressed - fungsi ini akan dipanggil untuk melakukan mint NFT pengguna.

Di dekat akhir file ini, kita memiliki UI dari komponen kita. Jika Anda memindai kode ini dengan cermat, Anda akan melihat bahwa kita memperbarui variabel status url, name, dan description kita ketika input di bidang teks yang sesuai berubah.

Anda juga akan melihat bahwa connectWalletPressed dan onMintPressed dipanggil ketika tombol dengan ID mintButton dan walletButton masing-masing diklik.

1//the UI of our component // UI dari komponen kita
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)
Tampilkan semua

Terakhir, mari kita bahas di mana komponen Minter ini ditambahkan.

Jika Anda pergi ke file App.js, yang merupakan komponen utama di React yang bertindak sebagai wadah untuk semua komponen lainnya, Anda akan melihat bahwa komponen Minter kita disuntikkan pada baris 7.

Dalam tutorial ini, kita hanya akan mengedit file Minter.js dan menambahkan file di folder src kita.

Sekarang setelah kita memahami apa yang sedang kita kerjakan, mari kita siapkan dompet Ethereum kita!

Siapkan dompet Ethereum Anda

Agar pengguna dapat berinteraksi dengan kontrak pintar Anda, mereka perlu menghubungkan dompet Ethereum mereka ke dapp Anda.

Unduh MetaMask

Untuk tutorial ini, kita akan menggunakan MetaMask, dompet virtual di browser yang digunakan untuk mengelola alamat akun Ethereum Anda. Jika Anda ingin memahami lebih lanjut tentang cara kerja transaksi di Ethereum, periksa halaman ini.

Anda dapat mengunduh dan membuat akun MetaMask secara gratis di sini (opens in a new tab). Saat Anda membuat akun, atau jika Anda sudah memiliki akun, pastikan untuk beralih ke “Ropsten Test Network” di kanan atas (sehingga kita tidak berurusan dengan uang sungguhan).

Tambahkan ether dari Faucet

Untuk melakukan mint NFT kita (atau menandatangani transaksi apa pun di blockchain Ethereum), kita akan membutuhkan beberapa Eth palsu. Untuk mendapatkan Eth, Anda dapat pergi ke faucet Ropsten (opens in a new tab) dan memasukkan alamat akun Ropsten Anda, lalu klik “Send Ropsten Eth.” Anda akan melihat Eth di akun MetaMask Anda segera setelahnya!

Periksa saldo Anda

Untuk memeriksa kembali apakah saldo kita ada di sana, mari kita buat permintaan eth_getBalance (opens in a new tab) menggunakan alat komposer Alchemy (opens in a new tab). Ini akan mengembalikan jumlah Eth di dompet kita. Setelah Anda memasukkan alamat akun MetaMask Anda dan mengklik “Send Request”, Anda akan melihat respons seperti ini:

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

CATATAN: Hasil ini dalam wei, bukan eth. Wei digunakan sebagai denominasi terkecil dari ether. Konversi dari wei ke eth adalah: 1 eth = 10¹⁸ wei. Jadi jika kita mengonversi 0xde0b6b3a7640000 ke desimal, kita mendapatkan 1*10¹⁸ yang sama dengan 1 eth.

Fiuh! Uang palsu kita semuanya ada di sana!

Hubungkan MetaMask ke UI Anda

Sekarang setelah dompet MetaMask kita disiapkan, mari kita hubungkan dapp kita ke sana!

Karena kita ingin mengikuti paradigma MVC (opens in a new tab), kita akan membuat file terpisah yang berisi fungsi kita untuk mengelola logika, data, dan aturan dapp kita, lalu meneruskan fungsi tersebut ke frontend kita (komponen Minter.js kita).

Fungsi connectWallet

Untuk melakukannya, mari kita buat folder baru bernama utils di direktori src Anda dan tambahkan file bernama interact.js di dalamnya, yang akan berisi semua fungsi interaksi dompet dan kontrak pintar kita.

Di file interact.js kita, kita akan menulis fungsi connectWallet, yang kemudian akan kita impor dan panggil di komponen Minter.js kita.

Di file interact.js Anda, tambahkan yang berikut ini

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}
Tampilkan semua

Mari kita uraikan apa yang dilakukan kode ini:

Pertama, fungsi kita memeriksa apakah window.ethereum diaktifkan di browser Anda.

window.ethereum adalah API global yang disuntikkan oleh MetaMask dan penyedia dompet lainnya yang memungkinkan situs web untuk meminta akun Ethereum pengguna. Jika disetujui, ia dapat membaca data dari blockchain yang terhubung dengan pengguna, dan menyarankan agar pengguna menandatangani pesan dan transaksi. Periksa dokumentasi MetaMask (opens in a new tab) untuk info lebih lanjut!

Jika window.ethereum tidak ada, maka itu berarti MetaMask tidak diinstal. Ini menghasilkan objek JSON yang dikembalikan, di mana address yang dikembalikan adalah string kosong, dan objek JSX status menyampaikan bahwa pengguna harus menginstal MetaMask.

Sebagian besar fungsi yang kita tulis akan mengembalikan objek JSON yang dapat kita gunakan untuk memperbarui variabel status dan UI kita.

Sekarang jika window.ethereum ada, maka di situlah hal-hal menjadi menarik.

Menggunakan loop try/catch, kita akan mencoba terhubung ke MetaMask dengan memanggil window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). Memanggil fungsi ini akan membuka MetaMask di browser, di mana pengguna akan diminta untuk menghubungkan dompet mereka ke dapp Anda.

  • Jika pengguna memilih untuk terhubung, method: "eth_requestAccounts" akan mengembalikan array yang berisi semua alamat akun pengguna yang terhubung ke dapp. Secara keseluruhan, fungsi connectWallet kita akan mengembalikan objek JSON yang berisi address pertama dalam array ini (lihat baris 9) dan pesan status yang meminta pengguna untuk menulis pesan ke kontrak pintar.
  • Jika pengguna menolak koneksi, maka objek JSON akan berisi string kosong untuk address yang dikembalikan dan pesan status yang mencerminkan bahwa pengguna menolak koneksi.

Tambahkan fungsi connectWallet ke Komponen UI Minter.js Anda

Sekarang setelah kita menulis fungsi connectWallet ini, mari kita hubungkan ke komponen Minter.js. kita.

Pertama, kita harus mengimpor fungsi kita ke dalam file Minter.js kita dengan menambahkan import { connectWallet } from "./utils/interact.js"; ke bagian atas file Minter.js. 11 baris pertama Minter.js Anda sekarang akan terlihat seperti ini:

1import { useEffect, useState } from "react";
2import { connectWallet } from "./utils/interact.js";
3
4const Minter = (props) => {
5
6 //State variables // Variabel state
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("");
Tampilkan semua

Kemudian, di dalam fungsi connectWalletPressed kita, kita akan memanggil fungsi connectWallet yang diimpor, seperti ini:

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

Perhatikan bagaimana sebagian besar fungsionalitas kita diabstraksikan dari komponen Minter.js kita dari file interact.js? Ini agar kita mematuhi paradigma M-V-C!

Di connectWalletPressed, kita cukup melakukan panggilan await ke fungsi connectWallet yang diimpor, dan menggunakan responsnya, kita memperbarui variabel status dan walletAddress kita melalui hook status mereka.

Sekarang, mari kita simpan kedua file Minter.js dan interact.js dan uji UI kita sejauh ini.

Buka browser Anda di localhost:3000, dan tekan tombol "Connect Wallet" di kanan atas halaman.

Jika Anda telah menginstal MetaMask, Anda akan diminta untuk menghubungkan dompet Anda ke dapp Anda. Terima undangan untuk terhubung.

Anda akan melihat bahwa tombol dompet sekarang mencerminkan bahwa alamat Anda terhubung.

Selanjutnya, coba segarkan halaman... ini aneh. Tombol dompet kita meminta kita untuk menghubungkan MetaMask, meskipun sudah terhubung...

Namun jangan khawatir! Kita dapat dengan mudah memperbaikinya dengan mengimplementasikan fungsi yang disebut getCurrentWalletConnected, yang akan memeriksa apakah sebuah alamat sudah terhubung ke dapp kita dan memperbarui UI kita sesuai dengan itu!

Fungsi getCurrentWalletConnected

Di file interact.js Anda, tambahkan fungsi getCurrentWalletConnected berikut:

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}
Tampilkan semua

Kode ini sangat mirip dengan fungsi connectWallet yang baru saja kita tulis sebelumnya.

Perbedaan utamanya adalah alih-alih memanggil metode eth_requestAccounts, yang membuka MetaMask bagi pengguna untuk menghubungkan dompet mereka, di sini kita memanggil metode eth_accounts, yang hanya mengembalikan array yang berisi alamat MetaMask yang saat ini terhubung ke dapp kita.

Untuk melihat fungsi ini beraksi, mari kita panggil di fungsi useEffect dari komponen Minter.js kita.

Seperti yang kita lakukan untuk connectWallet, kita harus mengimpor fungsi ini dari file interact.js kita ke dalam file Minter.js kita seperti ini:

1import { useEffect, useState } from "react"
2import {
3 connectWallet,
4 getCurrentWalletConnected, //import here // impor di sini
5} from "./utils/interact.js"

Sekarang, kita cukup memanggilnya di fungsi useEffect kita:

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

Perhatikan, kita menggunakan respons dari panggilan kita ke getCurrentWalletConnected untuk memperbarui variabel status walletAddress dan status kita.

Setelah Anda menambahkan kode ini, coba segarkan jendela browser kita. Tombol tersebut harus mengatakan bahwa Anda terhubung, dan menampilkan pratinjau alamat dompet Anda yang terhubung - bahkan setelah Anda menyegarkan!

Implementasikan addWalletListener

Langkah terakhir dalam penyiapan dompet dapp kita adalah mengimplementasikan pendengar dompet sehingga UI kita diperbarui ketika status dompet kita berubah, seperti ketika pengguna memutuskan sambungan atau beralih akun.

Di file Minter.js Anda, tambahkan fungsi addWalletListener yang terlihat seperti berikut:

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}
Tampilkan semua

Mari kita uraikan dengan cepat apa yang terjadi di sini:

  • Pertama, fungsi kita memeriksa apakah window.ethereum diaktifkan (yaitu, MetaMask diinstal).
    • Jika tidak, kita cukup mengatur variabel status status kita ke string JSX yang meminta pengguna untuk menginstal MetaMask.
    • Jika diaktifkan, kita menyiapkan pendengar window.ethereum.on("accountsChanged") pada baris 3 yang mendengarkan perubahan status di dompet MetaMask, yang mencakup saat pengguna menghubungkan akun tambahan ke dapp, beralih akun, atau memutuskan sambungan akun. Jika ada setidaknya satu akun yang terhubung, variabel status walletAddress diperbarui sebagai akun pertama dalam array accounts yang dikembalikan oleh pendengar. Jika tidak, walletAddress diatur sebagai string kosong.

Terakhir, kita harus memanggilnya di fungsi useEffect kita:

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

Dan voila! Kita telah selesai memprogram semua fungsionalitas dompet kita! Sekarang setelah dompet kita disiapkan, mari kita cari tahu cara melakukan mint NFT kita!

Dasar-dasar Metadata NFT

Jadi ingat metadata NFT yang baru saja kita bicarakan di Langkah 0 dari tutorial ini—itu menghidupkan NFT, memungkinkannya memiliki properti, seperti aset digital, nama, deskripsi, dan atribut lainnya.

Kita perlu mengonfigurasi metadata ini sebagai objek JSON dan menyimpannya, sehingga kita dapat meneruskannya sebagai parameter tokenURI saat memanggil fungsi mintNFT kontrak pintar kita.

Teks di bidang "Link to Asset", "Name", "Description" akan terdiri dari berbagai properti metadata NFT kita. Kita akan memformat metadata ini sebagai objek JSON, tetapi ada beberapa opsi untuk tempat kita dapat menyimpan objek JSON ini:

  • Kita bisa menyimpannya di blockchain Ethereum; namun, melakukan hal itu akan sangat mahal.
  • Kita bisa menyimpannya di server terpusat, seperti AWS atau Firebase. Tapi itu akan mengalahkan etos desentralisasi kita.
  • Kita bisa menggunakan IPFS, protokol desentralisasi dan jaringan peer-to-peer untuk menyimpan dan berbagi data dalam sistem file terdistribusi. Karena protokol ini terdesentralisasi dan gratis, ini adalah opsi terbaik kita!

Untuk menyimpan metadata kita di IPFS, kita akan menggunakan Pinata (opens in a new tab), API dan toolkit IPFS yang nyaman. Pada langkah berikutnya, kita akan menjelaskan dengan tepat cara melakukan ini!

Gunakan Pinata untuk menyematkan metadata Anda ke IPFS

Jika Anda tidak memiliki akun Pinata (opens in a new tab), daftar untuk akun gratis di sini (opens in a new tab) dan selesaikan langkah-langkah untuk memverifikasi email dan akun Anda.

Buat kunci API Pinata Anda

Navigasikan ke halaman https://pinata.cloud/keys (opens in a new tab), lalu pilih tombol "New Key" di bagian atas, atur widget Admin sebagai diaktifkan, dan beri nama kunci Anda.

Anda kemudian akan diperlihatkan popup dengan info API Anda. Pastikan untuk meletakkannya di tempat yang aman.

Sekarang setelah kunci kita disiapkan, mari kita tambahkan ke proyek kita sehingga kita dapat menggunakannya.

Buat file .env

Kita dapat dengan aman menyimpan kunci dan rahasia Pinata kita dalam file lingkungan. Mari kita instal paket dotenv (opens in a new tab) di direktori proyek Anda.

Buka tab baru di terminal Anda (terpisah dari yang menjalankan host lokal) dan pastikan Anda berada di folder minter-starter-files, lalu jalankan perintah berikut di terminal Anda:

1npm install dotenv --save

Selanjutnya, buat file .env di direktori root dari minter-starter-files Anda dengan memasukkan yang berikut ini di baris perintah Anda:

1vim.env

Ini akan membuka file .env Anda di vim (editor teks). Untuk menyimpannya tekan "esc" + ":" + "q" pada keyboard Anda dalam urutan itu.

Selanjutnya, di VSCode, navigasikan ke file .env Anda dan tambahkan kunci API Pinata dan rahasia API Anda ke dalamnya, seperti ini:

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

Simpan file, dan kemudian Anda siap untuk mulai menulis fungsi untuk mengunggah metadata JSON Anda ke IPFS!

Implementasikan pinJSONToIPFS

Untungnya bagi kita, Pinata memiliki API khusus untuk mengunggah data JSON ke IPFS (opens in a new tab) dan contoh JavaScript dengan axios yang nyaman yang dapat kita gunakan, dengan beberapa modifikasi kecil.

Di folder utils Anda, mari kita buat file lain bernama pinata.js dan kemudian impor rahasia dan kunci Pinata kita dari file .env seperti ini:

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

Selanjutnya, tempelkan kode tambahan dari bawah ke dalam file pinata.js Anda. Jangan khawatir, kita akan menguraikan apa arti semuanya!

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 //making axios POST request to Pinata ⬇️ // membuat permintaan POST axios ke 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}
Tampilkan semua

Jadi apa sebenarnya yang dilakukan kode ini?

Pertama, ia mengimpor axios (opens in a new tab), klien HTTP berbasis promise untuk browser dan node.js, yang akan kita gunakan untuk membuat permintaan ke Pinata.

Kemudian kita memiliki fungsi asinkron kita pinJSONToIPFS, yang mengambil JSONBody sebagai inputnya dan kunci api serta rahasia Pinata di headernya, semuanya untuk membuat permintaan POST ke API pinJSONToIPFS mereka.

  • Jika permintaan POST ini berhasil, maka fungsi kita mengembalikan objek JSON dengan boolean success sebagai true dan pinataUrl tempat metadata kita disematkan. Kita akan menggunakan pinataUrl yang dikembalikan ini sebagai input tokenURI ke fungsi mint kontrak pintar kita.
  • Jika permintaan post ini gagal, maka fungsi kita mengembalikan objek JSON dengan boolean success sebagai false dan string message yang menyampaikan kesalahan kita.

Sama seperti tipe pengembalian fungsi connectWallet kita, kita mengembalikan objek JSON sehingga kita dapat menggunakan parameternya untuk memperbarui variabel status dan UI kita.

Muat kontrak pintar Anda

Sekarang setelah kita memiliki cara untuk mengunggah metadata NFT kita ke IPFS melalui fungsi pinJSONToIPFS kita, kita akan membutuhkan cara untuk memuat instance kontrak pintar kita sehingga kita dapat memanggil fungsi mintNFT-nya.

Seperti yang kami sebutkan sebelumnya, dalam tutorial ini kita akan menggunakan kontrak pintar NFT yang sudah ada ini (opens in a new tab); namun, jika Anda ingin mempelajari cara kami membuatnya, atau membuatnya sendiri, kami sangat menyarankan Anda untuk memeriksa tutorial kami yang lain, "Cara Membuat NFT." (opens in a new tab).

ABI kontrak

Jika Anda memeriksa file kami dengan cermat, Anda akan melihat bahwa di direktori src kita, ada file contract-abi.json. ABI diperlukan untuk menentukan fungsi mana yang akan dipanggil oleh kontrak serta memastikan bahwa fungsi tersebut akan mengembalikan data dalam format yang Anda harapkan.

Kita juga akan membutuhkan kunci API Alchemy dan API Web3 Alchemy untuk terhubung ke blockchain Ethereum dan memuat kontrak pintar kita.

Buat kunci API Alchemy Anda

Jika Anda belum memiliki akun Alchemy, daftar gratis di sini. (opens in a new tab)

Setelah Anda membuat akun Alchemy, Anda dapat menghasilkan kunci API dengan membuat aplikasi. Ini akan memungkinkan kita untuk membuat permintaan ke testnet Ropsten.

Navigasikan ke halaman “Create App” di Dasbor Alchemy Anda dengan mengarahkan kursor ke “Apps” di bilah navigasi dan mengklik “Create App”.

Beri nama aplikasi Anda, kami memilih "My First NFT!", tawarkan deskripsi singkat, pilih “Staging” untuk Lingkungan yang digunakan untuk pembukuan aplikasi Anda, dan pilih “Ropsten” untuk jaringan Anda.

Klik “Create app” dan selesai! Aplikasi Anda akan muncul di tabel di bawah ini.

Luar biasa, jadi sekarang setelah kita membuat URL API Alchemy HTTP kita, salin ke papan klip Anda...

…dan kemudian mari kita tambahkan ke file .env kita. Secara keseluruhan, file .env Anda akan terlihat seperti ini:

1REACT_APP_PINATA_KEY = <pinata-key>
2REACT_APP_PINATA_SECRET = <pinata-secret>
3REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/<alchemy-key> // eth-ropsten.alchemyapi.io/v2/<alchemy-key>

Sekarang setelah kita memiliki ABI kontrak kita dan kunci API Alchemy kita, kita siap untuk memuat kontrak pintar kita menggunakan Alchemy Web3 (opens in a new tab).

Siapkan titik akhir dan kontrak Alchemy Web3 Anda

Pertama, jika Anda belum memilikinya, Anda perlu menginstal Alchemy Web3 (opens in a new tab) dengan menavigasi ke direktori beranda: nft-minter-tutorial di terminal:

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

Selanjutnya mari kembali ke file interact.js kita. Di bagian atas file, tambahkan kode berikut untuk mengimpor kunci Alchemy Anda dari file .env Anda dan menyiapkan titik akhir Alchemy Web3 Anda:

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) adalah pembungkus di sekitar Web3.js (opens in a new tab), menyediakan metode API yang ditingkatkan dan manfaat penting lainnya untuk membuat hidup Anda sebagai pengembang web3 lebih mudah. Ini dirancang untuk membutuhkan konfigurasi minimal sehingga Anda dapat mulai menggunakannya di aplikasi Anda segera!

Selanjutnya, mari kita tambahkan ABI kontrak dan alamat kontrak kita ke file kita.

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"

Setelah kita memiliki keduanya, kita siap untuk mulai membuat kode fungsi mint kita!

Implementasikan fungsi mintNFT

Di dalam file interact.js Anda, mari kita definisikan fungsi kita, mintNFT, yang sesuai namanya akan melakukan mint NFT kita.

Karena kita akan melakukan banyak panggilan asinkron (ke Pinata untuk menyematkan metadata kita ke IPFS, Alchemy Web3 untuk memuat kontrak pintar kita, dan MetaMask untuk menandatangani transaksi kita), fungsi kita juga akan asinkron.

Tiga input ke fungsi kita adalah url dari aset digital kita, name, dan description. Tambahkan tanda tangan fungsi berikut di bawah fungsi connectWallet:

1export const mintNFT = async (url, name, description) => {}

Penanganan kesalahan input

Tentu saja, masuk akal untuk memiliki semacam penanganan kesalahan input di awal fungsi, jadi kita keluar dari fungsi ini jika parameter input kita tidak benar. Di dalam fungsi kita, mari kita tambahkan kode berikut:

1export const mintNFT = async (url, name, description) => {
2 //error handling // penanganan kesalahan
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}
Tampilkan semua

Pada dasarnya, jika salah satu parameter input adalah string kosong, maka kita mengembalikan objek JSON di mana boolean success adalah false, dan string status menyampaikan bahwa semua bidang di UI kita harus lengkap.

Unggah metadata ke IPFS

Setelah kita tahu metadata kita diformat dengan benar, langkah selanjutnya adalah membungkusnya ke dalam objek JSON dan mengunggahnya ke IPFS melalui pinJSONToIPFS yang kita tulis!

Untuk melakukannya, pertama-tama kita perlu mengimpor fungsi pinJSONToIPFS ke dalam file interact.js kita. Di bagian paling atas interact.js, mari kita tambahkan:

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

Ingat bahwa pinJSONToIPFS mengambil badan JSON. Jadi sebelum kita memanggilnya, kita perlu memformat parameter url, name, dan description kita ke dalam objek JSON.

Mari kita perbarui kode kita untuk membuat objek JSON yang disebut metadata dan kemudian melakukan panggilan ke pinJSONToIPFS dengan parameter metadata ini:

1export const mintNFT = async (url, name, description) => {
2 //error handling // penanganan kesalahan
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 //make metadata // buat metadata
11 const metadata = new Object()
12 metadata.name = name
13 metadata.image = url
14 metadata.description = description
15
16 //make pinata call // buat panggilan 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}
Tampilkan semua

Perhatikan, kita menyimpan respons dari panggilan kita ke pinJSONToIPFS(metadata) di objek pinataResponse. Kemudian, kita mengurai objek ini untuk mencari kesalahan apa pun.

Jika ada kesalahan, kita mengembalikan objek JSON di mana boolean success adalah false dan string status kita menyampaikan bahwa panggilan kita gagal. Jika tidak, kita mengekstrak pinataURL dari pinataResponse dan menyimpannya sebagai variabel tokenURI kita.

Sekarang saatnya untuk memuat kontrak pintar kita menggunakan API Web3 Alchemy yang kita inisialisasi di bagian atas file kita. Tambahkan baris kode berikut ke bagian bawah fungsi mintNFT untuk mengatur kontrak pada variabel global window.contract:

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

Hal terakhir yang ditambahkan dalam fungsi mintNFT kita adalah transaksi Ethereum kita:

1//set up your Ethereum transaction // siapkan transaksi Ethereum Anda
2const transactionParameters = {
3 to: contractAddress, // Required except during contract publications. // Diperlukan kecuali selama publikasi kontrak.
4 from: window.ethereum.selectedAddress, // must match user's active address. // harus cocok dengan alamat aktif pengguna.
5 data: window.contract.methods
6 .mintNFT(window.ethereum.selectedAddress, tokenURI)
7 .encodeABI(), //make call to NFT smart contract // buat panggilan ke smart contract NFT
8}
9
10//sign the transaction via MetaMask // tandatangani transaksi melalui 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}
Tampilkan semua

Jika Anda sudah terbiasa dengan transaksi Ethereum, Anda akan melihat bahwa strukturnya cukup mirip dengan apa yang pernah Anda lihat.

  • Pertama, kita menyiapkan parameter transaksi kita.
    • to menentukan alamat penerima (kontrak pintar kita)
    • from menentukan penandatangan transaksi (alamat pengguna yang terhubung ke MetaMask: window.ethereum.selectedAddress)
    • data berisi panggilan ke metode mintNFT kontrak pintar kita, yang menerima tokenURI kita dan alamat dompet pengguna, window.ethereum.selectedAddress, sebagai input
  • Kemudian, kita melakukan panggilan await, window.ethereum.request, di mana kita meminta MetaMask untuk menandatangani transaksi. Perhatikan, dalam permintaan ini, kita menentukan metode eth kita (eth_SentTransaction) dan meneruskan transactionParameters kita. Pada titik ini, MetaMask akan terbuka di browser, dan meminta pengguna untuk menandatangani atau menolak transaksi.
    • Jika transaksi berhasil, fungsi akan mengembalikan objek JSON di mana boolean success diatur ke true dan string status meminta pengguna untuk memeriksa Etherscan untuk informasi lebih lanjut tentang transaksi mereka.
    • Jika transaksi gagal, fungsi akan mengembalikan objek JSON di mana boolean success diatur ke false, dan string status menyampaikan pesan kesalahan.

Secara keseluruhan, fungsi mintNFT kita akan terlihat seperti ini:

1export const mintNFT = async (url, name, description) => {
2 //error handling // penanganan kesalahan
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 //make metadata // buat metadata
11 const metadata = new Object()
12 metadata.name = name
13 metadata.image = url
14 metadata.description = description
15
16 //pinata pin request // permintaan pin 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 //load smart contract // muat smart contract
27 window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract(); // loadContract();
28
29 //set up your Ethereum transaction // siapkan transaksi Ethereum Anda
30 const transactionParameters = {
31 to: contractAddress, // Required except during contract publications. // Diperlukan kecuali selama publikasi kontrak.
32 from: window.ethereum.selectedAddress, // must match user's active address. // harus cocok dengan alamat aktif pengguna.
33 data: window.contract.methods
34 .mintNFT(window.ethereum.selectedAddress, tokenURI)
35 .encodeABI(), //make call to NFT smart contract // buat panggilan ke smart contract NFT
36 }
37
38 //sign transaction via MetaMask // tandatangani transaksi melalui 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}
Tampilkan semua

Itu adalah satu fungsi raksasa! Sekarang, kita hanya perlu menghubungkan fungsi mintNFT kita ke komponen Minter.js kita...

Hubungkan mintNFT ke frontend Minter.js kita

Buka file Minter.js Anda dan perbarui baris import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js"; di bagian atas menjadi:

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

Terakhir, implementasikan fungsi onMintPressed untuk melakukan panggilan await ke fungsi mintNFT yang Anda impor dan perbarui variabel status status untuk mencerminkan apakah transaksi kita berhasil atau gagal:

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

Terapkan NFT Anda ke situs web langsung

Siap untuk membuat proyek Anda langsung agar pengguna dapat berinteraksi dengannya? Periksa tutorial ini (opens in a new tab) untuk menerapkan Minter Anda ke situs web langsung.

Satu langkah terakhir...

Guncang dunia blockchain

Hanya bercanda, Anda berhasil mencapai akhir tutorial!

Sebagai rekap, dengan membangun minter NFT, Anda berhasil mempelajari cara:

  • Terhubung ke MetaMask melalui proyek frontend Anda
  • Memanggil metode kontrak pintar dari frontend Anda
  • Menandatangani transaksi menggunakan MetaMask

Mungkin, Anda ingin dapat memamerkan NFT yang di-mint melalui dapp Anda di dompet Anda — jadi pastikan untuk memeriksa tutorial singkat kami Cara Melihat NFT Anda di Dompet Anda (opens in a new tab)!

Dan, seperti biasa, jika Anda memiliki pertanyaan, kami di sini untuk membantu di Discord Alchemy (opens in a new tab). Kami tidak sabar untuk melihat bagaimana Anda menerapkan konsep dari tutorial ini ke proyek masa depan Anda!

Pembaruan terakhir halaman: 25 Februari 2026

Apakah tutorial ini membantu?