---
# System prepended metadata

title: Z 前端開發班 - 實戰 NFT ERC721 網站完整功能與串接

---

# Z 前端開發班 - 實戰 NFT ERC721 網站完整功能與串接

Codesandbox: https://codesandbox.io/p/github/z-institute/web3-frontend-nft/draft/great-bush

## Tina
- 程式碼範例
    - Goerli 測試網合約部署地址：[0x6fC9d31fcdBCCA0cb4a68840B87F0a80c8Ce12Da](https://goerli.etherscan.io/address/0x6fC9d31fcdBCCA0cb4a68840B87F0a80c8Ce12Da#code)
    - NFT 智能合約：https://github.com/z-institute/web3-frontend-contracts/blob/main/NFT.sol
    - Metadata Template https://gist.github.com/tina1998612/6b3f91350a3a681c4c1f8d1c132019f9
    - `signWhitelistVoucher.js` 簽白名單：https://gist.github.com/tina1998612/530065be6cbf8d68d81cddd277e2564f
    - `whitelist.txt` 白名單清單：https://gist.github.com/tina1998612/7c8923e7b10e1e31219ee97ddf5ebc2c
    - `whitelist.json` 簽署完的簽章（只適用於目前部署在 Goerli 測試網的 NFT 合約）：https://gist.github.com/tina1998612/0f2ad0a527a78852634d717911d4b132
    - Opensea 測試網 NFT 連結：https://testnets.opensea.io/assets/goerli/0x6fc9d31fcdbcca0cb4a68840b87f0a80c8ce12da/1
- 步驟
    - 創建 hardhat project：`npx hardhat init`
    - `hardhat.config.js` 設定添加下列到 `networks` 底下
    ```
    goerli: {
          url: process.env.GOERLI_RPC_URL,
          accounts: {
            mnemonic: process.env.MNEMONIC,
          },
          chainId: 5,
        },
    ```
    - 執行簽署並獲得 `whitelist.json`：`npx hardhat run scripts/signWhitelistVoucher.js --network goerli`

  
## Agenda
1. ERC721 合約介紹
2. 其他變體的 NFT 合約介紹 - 721A / 721R / PBT / 一些近期的新標準
3. 常見交易平台 storefront 合約介紹
4. 多階段 Mint (不同價量) 原理講解
5. 合約功能測試
    * 合約在 Goerli 部署時給 symbol name 跟 uri
7. Meta Data 與圖片準備
    * 上傳到 ipfs Pinata / nftupload
        * https://www.pinata.cloud
        ![](https://i.imgur.com/JPIdUzd.png)
    * NFT Storage https://nft.storage/
        ![](https://i.imgur.com/CVaDvQX.jpg)

    * 中心化伺服器
8. 串接 NFT Mint 前端功能
    * 使用 ETH
        * Reject Handle
        ```javascript=
        try {
            let tx = await mint();
            setIsMinting(true);
            setCurrentTxHash(tx.hash);
            await tx.wait();
            setIsMinting(false);
            onOpen();
          } catch (e) {
            toast({
              title: "Error",
              description: JSON.stringify(e),
              status: "error",
              duration: 9000,
              isClosable: true,
            });
            console.log("Error!", e);
          }
        ```
    * 使用自定義 Token
    * 一次 Mint 多個的價量資料設定
9. 其他功能(可自行延伸練習)
    * 如何設計 NFT Redeem 功能
    * Burn Token 功能
    * 如何調閱 NFT 資料與 Opensea API 作為會員組憑證
    * 設定 Collection Fee 與 Opensea 頁面
        * ![](https://i.imgur.com/aiV0iLA.png)
    * 簡介動態型(內嵌網頁) NFT 運作原理
        * 範例 https://gateway.fxhash2.xyz/ipfs/QmTX4bgRjn9RUvNyLfvWBi8vxtVD7EkRavktoYVtTuDVcZ/?fxhash=ooSfhwCapGMvV7eyFJYL9UnPrgNuSHXww5ahEw7MG8vyWaNzZJK
    * 其他鏈開發 NFT 應用時會需要留意的事項

https://openprocessing.org/sketch/1723320
```javascript=
class Random {
    constructor(seed) {
        this.seed = seed;
    }
    random() {
        return this.random_dec();
    }
    random_dec() {
        return fxrand();
        /* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */
        this.seed ^= this.seed << 13;
        this.seed ^= this.seed >> 17;
        this.seed ^= this.seed << 5;
        return ((this.seed < 0 ? ~this.seed + 1 : this.seed) % 1000) / 1000;
    }
    random_num(a, b) {
        if (b === undefined) {
            b = a;
            a = 0;
        }
        return a + (b - a) * this.random_dec();
    }
    random_int(a, b) {
        if (b === undefined) {
            b = a;
            a = 0;
        }
        return Math.floor(this.random_num(a, b + 1));
    }
    random_bool(p) {
        return this.random_dec() < p;
    }
    random_choice(list) {
        return list[Math.floor(this.random_num(0, list.length * 0.99))];
    }

    random_choice_weight(obj) {
        let sum = Object.values(obj).reduce((a, b) => a + b, 0);
        let steps = Object.values(obj).reduce(
            (arr, num) => {
                arr.push((arr.slice(-1) || 0) * 1 + num);
                return arr;
            },
            [0]
        );
        let ran = this.random_num(0, sum);
        let result = 0;
        for (let i = steps.length - 1; i >= 1; i--) {
            result = i - 1;
            if (ran > steps[i - 1] && ran < steps[i]) {
                break;
            }
        }
        return Object.keys(obj)[result];
    }
}
let R = new Random()
let random = (obj, obj2) => {
    //random()
    if (obj == undefined) {
        return R.random_dec()
    }
    //random([1,2,3])
    if (Array.isArray(obj)) {
        return R.random_choice(obj)
    }
    //random(50)
    if (typeof obj == 'number' && typeof obj2 == 'number') {
        return R.random_num(obj, obj2)
    }
    //random(50)
    if (typeof obj == 'number' && obj2 == undefined) {
        return R.random_num(0, obj)
    }
    if (typeof obj == 'object') {
        return R.random_choice_weight(obj)

    }
}
```

## 參考
