---
title: Tutorial Pencetak NFT
description: Dalam tutorial ini, Anda akan membangun pencetak NFT dan mempelajari cara membuat aplikasi terdesentralisasi (dapp) full stack dengan menghubungkan kontrak pintar Anda ke frontend React menggunakan MetaMask dan alat Web3.
author: "smudgil"
tags: ["solidity", "NFT", "alchemy", "kontrak pintar", "frontend", "Pinata", "erc-721"]
skill: intermediate
breadcrumb: Dapp pencetak NFT
lang: id
published: 2021-10-06
---

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 pencetak NFT — UI sederhana di mana Anda dapat memasukkan tautan ke aset digital Anda, judul, dan deskripsi — Anda akan mempelajari cara:

- Menghubungkan ke MetaMask melalui proyek frontend Anda
- Memanggil metode kontrak pintar dari frontend Anda
- Menandatangani transaksi menggunakan MetaMask

Dalam tutorial ini, kita akan menggunakan [React](https://react.dev/) 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 berfokus pada penambahan 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](https://react.dev/learn/tutorial-tic-tac-toe) ini. Untuk pembelajar yang lebih visual, kami sangat merekomendasikan seri video [Tutorial React Modern Lengkap](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d) yang luar biasa ini oleh Net Ninja.

Dan jika Anda belum melakukannya, Anda pasti akan membutuhkan akun Alchemy untuk menyelesaikan tutorial ini serta membangun apa pun di rantai blok. Daftar untuk akun gratis [di sini](https://alchemy.com/).

Tanpa basa-basi lagi, mari kita mulai!

## Dasar-dasar Pembuatan NFT 101 {#making-nfts-101}

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

### Menyebarkan kontrak pintar NFT di rantai blok Ethereum {#publish-nft}

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 pencetakan {#minting-function}

Biasanya, fungsi pencetakan ini mewajibkan Anda untuk meneruskan dua variabel sebagai parameter, pertama `recipient`, yang menentukan alamat yang akan menerima NFT Anda yang baru dicetak, dan kedua `tokenURI` NFT, sebuah string yang merujuk 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](https://gateway.pinata.cloud/ipfs/QmSvBcb4tjdFpajGJhbFAWeK3JAxCdNQLQtr6ZdiSi42V2), yang berisi metadata NFT.

Dalam tutorial ini, kita akan berfokus pada bagian 2, memanggil fungsi pencetakan kontrak pintar NFT menggunakan UI React kita.

Anda akan membutuhkan kontrak pintar NFT ERC-721 yang disebarkan ke testnet yang didukung seperti Sepolia. Jika Anda ingin menyebarkannya sendiri, kami merekomendasikan panduan Alchemy untuk [menyebarkan kontrak pintar ke Sepolia](https://www.alchemy.com/docs/how-to-deploy-a-smart-contract-to-the-sepolia-testnet).

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

## Kloning file awal {#clone-the-starter-files}

Pertama, buka [repositori GitHub nft-minter-tutorial](https://github.com/alchemyplatform/nft-minter-tutorial) 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 mempelajari 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 {#step-2-check-out-our-starter-files}

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

### Jalankan proyek react Anda {#get-your-react-project-running}

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

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

```bash
cd minter-starter-files
npm install
```

Setelah selesai diinstal, jalankan `npm start` di terminal Anda:

```bash
npm start
```

Melakukan hal itu akan membuka http://localhost:3000/ di peramban 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 {#minter-js}

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

Mari kita 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 state yang akan kita perbarui setelah peristiwa tertentu.

```javascript
//Variabel state
const [walletAddress, setWallet] = useState("")
const [status, setStatus] = useState("")
const [name, setName] = useState("")
const [description, setDescription] = useState("")
const [url, setURL] = useState("")
```

Belum pernah mendengar tentang variabel state React atau state hooks? Periksa dokumentasi [ini](https://legacy.reactjs.org/docs/hooks-state.html).

Berikut adalah representasi dari 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 state, 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:

```javascript
useEffect(async () => {
  //TODO: implementasikanasikanasikan
}, [])

const connectWalletPressed = async () => {
  //TODO: implement
}

const onMintPressed = async () => {
  //TODO: implement
}
```

- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) - 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 aplikasi terdesentralisasi (dapp) kita.
- `onMintPressed` - fungsi ini akan dipanggil untuk mencetak 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 state `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.

```javascript
//UI dari komponen kita
return (
  <div className="Minter">
    <button id="walletButton" onClick={connectWalletPressed}>
      {walletAddress.length > 0 ? (
        "Connected: " +
        String(walletAddress).substring(0, 6) +
        "..." +
        String(walletAddress).substring(38)
      ) : (
        <span>Connect Wallet</span>
      )}
    </button>

    <br></br>
    <h1 id="title">🧙‍♂️ Alchemy NFT Minter</h1>
    <p>
      Simply add your asset's link, name, and description, then press "Mint."
    </p>
    <form>
      <h2>🖼 Link to asset: </h2>
      <input
        type="text"
        placeholder="e.g., https://gateway.pinata.cloud/ipfs/<hash>"
        onChange={(event) => setURL(event.target.value)}
      />
      <h2>🤔 Name: </h2>
      <input
        type="text"
        placeholder="e.g., My first NFT!"
        onChange={(event) => setName(event.target.value)}
      />
      <h2>✍️ Description: </h2>
      <input
        type="text"
        placeholder="e.g., Even cooler than cryptokitties ;)"
        onChange={(event) => setDescription(event.target.value)}
      />
    </form>
    <button id="mintButton" onClick={onMintPressed}>
      Mint NFT
    </button>
    <p id="status">{status}</p>
  </div>
)
```

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

Jika Anda membuka 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 `Minter.js file` 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 {#set-up-your-ethereum-wallet}

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

### Unduh MetaMask {#download-metamask}

Untuk tutorial ini, kita akan menggunakan MetaMask, dompet virtual di peramban yang digunakan untuk mengelola alamat akun Ethereum Anda. Jika Anda ingin memahami lebih lanjut tentang cara kerja transaksi di Ethereum, periksa [halaman ini](/developers/docs/transactions/).

Anda dapat mengunduh dan membuat akun MetaMask secara gratis [di sini](https://metamask.io/download). Saat Anda membuat akun, atau jika Anda sudah memiliki akun, pastikan untuk beralih ke jaringan pengujian (testnet) yang didukung seperti Sepolia \(sehingga kita tidak berurusan dengan uang sungguhan\).

### Tambahkan ether dari Faucet {#add-ether-from-faucet}

Untuk mencetak NFT kita (atau menandatangani transaksi apa pun di rantai blok Ethereum), kita akan membutuhkan beberapa ETH palsu. Untuk mendapatkan ETH testnet, gunakan faucet yang dikelola seperti [faucet Alchemy Sepolia](https://www.alchemy.com/faucets/ethereum-sepolia) dan masukkan alamat akun Sepolia Anda. Anda akan melihat ETH di akun MetaMask Anda segera setelahnya!

### Periksa saldo Anda {#check-your-balance}

Untuk memeriksa ulang apakah saldo kita ada di sana, mari kita buat permintaan [eth_getBalance](https://www.alchemy.com/docs/chains/ethereum/ethereum-api-endpoints/eth-get-balance) menggunakan [alat sandbox Alchemy](https://sandbox.alchemy.com/?network=ETH_SEPOLIA&method=eth_getBalance&body.id=1&body.jsonrpc=2.0&body.method=eth_getBalance&body.params%5B0%5D=&body.params%5B1%5D=latest). 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:

```text
{"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! <Emoji text=":money_mouth_face:" size={1} />

## Hubungkan MetaMask ke UI Anda {#connect-metamask-to-your-ui}

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

Karena kita ingin mengikuti paradigma [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), kita akan membuat file terpisah yang berisi fungsi-fungsi kita untuk mengelola logika, data, dan aturan dapp kita, lalu meneruskan fungsi-fungsi tersebut ke frontend kita (komponen Minter.js kita).

### Fungsi `connectWallet` {#connect-wallet-function}

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

```javascript
export const connectWallet = async () => {
  if (window.ethereum) {
    try {
      const addressArray = await window.ethereum.request({
        method: "eth_requestAccounts",
      })
      const obj = {
        status: "👆🏽 Write a message in the text-field above.",
        address: addressArray[0],
      }
      return obj
    } catch (err) {
      return {
        address: "",
        status: "😥 " + err.message,
      }
    }
  } else {
    return {
      address: "",
      status: (
        <span>
          <p>
            {" "}
            🦊 <a target="_blank" href={`https://metamask.io/download`}>
              You must install MetaMask, a virtual Ethereum wallet, in your
              browser.
            </a>
          </p>
        </span>
      ),
    }
  }
}
```

Mari kita uraikan apa yang dilakukan kode ini:

Pertama, fungsi kita memeriksa apakah `window.ethereum` diaktifkan di peramban 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 rantai blok yang terhubung dengan pengguna, dan menyarankan agar pengguna menandatangani pesan dan transaksi. Periksa [dokumentasi MetaMask](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents) 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 state dan UI kita.**

Sekarang jika `window.ethereum` _ada_, maka saat itulah hal-hal menjadi menarik.

Menggunakan loop try/catch, kita akan mencoba menghubungkan ke MetaMask dengan memanggil [`window.ethereum.request({ method: "eth_requestAccounts" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts). Memanggil fungsi ini akan membuka MetaMask di peramban, 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 {#add-connect-wallet}

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 dari `Minter.js` Anda sekarang akan terlihat seperti ini:

```javascript
import { useEffect, useState } from "react";
import { connectWallet } from "./utils/interact.js";

const Minter = (props) => {

  //Variabel state
  const [walletAddress, setWallet] = useState("");
  const [status, setStatus] = useState("");
  const [name, setName] = useState("");
  const [description, setDescription] = useState("");
  const [url, setURL] = useState("");
```

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

```javascript
const connectWalletPressed = async () => {
  const walletResponse = await connectWallet()
  setStatus(walletResponse.status)
  setWallet(walletResponse.address)
}
```

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 state hooks mereka.

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

Buka peramban 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 telah terhubung.

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

Tapi 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 {#get-current-wallet}

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

```javascript
export const getCurrentWalletConnected = async () => {
  if (window.ethereum) {
    try {
      const addressArray = await window.ethereum.request({
        method: "eth_accounts",
      })
      if (addressArray.length > 0) {
        return {
          address: addressArray[0],
          status: "👆🏽 Write a message in the text-field above.",
        }
      } else {
        return {
          address: "",
          status: "🦊 Connect to MetaMask using the top right button.",
        }
      }
    } catch (err) {
      return {
        address: "",
        status: "😥 " + err.message,
      }
    }
  } else {
    return {
      address: "",
      status: (
        <span>
          <p>
            {" "}
            🦊 <a target="_blank" href={`https://metamask.io/download`}>
              You must install MetaMask, a virtual Ethereum wallet, in your
              browser.
            </a>
          </p>
        </span>
      ),
    }
  }
}
```

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 agar pengguna dapat 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:

```javascript
import { useEffect, useState } from "react"
import {
  connectWallet,
  getCurrentWalletConnected, //impor di sini
} from "./utils/interact.js"
```

Sekarang, kita cukup memanggilnya di fungsi `useEffect` kita:

```javascript
useEffect(async () => {
  const { address, status } = await getCurrentWalletConnected()
  setWallet(address)
  setStatus(status)
}, [])
```

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

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

### Implementasikan addWalletListener {#implement-add-wallet-listener}

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

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

```javascript
function addWalletListener() {
  if (window.ethereum) {
    window.ethereum.on("accountsChanged", (accounts) => {
      if (accounts.length > 0) {
        setWallet(accounts[0])
        setStatus("👆🏽 Write a message in the text-field above.")
      } else {
        setWallet("")
        setStatus("🦊 Connect to MetaMask using the top right button.")
      }
    })
  } else {
    setStatus(
      <p>
        {" "}
        🦊 <a target="_blank" href={`https://metamask.io/download`}>
          You must install MetaMask, a virtual Ethereum wallet, in your browser.
        </a>
      </p>
    )
  }
}
```

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 state `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 state 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 state `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:

```javascript
useEffect(async () => {
  const { address, status } = await getCurrentWalletConnected()
  setWallet(address)
  setStatus(status)

  addWalletListener()
}, [])
```

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

## Dasar-dasar Metadata NFT 101 {#nft-metadata-101}

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` dari 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 dapat menyimpannya di rantai blok Ethereum; namun, melakukan hal itu akan sangat mahal.
- Kita dapat menyimpannya di server terpusat, seperti AWS atau Firebase. Tetapi itu akan mengalahkan etos desentralisasi kita.
- Kita dapat menggunakan IPFS, protokol terdesentralisasi 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](https://pinata.cloud/), API dan toolkit IPFS yang praktis. Pada langkah berikutnya, kita akan menjelaskan dengan tepat cara melakukan ini!

## Gunakan Pinata untuk menyematkan metadata Anda ke IPFS {#use-pinata-to-pin-your-metadata-to-ipfs}

Jika Anda tidak memiliki akun [Pinata](https://pinata.cloud/), daftar untuk akun gratis [di sini](https://app.pinata.cloud/auth/signup) dan selesaikan langkah-langkah untuk memverifikasi email dan akun Anda.

### Buat kunci API Pinata Anda {#create-pinata-api-key}

Navigasikan ke halaman [https://pinata.cloud/keys](https://pinata.cloud/keys), 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 {#create-a-env}

Kita dapat menyimpan kunci dan rahasia Pinata kita dengan aman di file environment. Mari kita instal [paket dotenv](https://www.npmjs.com/package/dotenv) di direktori proyek Anda.

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

```text
npm install dotenv --save
```

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

```javascript
vim.env
```

Ini akan membuka file `.env` Anda di vim \(editor teks\). Untuk menyimpannya, tekan "esc" + ":" + "q" pada keyboard Anda secara berurutan.

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

```text
REACT_APP_PINATA_KEY = <pinata-api-key>
REACT_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 {#pin-json-to-ipfs}

Untungnya bagi kita, Pinata memiliki [API khusus untuk mengunggah data JSON ke IPFS](https://docs.pinata.cloud/api-reference/endpoint/ipfs/pin-json-to-ipfs#pin-json) dan contoh JavaScript dengan axios yang praktis yang dapat kita gunakan, dengan beberapa modifikasi kecil.

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

```javascript
require("dotenv").config()
const key = process.env.REACT_APP_PINATA_KEY
const 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!

```javascript
require("dotenv").config()
const key = process.env.REACT_APP_PINATA_KEY
const secret = process.env.REACT_APP_PINATA_SECRET

const axios = require("axios")

export const pinJSONToIPFS = async (JSONBody) => {
  const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`
  //membuat permintaan POST axios ke Pinata ⬇️
  return axios
    .post(url, JSONBody, {
      headers: {
        pinata_api_key: key,
        pinata_secret_api_key: secret,
      },
    })
    .then(function (response) {
      return {
        success: true,
        pinataUrl:
          "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash,
      }
    })
    .catch(function (error) {
      console.log(error)
      return {
        success: false,
        message: error.message,
      }
    })
}
```

Jadi apa sebenarnya yang dilakukan kode ini?

Pertama, ia mengimpor [axios](https://www.npmjs.com/package/axios), klien HTTP berbasis promise untuk peramban 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 pencetakan 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.

Seperti halnya tipe pengembalian fungsi `connectWallet` kita, kita mengembalikan objek JSON sehingga kita dapat menggunakan parameternya untuk memperbarui variabel state dan UI kita.

## Muat kontrak pintar Anda {#load-your-smart-contract}

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

Seperti yang kami sebutkan sebelumnya, dalam tutorial ini Anda akan membutuhkan kontrak pintar NFT yang ada yang disebarkan ke testnet yang didukung seperti Sepolia. Jika Anda ingin menyebarkannya sendiri, kami merekomendasikan panduan Alchemy untuk [menyebarkan kontrak pintar ke Sepolia](https://www.alchemy.com/docs/how-to-deploy-a-smart-contract-to-the-sepolia-testnet).

### ABI kontrak {#contract-abi}

Jika Anda memeriksa file kita 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 rantai blok Ethereum dan memuat kontrak pintar kita.

### Buat kunci API Alchemy Anda {#create-alchemy-api}

Jika Anda belum memiliki akun Alchemy, [daftar gratis di sini.](https://alchemy.com/?a=eth-org-nft-minter)

Setelah Anda membuat akun Alchemy, Anda dapat menghasilkan kunci API dengan membuat aplikasi. Ini akan memungkinkan kita untuk membuat permintaan ke jaringan pengujian (testnet) Sepolia.

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 Environment yang digunakan untuk pembukuan aplikasi Anda, dan pilih "Sepolia" 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...

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

```text
REACT_APP_PINATA_KEY = <pinata-key>
REACT_APP_PINATA_SECRET = <pinata-secret>
REACT_APP_ALCHEMY_KEY = https://eth-sepolia.g.alchemy.com/v2/<alchemy-key>
```

Sekarang setelah kita memiliki ABI kontrak dan kunci API Alchemy kita, kita siap untuk memuat kontrak pintar kita menggunakan [Web3 Alchemy](https://github.com/alchemyplatform/alchemy-web3).

### Siapkan titik akhir dan kontrak Web3 Alchemy Anda {#setup-alchemy-endpoint}

Pertama, jika Anda belum memilikinya, Anda perlu menginstal [Web3 Alchemy](https://github.com/alchemyplatform/alchemy-web3) dengan menavigasi ke direktori beranda: `nft-minter-tutorial` di terminal:

```text
cd ..
npm install @alch/alchemy-web3
```

Selanjutnya mari kita 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 Web3 Alchemy Anda:

```javascript
require("dotenv").config()
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(alchemyKey)
```

[Web3 Alchemy](https://github.com/alchemyplatform/alchemy-web3) adalah pembungkus di sekitar [Web3.js](https://docs.web3js.org/), yang 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.

```javascript
require("dotenv").config()
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(alchemyKey)

const contractABI = require("../contract-abi.json")
const contractAddress = "<your-contract-address>"
```

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

## Implementasikan fungsi mintNFT {#implement-the-mintnft-function}

Di dalam file `interact.js` Anda, mari kita tentukan fungsi kita, `mintNFT`, yang sesuai dengan namanya akan mencetak NFT kita.

Karena kita akan melakukan banyak panggilan asinkron \(ke Pinata untuk menyematkan metadata kita ke IPFS, Web3 Alchemy 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`:

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

### Penanganan kesalahan input {#input-error-handling}

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

```javascript
export const mintNFT = async (url, name, description) => {
  //penanganan kesalahan
  if (url.trim() == "" || name.trim() == "" || description.trim() == "") {
    return {
      success: false,
      status: "❗Please make sure all fields are completed before minting.",
    }
  }
}
```

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 {#upload-metadata-to-ipfs}

Setelah kita mengetahui 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:

```javascript
import { pinJSONToIPFS } from "./pinata.js"
```

Ingat kembali bahwa `pinJSONToIPFS` mengambil body 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` lalu melakukan panggilan ke `pinJSONToIPFS` dengan parameter `metadata` ini:

```javascript
export const mintNFT = async (url, name, description) => {
  //penanganan kesalahan
  if (url.trim() == "" || name.trim() == "" || description.trim() == "") {
    return {
      success: false,
      status: "❗Please make sure all fields are completed before minting.",
    }
  }

  //buat metadata
  const metadata = new Object()
  metadata.name = name
  metadata.image = url
  metadata.description = description

  //buat panggilan Pinata
  const pinataResponse = await pinJSONToIPFS(metadata)
  if (!pinataResponse.success) {
    return {
      success: false,
      status: "😢 Something went wrong while uploading your tokenURI.",
    }
  }
  const tokenURI = pinataResponse.pinataUrl
}
```

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`:

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

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

```javascript
//siapkan transaksi Ethereum Anda
const transactionParameters = {
  to: contractAddress, // Diperlukan kecuali selama publikasi kontrak.
  from: window.ethereum.selectedAddress, // harus cocok dengan alamat aktif pengguna.
  data: window.contract.methods
    .mintNFT(window.ethereum.selectedAddress, tokenURI)
    .encodeABI(), //buat panggilan ke kontrak pintar NFT
}

//tandatangani transaksi melalui MetaMask
try {
  const txHash = await window.ethereum.request({
    method: "eth_sendTransaction",
    params: [transactionParameters],
  })
  return {
    success: true,
    status:
      "✅ Check out your transaction on Etherscan: https://sepolia.etherscan.io/tx/" +
      txHash,
  }
} catch (error) {
  return {
    success: false,
    status: "😥 Something went wrong: " + error.message,
  }
}
```

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 peramban, 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:

```javascript
export const mintNFT = async (url, name, description) => {
  //penanganan kesalahan
  if (url.trim() == "" || name.trim() == "" || description.trim() == "") {
    return {
      success: false,
      status: "❗Please make sure all fields are completed before minting.",
    }
  }

  //buat metadata
  const metadata = new Object()
  metadata.name = name
  metadata.image = url
  metadata.description = description

  //permintaan pin Pinata
  const pinataResponse = await pinJSONToIPFS(metadata)
  if (!pinataResponse.success) {
    return {
      success: false,
      status: "😢 Something went wrong while uploading your tokenURI.",
    }
  }
  const tokenURI = pinataResponse.pinataUrl

  //muat kontrak pintar
  window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract();

  //siapkan transaksi Ethereum Anda
  const transactionParameters = {
    to: contractAddress, // Diperlukan kecuali selama publikasi kontrak.
    from: window.ethereum.selectedAddress, // harus cocok dengan alamat aktif pengguna.
    data: window.contract.methods
      .mintNFT(window.ethereum.selectedAddress, tokenURI)
      .encodeABI(), //buat panggilan ke kontrak pintar NFT
  }

  //tandatangani transaksi melalui MetaMask
  try {
    const txHash = await window.ethereum.request({
      method: "eth_sendTransaction",
      params: [transactionParameters],
    })
    return {
      success: true,
      status:
        "✅ Check out your transaction on Etherscan: https://sepolia.etherscan.io/tx/" +
        txHash,
    }
  } catch (error) {
    return {
      success: false,
      status: "😥 Something went wrong: " + error.message,
    }
  }
}
```

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 {#connect-our-frontend}

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

```javascript
import {
  connectWallet,
  getCurrentWalletConnected,
  mintNFT,
} from "./utils/interact.js"
```

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

```javascript
const onMintPressed = async () => {
  const { status } = await mintNFT(url, name, description)
  setStatus(status)
}
```

## Sebarkan NFT Anda ke situs web langsung {#deploy-your-nft}

Siap untuk menayangkan proyek Anda agar pengguna dapat berinteraksi dengannya? Periksa [dokumentasi penyebaran React](https://create-react-app.dev/docs/deployment/) untuk menyebarkan Minter Anda ke situs web langsung.

Satu langkah terakhir...

## Guncang dunia rantai blok {#take-the-blockchain-world-by-storm}

Hanya bercanda, Anda berhasil mencapai akhir tutorial!

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

- Menghubungkan ke MetaMask melalui proyek frontend Anda
- Memanggil metode kontrak pintar dari frontend Anda
- Menandatangani transaksi menggunakan MetaMask

Agaknya, Anda ingin dapat memamerkan NFT yang dicetak melalui dapp Anda di dompet Anda — jadi pastikan untuk memeriksa tutorial singkat kami [Cara Melihat NFT Anda di Dompet Anda](/developers/tutorials/how-to-view-nft-in-metamask/)!

Dan, seperti biasa, jika Anda memiliki pertanyaan, kami di sini untuk membantu di [Discord Alchemy](https://discord.gg/gWuC7zB). Kami tidak sabar untuk melihat bagaimana Anda menerapkan konsep dari tutorial ini ke proyek masa depan Anda!