---
# System prepended metadata

title: 如何在 Polygon 鏈上鑄造 NFT

---

# 如何在 Polygon 鏈上鑄造 NFT

* [ mint-nftstorege-polygon](https://nftschool.dev/tutorial/mint-nftstorage-polygon/)
* 參考自[從以太坊白皮書理解 web 3 概念](https://ithelp.ithome.com.tw/m/articles/10306796)

有以下3特性：
1. 可拓展性：避免過高鑄造花費、過慢鑄造速度
2. 持久性：在 NFT BURN 之前，asset 資料能一直存在
3. 不可修改性： NFT 與其對應的數位資產避免被串改
<blockquote>
Polygon 鏈強調於其可拓展性。並且相容於 Ethereum 與 EVM。

nft.storage 則保證其持久性透過 Filecoin 網路，透過 IPFS 保證其不可修改性。  
</blockquote>

### 環境設定
* nodejs：16.14.0
* npm：版本8.0.0 ↑

其他設定
1.  MetaMask 錢包安裝
2. 設定 MetaMask 連接網路到 [Polygon Mumbai 測試鏈](https://blog.pods.finance/guide-connecting-mumbai-testnet-to-your-metamask-87978071aca8)
3. 透過 [Mumbai faucet](https://faucet.polygon.technology/) 取得 MATIC token
4. 複製 MetaMask 錢包製作出來 Private Key: 需要來產生簽章

### 準備

[取得 nft.storage 的 API KEY](https://nft.storage/login/)

### 設定 node module 執行環境
* Hardhat、Hardhat-Ethers、ethers: 用來開發 Ethereum 的開發執行環境其中相容於 Polygon
* OpenZeppelin: 用來開發 Smart Contract 的函式庫
* nft.storage: 用來連接 nft.storage api
* dotenv： 用來讀取環境變數

建立資料夾 polygon_nft_mint
```csharp!
mkdir polygon_nft_mint
```
初始化 node_module 環境
```csharp!
cd polygon_nft_mint
npm init -y
```
安裝所需套件
```csharp!
npm add hardhat @openzeppelin/contracts nft.storage dotenv @nomiclabs/hardhat-ethers ethers@^5.0.0
```
執行 hardhat 初始化
```csharp!
npx hardhat
```
選擇 Create an empty hardhat.config.js
並更新 hardhat.config.js：
```javascript!
/**
* @type import('hardhat/config').HardhatUserConfig
*/
require("@nomiclabs/hardhat-ethers");
require('dotenv').config();
const { PRIVATE_KEY } = process.env;
module.exports = {
  defaultNetwork: "PolygonMumbai",
  networks: {
    hardhat: {
    },
     PolygonMumbai: {
      url: "https://rpc-mumbai.maticvigil.com",
      accounts: [PRIVATE_KEY]
    }
  },
  solidity: {
    version: "0.8.12",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  },
}
```

建立一個 .env 檔案：
```env!
PRIVATE_KEY="wallet private key"
NFT_STORAGE_API_KEY="Your api key"
```

下一步建立三個資料夾

1. contracts: 用來存放 Smart Contract
2. assets: 用來存放要放到 nft.storage 的數位資料
3. scripts: 用來存放要執行發佈的 javascript 檔案

```csharp!
mkdir contracts assets scripts
```

### 鑄造 NFT

把數位檔案放到 nft.storage
建立scripts/store-asset.mjs
把要用的圖片放到assets
```ECMAScript!
import { NFTStorage, File } from "nft.storage"
import fs from 'fs'
import dotenv from 'dotenv'
dotenv.config()

const API_KEY = process.env.NFT_STORAGE_API_KEY

async function storeAsset() {
   const client = new NFTStorage({ token: API_KEY })
   const metadata = await client.store({
       name: 'Little Red Riding Hood_NFT',
       description: 'This famous tale is short but very SNAPPY!',
       image: new File(
           [await fs.promises.readFile('assets/Little Red Riding Hood.jpg')],
           'pig.jpg',
           { type: 'image/jpg' }
       ),
       external_url: new File(
            [await fs.promises.readFile('assets/Little Red Riding Hood.pdf')],
            'pig.pdf',
            { type: 'application/pdf' }
        ),
       attributes: [
        {
            "trait_type": "other_trading_sites",
            "value": "https://shp.ee/quwxcgm"
        },
        {
          "trait_type": "seller_email",
          "value": "example@gmail.com"
        },
        {
          "trait_type": "seller_phone",
          "value": "0927XXXX76"
        }
      ]
   })
   console.log("Metadata stored on Filecoin and IPFS with URL:", metadata.url)
}

storeAsset()
   .then(() => process.exit(0))
   .catch((error) => {
       console.error(error);
       process.exit(1);
   });
```
透過執行以下指令就可以把檔案上傳並且產生 meta 資料
`node scripts/store-asset.mjs`
![](https://i.imgur.com/sLtdBuY.png)

### 在 Polygon 建立 NFT

建立 Smart Contract 來做鑄造
建立 contracts/ExampleNFT.sol 

```solidity!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract ExampleNFT is ERC721URIStorage, Ownable {
   using Counters for Counters.Counter;
   Counters.Counter private _tokenIds;

   constructor() ERC721("NFT", "ENFT") {}

   function mintNFT(address recipient, string memory tokenURI)
       public onlyOwner
       returns (uint256)
   {
       _tokenIds.increment();

       uint256 newItemId = _tokenIds.current();
       _mint(recipient, newItemId);
       _setTokenURI(newItemId, tokenURI);

       return newItemId;
   }
}
```
<blockquote>

@openzeppelin/contracts/token/ERC721/ERC721.sol 包含了所有 ERC-721 標準，實作了關於 NFT Token 所需要的特性
    
@openzeppelin/contracts/utils/Counters.sol 替供了一個 Counter 可以隨著呼叫 increment() 來不斷增加，這邊用來當作 unqiue ID

@openzeppelin/contracts/access/Ownable.sol 用來設定 NFT 的存取權限

constructor 這邊用來把 ERC-721 特性附加到 NFT 上面

可以看到 mintNFT 這個 function 的存取權限是 onlyOwner 代表只有 Smart Contract 的 onwer 才能呼叫

mintNFT 的第一個參數 recipient 是用來設定 NFT 接收者的 address
mintNFT 的第二個參數 tokenURI 則是用來儲存一個能夠顯示 NFT metadata 的 URL
</blockquote>

### 發佈 Smart Contract 到 Polygon 鏈
建立 scripts/deploy-contract.mjs
```ECMAScript!
async function deployContract() {
 const ExampleNFT = await ethers.getContractFactory("ExampleNFT")
 const exampleNFT = await ExampleNFT.deploy()
 await exampleNFT.deployed()
 // This solves the bug in Mumbai network where the contract address is not the real one
 const txHash = exampleNFT.deployTransaction.hash
 const txReceipt = await ethers.provider.waitForTransaction(txHash)
 const contractAddress = txReceipt.contractAddress
 console.log("Contract deployed to address:", contractAddress)
}

deployContract()
 .then(() => process.exit(0))
 .catch((error) => {
   console.error(error);
   process.exit(1);
 });
```
以下指令部署
`npx hardhat run scripts/deploy-contract.mjs --network PolygonMumbai`
![](https://i.imgur.com/LwPBGO7.png)

### 鑄造 NFT 到 Polygon

利用得到的metadata URL、合約部署位址
建立scripts/mint-nft.mjs
```ECMAScript!
const CONTRACT_ADDRESS = "0xA16871aC60308AB87Ce1DBdF93644456FF7D3BE6"
const META_DATA_URL = "ipfs://bafyreibcgwqmp6fzter7zebelf57ae3hktkqaejbwatl4ka4li4qbspbwi/metadata.json"

async function mintNFT(contractAddress, metaDataURL) {
   const ExampleNFT = await ethers.getContractFactory("ExampleNFT")
   const [owner] = await ethers.getSigners()
   await ExampleNFT.attach(contractAddress).mintNFT(owner.address, metaDataURL)
   console.log("NFT minted to: ", owner.address)
}

mintNFT(CONTRACT_ADDRESS, META_DATA_URL)
   .then(() => process.exit(0))
   .catch((error) => {
       console.error(error);
       process.exit(1);
   });
```

指令鑄造
`npx hardhat run scripts/mint-nft.mjs --network PolygonMumbai`
![](https://i.imgur.com/v0rfi1O.png)

可以到[opensea](https://testnets.opensea.io/account/created)看鑄造出來的NFT
![](https://i.imgur.com/RTul9xs.png)


<!-- ![](https://i.imgur.com/ouuJ52U.png) -->

