---
title: "NFT minter 教學：如何打造全端 dApp"
description: "透過我們完整的 NFT Minter 教學，深入了解 NFT 的世界。學習輕鬆打造全端 dApp，立即開始鑄造你的數位資產！"
---

# NFT minter 教學：如何打造全端 dApp

在本教學中，你將建立一個 NFT minter，並學習如何透過 Metamask 和 Web3 工具將智能合約連接到 React 前端，打造一個全端 dApp。

此外，別忘了看看我們 NFT 教學系列的其他文章：

- 🌟  [How to mint an NFT using Web3.js](https://www.alchemy.com/blog/how-to-mint-an-nft-using-web3-js)
- 🪄  [How to mint an NFT with Ether.js](https://www.alchemy.com/blog/how-to-mint-an-nft-with-ethers-js)
- 👛  [How to view your NFT in your mobile wallet](https://www.alchemy.com/blog/how-to-view-your-nft-in-your-mobile-wallet)
- 💸  [How to set a price on an NFT](https://www.alchemy.com/blog/how-to-set-a-price-on-an-nft)

對於來自 Web2 背景的開發者來說，最大的挑戰之一，就是搞清楚如何將智能合約連接到前端專案並與其互動。

透過建立一個 NFT minter——一個可以輸入數位資產連結、標題和描述的簡單 UI——你將學會如何：

- 透過前端專案連接到 Metamask
- 從前端呼叫智能合約方法
- 使用 Metamask 簽署交易

在本教學中，我們將使用 [React](https://reactjs.org/) 作為前端框架。由於本教學主要著重於 [Web3 development](https://www.alchemy.com/overviews/how-to-learn-web3-development)，我們不會花太多時間講解 React 的基礎知識，而是專注於為我們的專案加上功能。

<CalloutBlock>

作為先備知識，你應該具備初階的 React 理解——知道 component、props、useState/useEffect 以及基本函式呼叫的運作方式。如果你從未聽過這些名詞，可以先看看這篇 [Intro to React tutorial](https://reactjs.org/tutorial/tutorial.html)。對於偏好影片學習的人，我們強烈推薦 Net Ninja 出品的 [Full Modern React Tutorial](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d) 系列影片。

</CalloutBlock>

廢話不多說，開始吧！

## 第 0 步：製作 NFT 101

在開始看任何程式碼之前，先了解製作 NFT 的運作方式很重要。它包含兩個步驟：

1\) 你在 Ethereum 區塊鏈上發布一個 NFT 智能合約，通常會是 [ERC-721](https://eips.ethereum.org/EIPS/eip-721) 或 [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155) 智能合約。

<CalloutBlock>

這兩種 NFT 智能合約標準最大的差異在於，ERC-1155 是一種多代幣標準，並包含批次功能，而 ERC-721 是單一代幣標準，因此一次只能轉移一個代幣。

</CalloutBlock>

2\) 你呼叫該 NFT 智能合約上的 mint 函式來鑄造 NFT。*Minting（鑄造）*就是在區塊鏈上發布你的非同質化代幣（non fungible token）的一個獨立實例。

通常，這個 mint 函式需要你傳入兩個變數作為參數：第一個是接收者（recipient），指定將收到你新鑄造 NFT 的地址；第二個是 NFT 的 tokenURI，一個指向描述 NFT 元資料的 JSON 文件的字串。

<CalloutBlock>

NFT 的元資料才是真正賦予它生命的東西，讓它擁有名稱、描述、圖片（或其他數位資產）以及其他屬性。這裡有一個 [tokenURI 的範例](https://gateway.pinata.cloud/ipfs/QmSvBcb4tjdFpajGJhbFAWeK3JAxCdNQLQtr6ZdiSi42V2)，內含一個 NFT 的元資料。

</CalloutBlock>

在本教學中，我們將專注於第二部分，也就是使用我們的 React UI 來呼叫既有 NFT 智能合約的 mint 函式。

[這裡有一個連結](https://sepolia.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE)是本教學中我們將會呼叫的 ERC-721 NFT 智能合約。如果你想了解我們是如何製作它的，強烈建議你參考另一篇教學「[How to Create an NFT](https://www.alchemy.com/blog/how-to-create-an-nft)」。

好的，現在我們了解了製作 NFT 的運作方式，接下來把起始檔案 clone 下來吧！

## 第 1 步：clone 起始檔案

首先，前往 [nft-minter-tutorial](https://github.com/alchemyplatform/nft-minter-tutorial) 的 github repository，取得本專案的起始檔案。將此 repository clone 到你的本機環境。

<CalloutBlock>

不知道怎麼 clone repository？參考 Github 的[這份指南](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository)。

</CalloutBlock>

打開你 clone 的 **nft-minter-tutorial** repository 後，你會發現它包含兩個資料夾：**minter-starter-files** 和 **nft-minter**。

- **minter-starter-files** 包含本專案的起始檔案（基本上就是 React UI）。在本教學中，**我們將在這個目錄下工作**，學習如何透過連接到你的 Ethereum 錢包和 NFT 智能合約，讓這個 UI 動起來。
- **nft-minter** 包含整個完成版的教學內容，供你在**卡關時作為參考**。

接著，用你喜歡的程式碼編輯器打開你複製的 **minter-starter-files**（在 Alchemy，我們很喜歡用 [VSCode](https://code.visualstudio.com/download)），然後進入 **src** 資料夾：

我們將在 "src" 資料夾內工作

<ImageBlock
  src="https://media.alchemy.com/1704629644-open-minter-starter-files-in-editor.png"
  alt="VSCode 開啟 minter-starter-files 專案，顯示 src 資料夾"
  width={2880}
  height={1798}
  caption="在你慣用的編輯器中開啟 minter-starter-files"
/>

我們所寫的所有程式碼都會放在 **src** 資料夾下。我們會編輯 **Minter.js** component，並撰寫額外的 javascript 檔案，讓我們的專案具備 Web3 功能。

## 第 2 步：檢視起始檔案

在開始寫程式碼之前，先來看看起始檔案裡已經提供了什麼，這點很重要。

#### 讓你的 react 專案跑起來

先從在瀏覽器中執行 React 專案開始。React 的優點在於，一旦我們的專案在瀏覽器中執行，我們儲存的任何變更都會即時更新在瀏覽器中。

要讓專案跑起來，先進入 **minter-starter-files** 資料夾的根目錄，然後在終端機執行 `npm install` 來安裝專案的依賴套件：

安裝完成後，在終端機執行 `npm start`：

這樣應該會在你的瀏覽器中開啟 [http://localhost:3000/](http://localhost:3000/)，你會看到我們專案的前端畫面。它應該包含 3 個欄位：輸入 NFT 資產連結的地方、輸入 NFT 名稱、以及提供描述。

<ImageBlock
  src="https://media.alchemy.com/1704629829-frontend-for-our-project.png"
  alt="NFT minter 前端介面，含資產連結、名稱與描述欄位"
  width={2876}
  height={1606}
  caption="你的專案 UI 應呈現的樣子"
/>

如果你試著點擊「Connect Wallet」或「Mint NFT」按鈕，你會發現它們沒有作用——因為我們還沒有為它們寫上功能！:\)

#### minter.js component

<CalloutBlock>

**注意：**請確認你是在 **minter-starter-files** 資料夾，而不是 **nft-minter** 資料夾！

</CalloutBlock>

讓我們回到編輯器中的 **src** 資料夾，打開 **Minter.js** 檔案。理解這個檔案的每個部分非常重要，因為它是我們接下來要處理的主要 React component。

在這個檔案的最上方，我們有一些 state 變數，會在特定事件發生後更新。

<CalloutBlock>

沒聽過 React 的 state 變數或 state hooks？看看[這些](https://reactjs.org/docs/hooks-state.html)文件。

</CalloutBlock>

以下是每個變數所代表的意義：

- **walletAddress** - 儲存使用者錢包地址的字串
- **status** - 包含要顯示在 UI 底部訊息的字串
- **name** - 儲存 NFT 名稱的字串
- **description** - 儲存 NFT 描述的字串
- **url** - 指向 NFT 數位資產的連結字串

在這些 state 變數之後，你會看到三個尚未實作的函式：`useEffect`、`connectWalletPressed`、和 `onMintPressed`。你會注意到這些函式都是 **async**，因為我們將在其中發出非同步的 API 呼叫！它們的名稱就對應了各自的功能：

- [**useEffect**](https://reactjs.org/docs/hooks-effect.html)- 這是一個 React hook，會在你的 component 渲染後被呼叫。因為它傳入了一個空陣列 **\[\]** 作為 prop（見第 3 行），所以它只會在 component_第一次_渲染時被呼叫。在這裡，我們會呼叫錢包監聽器以及另一個錢包函式，來更新 UI 以反映錢包是否已經連接。
- **connectWalletPressed** - 這個函式會被呼叫，用來將使用者的 Metamask 錢包連接到我們的 dApp。
- **onMintPressed** - 這個函式會被呼叫，用來鑄造使用者的 NFT。

在這個檔案的接近尾端，是我們 component 的 UI。如果你仔細看這段程式碼，會注意到當對應文字欄位中的輸入有變化時，我們會更新 `url`、`name`、和 `description` 這幾個 state 變數。

你也會看到，當 ID 分別為 **mintButton** 和 **walletButton** 的按鈕被點擊時，`connectWalletPressed` 和 `onMintPressed` 會分別被呼叫。

最後，來看看這個 Minter component 是在哪裡被加入的。

如果你打開 **App.js** 檔案，這是 React 中作為所有其他 component 容器的主要 component，你會看到我們的 Minter component 是在第 7 行被注入的。

**在本教學中，我們只會編輯 Minter.js 檔案，並在 src 資料夾中新增檔案。**

現在我們了解要處理的東西了，接下來設定我們的 Ethereum 錢包吧！

## 第 3 步：設定你的 Ethereum 錢包

為了讓使用者能夠與你的智能合約互動，他們需要將 Ethereum 錢包連接到你的 dApp。

#### 下載 MetaMask

在本教學中，我們會使用 Metamask，這是一個瀏覽器中的虛擬錢包，用來管理你的 Ethereum 帳戶地址。如果你想更了解 Ethereum 上的交易是如何運作的，可以看看 Ethereum foundation 的[這個頁面](https://ethereum.org/en/developers/docs/transactions/)。

你可以[在這裡](https://metamask.io/download.html)免費下載並建立 Metamask 帳戶。建立帳戶時，或如果你已經有帳戶了，請確保切換到右上角的「Ropsten Test Network」（這樣我們就不會用到真實的錢）。

<ImageBlock
  src="https://media.alchemy.com/1704630094-sample-metamask-wallet.png"
  alt="MetaMask 錢包切換至 Ropsten 測試網路"
  width={712}
  height={1194}
  caption="MetaMask 錢包範例"
/>

#### 從水龍頭領取 ether

為了鑄造我們的 NFT（或簽署任何 Ethereum 區塊鏈上的交易），我們需要一些假的 Eth。要取得 Eth，你可以前往 [Sepolia faucet](/faucets/ethereum-sepolia)，輸入你的 Ropsten 帳戶地址，然後點擊「Send Ropsten Eth」。不久後你就會在 Metamask 帳戶中看到 Eth！

#### 檢查餘額

為了再次確認餘額有到帳，讓我們透過 [Alchemy's composer tool](https://www.alchemy.com/composer/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D) 發出一個 `eth_getBalance` 請求。這會回傳我們錢包中的 Eth 數量。輸入你的 Metamask 帳戶地址並點擊「Send Request」後，你應該會看到類似這樣的回應：

<CalloutBlock>

**注意：**這個結果的單位是 wei，不是 eth。Wei 是 ether 的最小單位。從 wei 轉換到 eth 的換算是：1 eth = 10¹⁸ wei。所以如果我們把 0xde0b6b3a7640000 轉換成十進位，會得到 1\*10¹⁸，也就是 1 eth。

</CalloutBlock>

呼！我們的假錢都到位了！🤑

## 第 4 步：將 MetaMask 連接到你的 UI

現在我們的 Metamask 錢包已經設定好了，接下來把我們的 dApp 連接到它吧！

因為我們想遵循 [M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 這個典範，我們會建立一個獨立的檔案，包含用來管理 dApp 邏輯、資料和規則的函式，然後把這些函式傳給我們的前端（也就是我們的 Minter.js component）。

#### connectWallet 函式

為此，讓我們在 **src** 目錄下建立一個名為 **utils** 的新資料夾，並在裡面加入一個名為 **interact.js** 的檔案，用來包含所有錢包和智能合約互動的函式。

在 **interact.js** 檔案中，我們會撰寫一個 `connectWallet` 函式，之後會在 **Minter.js** component 中 import 並呼叫它。

在你的 **interact.js** 檔案中，加入以下內容：

來拆解一下這段程式碼在做什麼：

首先，我們的函式會檢查你的瀏覽器中 `window.ethereum` 是否已啟用。

<CalloutBlock>

window.ethereum 是由 Metamask 和其他錢包供應商注入的全域 API，讓網站可以請求使用者的 Ethereum 帳戶。如果獲得批准，它就可以讀取使用者所連接區塊鏈上的資料，並建議使用者簽署訊息和交易。想了解更多資訊，可以參考 [Metamask docs](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents)！

</CalloutBlock>

如果 `window.ethereum` _不_存在，就代表沒有安裝 Metamask。這會回傳一個 JSON 物件，其中 **address** 是一個空字串，**status** 這個 JSX 物件則會告知使用者必須安裝 Metamask。

<CalloutBlock>

**我們所寫的大多數函式，都會回傳 JSON 物件，讓我們能用來更新 state 變數和 UI。**

</CalloutBlock>

現在，如果 `window.ethereum ` _存在_，事情就變得有趣了。

透過 try/catch 迴圈，我們會嘗試呼叫 [`window.ethereum.request\(\{ method: "eth\_requestAccounts" \}\);`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts)` ` 來連接 Metamask。呼叫這個函式會在瀏覽器中開啟 Metamask，使用者會被提示將他們的錢包連接到你的 dApp。

- 如果使用者選擇連接，`method: "eth_requestAccounts"` 會回傳一個陣列，包含所有連接到該 dApp 的使用者帳戶地址。整體來說，我們的 `connectWallet` 函式會回傳一個 JSON 物件，其中包含這個陣列中的*第一個* `address`（見第 9 行），以及一個提示使用者向智能合約寫入訊息的 `status` 訊息。
- 如果使用者拒絕連接，那麼這個 JSON 物件回傳的 `address` 會是空字串，而 `status` 訊息則會反映使用者拒絕了連接。

#### 將 connectWallet 函式加入你的 minter.js UI component

現在我們已經寫好了這個 `connectWallet` 函式，讓我們把它連接到 **Minter.js** component。

首先，我們必須把這個函式 import 到我們的 **Minter.js** 檔案中，方法是在 Minter.js 檔案頂部加入

`import \{ connectWallet \} from "./utils/interact.js"`**;**。現在你 **Minter.js** 檔案的前 11 行應該像這樣：

然後，在我們的 `connectWalletPressed` 函式內，我們會呼叫剛才 import 進來的 `connectWallet` 函式，像這樣：

注意到我們大部分的功能是如何從 **Minter.js** component 抽離到 **interact.js** 檔案中的嗎？這正是為了遵循 M-V-C 典範！

在 `connectWalletPressed` 中，我們只是對我們 import 的 `connectWallet` 函式做一個 await 呼叫，並用其回應，透過對應的 state hooks 更新 `status` 和 `walletAddress` 變數。

現在，讓我們儲存這兩個檔案（**Minter.js** 和 **interact.js**），並測試一下目前為止的 UI。

在瀏覽器中打開 [http://localhost:3000/](http://localhost:3000/) 頁面，點擊頁面右上角的「Connect Wallet」按鈕。

如果你已經安裝了 Metamask，你應該會被提示將錢包連接到你的 dApp。接受連接邀請。

你應該會看到錢包按鈕現在反映出你的地址已經連接了！Yasssss 🔥

接下來，試著重新整理頁面……奇怪了。即使錢包已經連接，我們的錢包按鈕還是在提示我們連接 Metamask……

<ImageBlock
  src="https://media.alchemy.com/1704630515-the-problem-on-page-reload.gif"
  alt="錢包按鈕在頁面重新整理後錯誤地要求重新連接 MetaMask"
  width={946}
  height={480}
  caption="頁面重新整理後的問題"
/>

不過別擔心！我們只要實作一個名為 `getCurrentWalletConnected` 的函式，就能輕鬆解決這個問題，它會檢查是否已經有地址連接到我們的 dApp，並相應地更新我們的 UI！

#### getCurrentWalletConnected 函式

在你的 **interact.js** 檔案中，加入以下的 `getCurrentWalletConnected` 函式：

這段程式碼和我們剛才寫的 `connectWallet` 函式*非常*相似。

主要的差異在於，這裡我們呼叫的不是會開啟 Metamask 讓使用者連接錢包的 `eth_requestAccounts` 方法，而是呼叫 `eth_accounts` 方法，它只會回傳一個包含目前已連接到我們 dApp 的 Metamask 地址的陣列。

要看到這個函式的實際效果，讓我們在 **Minter.js** component 的 `useEffect` 函式中呼叫它。

就像我們對 `connectWallet` 所做的那樣，我們必須從 **interact.js** 檔案 import 這個函式到 **Minter.js** 檔案，像這樣：

現在，我們只需要在 `useEffect` 函式中呼叫它：

注意，我們使用呼叫 `getCurrentWalletConnected` 的回應來更新我們的 `walletAddress` 和 `status` 這兩個 state 變數。

加入這段程式碼後，試著重新整理瀏覽器視窗。按鈕應該會顯示你已連接，並顯示你所連接錢包地址的預覽——即使重新整理後也一樣！😅

#### 實作 addWalletListener

我們 dApp 錢包設定的最後一步，是實作錢包監聽器，這樣當錢包狀態改變時（例如使用者斷開連接或切換帳戶），我們的 UI 也會隨之更新。

在你的 **Minter.js** 檔案中，加入一個像這樣的 `addWalletListener` 函式：

快速拆解一下這裡發生了什麼事：

首先，我們的函式會檢查 `window.ethereum` 是否啟用（也就是 Metamask 是否已安裝）。

- 如果沒有，我們就把 `status` 這個 state 變數設為一個提示使用者安裝 Metamask 的 JSX 字串。
- 如果已啟用，我們就在第 3 行設定監聽器 `window.ethereum.on("accountsChanged")`，用來監聽 Metamask 錢包中的狀態變化，包括使用者連接額外帳戶到 dApp、切換帳戶、或斷開帳戶連接等情況。如果至少有一個帳戶已連接，`walletAddress` 這個 state 變數就會被更新為監聽器回傳的 `accounts` 陣列中的第一個帳戶。否則，`walletAddress` 就會被設為空字串。

最後，我們必須在 `useEffect` 函式中呼叫它：

就這樣！我們已經完成了所有錢包功能的程式撰寫！現在錢包已經設定好了，讓我們來搞清楚如何鑄造我們的 NFT！

## 第 5 步：NFT 元資料 101

還記得我們在本教學第 0 步提到的 NFT 元資料嗎——它賦予了 NFT 生命，讓它可以擁有數位資產、名稱、描述等屬性。

我們需要把這份元資料設定為一個 JSON 物件並儲存起來，這樣我們才能在呼叫智能合約的 `mintNFT` 函式時，把它當作 `tokenURI` 參數傳入。

「Link to Asset」、「Name」、「Description」這幾個欄位的文字，將構成我們 NFT 元資料的不同屬性。我們會把這份元資料格式化為一個 JSON 物件，但關於要把這個 JSON 物件儲存在哪裡，有幾個選項：

- 我們可以把它儲存在 Ethereum 區塊鏈上；不過由於 Ethereum 的特性，這麼做會非常昂貴（我們說的是動輒數百美元）。❌
- 我們可以把它儲存在像 AWS 或 Firebase 這樣的中心化伺服器上。但這樣就違背了我們去中心化的理念。❌
- 我們可以使用 IPFS，一個去中心化的協定和點對點網路，用於在分散式檔案系統中儲存和分享資料。由於這個協定是去中心化且免費的，這是我們最好的選擇！✅

為了把我們的元資料儲存到 IPFS，我們會使用 [Pinata](https://pinata.cloud/)，一個方便的 IPFS API 和工具組。在下一步中，我們會詳細說明該怎麼做！

## 第 6 步：使用 Pinata 把你的元資料 pin 到 IPFS

如果你還沒有 Pinata 帳戶，[在這裡](https://app.pinata.cloud/register)註冊一個免費帳戶，並完成驗證你的電子郵件和帳戶的步驟。

#### 建立你的 Pinata API key

前往 [https://app.pinata.cloud/developers/api-keys](https://app.pinata.cloud/developers/api-keys) 頁面，選擇最上方的「New Key」按鈕，將 Admin widget 設為啟用，並為你的 key 命名。

<ImageBlock
  src="https://media.alchemy.com/1704630785-create-your-pinata-api-key.png"
  alt="Pinata API 金鑰頁面：建立啟用 Admin 選項的新金鑰"
  width={2880}
  height={1644}
  caption="建立你的 Pinata API 金鑰"
/>

接著會出現一個彈出視窗，顯示你的 API 資訊。請務必把它妥善保存在安全的地方。

<ImageBlock
  src="https://media.alchemy.com/1704630855-save-your-api-key-and-secret.png"
  alt="請務必將 API 金鑰與密鑰保存在安全的地方"
  width={2880}
  height={1650}
  caption="請務必將 API 金鑰與密鑰保存在安全的地方"
/>

現在我們的 key 已經設定好了，讓我們把它加入專案中以便使用。

#### 建立 .env 檔案

我們可以安全地把 Pinata 的 key 和 secret 儲存在環境檔案中。讓我們在你的專案目錄下安裝 [dotenv package](https://www.npmjs.com/package/dotenv)。

在終端機中開啟一個新分頁（和執行 local host 的那個分頁分開），確認你位於 **minter-starter-files** 資料夾中，然後在終端機執行以下指令：

接下來，在你的命令列輸入以下內容，在 **minter-starter-files** 的根目錄建立一個 **.env** 檔案：

這會在 vim（一種文字編輯器）中彈出你的 **.env** 檔案。要儲存它，依序在鍵盤上按下「esc」+「:」+「q」。

接著，在 VSCode 中，打開你的 .env 檔案，把你的 Pinata API key 和 API secret 加進去，像這樣：

儲存檔案，然後你就準備好開始撰寫將 JSON 元資料上傳到 IPFS 的函式了！

#### 實作 pinJSONToIPFS

幸運的是，Pinata 提供了[專門用於將 JSON 資料上傳到 IPFS 的 API](https://docs.pinata.cloud#PinJSONToIPFS)，以及一個方便的 JavaScript with axios 範例，我們可以稍作修改後使用。

在你的 utils 資料夾中，建立另一個名為 pinata.js 的檔案，然後像這樣從 .env 檔案 import 我們的 Pinata secret 和 key：

接下來，把下方的額外程式碼貼進你的 pinata.js 檔案中。別擔心，我們會拆解說明每一部分的意思！

那這段程式碼到底做了什麼呢？

首先，它 import 了 [axios](https://www.npmjs.com/package/axios)，一個基於 promise 的 HTTP client，可在瀏覽器和 node.js 中使用，我們會用它來向 Pinata 發出請求。

然後我們有一個非同步函式 `pinJSONToIPFS`，它接受一個 `JSONBody` 作為輸入，並在 header 中帶上 Pinata api key 和 secret，全部都是為了向他們的 `pinJSONToIPFS` API 發出 POST 請求。

- 如果這個 POST 請求成功，那麼我們的函式會回傳一個 JSON 物件，其中 `success` boolean 為 true，以及我們元資料被 pin 上去的 `pinataUrl`。我們會把這個回傳的 `pinataUrl` 用作智能合約 mint 函式的 `tokenURI` 輸入。
- 如果這個 POST 請求失敗，那麼我們的函式會回傳一個 JSON 物件，其中 `success` boolean 為 false，以及一個反映我們錯誤的 `message` 字串。

和我們 `connectWallet` 函式的回傳型別一樣，我們回傳 JSON 物件，這樣我們就能用它們的參數來更新 state 變數和 UI。

## 第 7 步：載入你的智能合約

現在我們已經有辦法透過 pinJSONToIPFS 函式把 NFT 元資料上傳到 IPFS 了，接下來我們需要一個方法來載入智能合約的實例，這樣才能呼叫它的 mintNFT 函式。

<CalloutBlock>

如同我們前面提到的，在本教學中我們會使用[這個既有的 NFT 智能合約](https://sepolia.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE)；不過，如果你想了解我們是如何製作它的，或是想自己做一個，強烈建議你參考另一篇教學「[How to Create an NFT](https://www.alchemy.com/blog/how-to-create-an-nft)」。

</CalloutBlock>

#### 合約 ABI

如果你仔細檢視過我們的檔案，你會注意到在 **src** 目錄下有一個 **contract-abi.json** 檔案。ABI 是必要的，用來指定合約會呼叫哪個函式，並確保該函式會以你所預期的格式回傳資料。

我們還需要一個 Alchemy API key 和 Alchemy Web3 API，來連接 Ethereum 區塊鏈並載入我們的智能合約。

#### 建立你的 Alchemy API key

如果你還沒有 Alchemy 帳戶，[在這裡免費註冊](https://alchemy.com/?r=affiliate:d8895272-924f-412b-b63c-60124deb9357)。

建立 Alchemy 帳戶後，你可以透過建立一個 app 來產生一組 API key。這樣我們就能向 Ropsten test network 發出請求。

在 Alchemy Dashboard 中，把滑鼠移到導覽列的「Apps」上，點擊「Create App」，前往「Create App」頁面

<ImageBlock
  src="https://media.alchemy.com/1704631152-create-a-new-app.jpg"
  alt="Alchemy 儀表板：從 Apps 選單建立新 app"
  width={2880}
  height={1640}
  caption="建立新 app"
/>

為你的 app 命名（我們選擇「My First NFT!」），提供簡短的描述，Environment 選擇「Staging」（用於你的 app 記錄管理），network 選擇「Ropsten」。

<ImageBlock
  src="https://media.alchemy.com/1704631219-configure-your-app-details.png"
  alt="Alchemy 儀表板：設定 app 名稱、描述與 Ropsten 網路"
  width={2880}
  height={1640}
  caption="設定你的 app 詳細資料"
/>

點擊「Create app」，就完成了！你的 app 應該會出現在下方的表格中。

太棒了，現在我們已經建立了 HTTP Alchemy API URL，把它複製到剪貼簿上，像這樣……

<ImageBlock
  src="https://media.alchemy.com/1704631290-copy-your-alchemy-api-key.gif"
  alt="Alchemy 儀表板：複製 app 的 HTTP API URL 供 .env 檔案使用"
  width={843}
  height={480}
  caption="複製你的 Alchemy API 金鑰"
/>

……然後把它加入我們的 **.env** 檔案。整體來說，你的 .env 檔案應該長這樣：

現在我們有了合約 ABI 和 Alchemy API key，可以準備使用 [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) 來載入我們的智能合約了。

#### 設定你的 Alchemy Web3 endpoint 和合約

首先，如果你還沒有安裝，你需要在終端機中進到 **nft-minter-tutorial** 這個 home directory 安裝 [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3)：

接下來，回到我們的 **interact.js** 檔案。在檔案最上方，加入以下程式碼，從 .env 檔案 import 你的 Alchemy key 並設定你的 Alchemy Web3 endpoint：

<CalloutBlock>

[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) 是一個圍繞 [Web3.js](https://web3js.readthedocs.io/en/v1.2.9/) 的 wrapper，提供了增強的 API 方法和其他重要的好處，讓你身為 web3 開發者的生活更輕鬆。它的設計只需要極少的設定，你就能立刻在你的 app 中開始使用！

</CalloutBlock>

接下來，讓我們把合約 ABI 和合約地址加到檔案中。

一旦我們有了這兩者，就可以開始撰寫我們的 mint 函式了！

## 第 8 步：實作 mintNFT 函式

在你的 **interact.js** 檔案中，讓我們定義我們的函式 **mintNFT**，正如其名，它會鑄造我們的 NFT。

因為我們會發出多次非同步呼叫（向 Pinata 把元資料 pin 到 IPFS、透過 Alchemy Web3 載入智能合約、透過 Metamask 簽署交易），我們的函式也會是非同步的。

這個函式的三個輸入會是我們數位資產的 `url`、`name`、和 `description`。在 `connectWallet` 函式下方加入以下函式簽章：

#### 輸入錯誤處理

自然地，在函式一開始加入某種輸入錯誤處理是合理的，這樣如果輸入參數不正確，我們就可以直接離開這個函式。在我們的函式中，加入以下程式碼：

基本上，如果任何一個輸入參數是空字串，我們就回傳一個 JSON 物件，其中 `success` boolean 為 false，`status` 字串會反映 UI 中的所有欄位都必須填寫完整。

#### 將元資料上傳到 IPFS

一旦我們確定元資料格式正確，下一步就是把它包裝成一個 JSON 物件，並透過我們寫好的 `pinJSONToIPFS` 把它上傳到 IPFS！

為此，我們首先需要把 `pinJSONToIPFS` 函式 import 到我們的 **interact.js** 檔案中。在 **interact.js** 的最上方，加入：

回想一下，`pinJSONToIPFS` 接受一個 JSON body 作為輸入。所以在我們呼叫它之前，需要先把 `url`、`name`、和 `description` 這幾個參數格式化成一個 JSON 物件。

讓我們更新程式碼，建立一個名為 `metadata` 的 JSON 物件，然後用這個 `metadata` 參數呼叫 `pinJSONToIPFS`：

注意，我們把呼叫 `pinJSONToIPFS(metadata)` 的回應儲存在 `pinataResponse` 這個物件中。然後，我們解析這個物件是否有任何錯誤。

如果有錯誤，我們就回傳一個 JSON 物件，其中 `success` boolean 為 false，`status` 字串會反映我們的呼叫失敗了。否則，我們從 `pinataResponse` 中取出 `pinataURL`，並把它儲存為我們的 `tokenURIvariable`。

現在是時候用我們在檔案最上方初始化的 Alchemy Web3 API 來載入我們的智能合約了。在 `mintNFT` 函式的最底部加入以下這行程式碼，把合約設定在 `window.contract` 這個全域變數中：

我們在 `mintNFT` 函式中最後要加入的，是我們的 Ethereum 交易：

如果你已經熟悉 Ethereum 交易，你會注意到這個結構和你之前看過的頗為相似。

首先，我們設定交易的參數。

- `to` 指定接收者地址（我們的智能合約）
- `from` 指定交易的簽署者（使用者連接到 Metamask 的地址：`window.ethereum.selectedAddress``\)`
- `data` 包含了對我們智能合約 `mintNFT` 方法的呼叫，它接收我們的 `tokenURI` 和使用者的錢包地址 `window.ethereum.selectedAddress` 作為輸入

然後，我們發出一個 await 呼叫 `window.ethereum.request`，請 Metamask 簽署這筆交易。注意，在這個請求中，我們指定了我們的 eth 方法（`eth\_SentTransaction`），並傳入我們的 `transactionParameters`。此時，Metamask 會在瀏覽器中開啟，提示使用者簽署或拒絕這筆交易。

- 如果交易成功，這個函式會回傳一個 JSON 物件，其中 boolean `success` 設為 true，`status` 字串會提示使用者到 Etherscan 查看有關這筆交易的更多資訊。
- 如果交易失敗，這個函式會回傳一個 JSON 物件，其中 `success` boolean 設為 false，`status` 字串則會反映錯誤訊息。

整體來說，我們的 `mintNFT` 函式應該長這樣：

好大一個函式！現在，我們只需要把 `mintNFT` 函式連接到我們的 **Minter.js** component……

## 第 9 步：把 mintNFT 連接到我們的 minter.js 前端

打開你的 **Minter.js** 檔案，把最上方的 `import \{ connectWallet \} from "./utils/interact.js"`; 這一行更新為：

最後，實作 `onMintPressed` 函式，對你 import 的 `mintNFT` 函式發出 await 呼叫，並更新 `status` 這個 state 變數，以反映我們的交易是成功還是失敗：

## 第 10 步：把你的 NFT 部署到一個上線的網站

準備好讓你的專案上線，供使用者互動了嗎？看看這篇[教學](https://app.gitbook.com/@alchemyapi/s/alchemy/tutorials/nft-minter/how-do-i-deploy-nfts-online)，了解如何把你的 Minter 部署到一個上線的網站：

## 第 11 步：席捲整個區塊鏈世界 🚀

開玩笑的，你已經完成整篇教學了！回顧一下，透過建立一個 NFT minter，你成功學會了如何：

- 透過前端專案連接到 Metamask
- 從前端呼叫智能合約方法
- 使用 Metamask 簽署交易

你大概會想在錢包中展示透過你的 dApp 鑄造的 NFT——所以別忘了看看我們的簡短教學 [How to View Your NFT in Your Wallet](/blog/how-to-view-your-nft-in-your-mobile-wallet)！

還有，一如既往，如果你有任何問題，歡迎造訪 [Alchemy support center](https://www.alchemy.com/support)。我們很期待看到你把這篇教學中的概念，應用到你未來的專案中！🧙‍♂️
