iT邦幫忙

2022 iThome 鐵人賽

DAY 27
1
Web 3

從以太坊白皮書理解 web 3 概念系列 第 28

從以太坊白皮書理解 web 3 概念 - Day27

  • 分享至 

  • xImage
  •  

從以太坊白皮書理解 web 3 概念 - Day27

NFT School Day 5 - Mint ERC721 NFTs on Avalanche

今天將會透過 Mint ERC721 NFTs on Avalanche 來研究如何在 Avalanche 上實作 NFT

研究主題如下

  • Avalanche vs Ethereum
  • 設定 local Avalanche 節點
  • 透過 AVAX 設定測試帳戶
  • 鑄造在 Avalanche 上的 ERC 721 Token

Avalanche vs Ethereum

Avalanche 是一個由多個區塊鏈所組成的網路,其中一個組成元素是 EVM 的分支並且相容於 Ethereum

Avalanche 包含3個子網路:

  • X-chain: 處理值的交換並且執行 Avalanche Virtual Machine(AVM)
  • P-chain: 處理 Platform(Protocol) 並且能夠產生一個代名區塊鏈
  • C-chain:與 EVM 相容並且可以執行 Solidity 所寫的 Smart Contract 。 鏈上的 Account 的 address 是與 Ethereum 相容的。

架構如下圖:

值得注意的是,大部份 Dapp 都是透過 C-chain 來做互動。

設定

環境設定

執行 local 模擬節點

為了在本機電腦可以執行與區塊鏈互動的部份,所以需要在本機執行節點程式

打開 avalancego 專案

其結構如下

avalancego/
├── ...
└──scripts
   ├── ansible
   ├── aws
   ├── build.sh
   ├── ...
   └── versions.sh

這邊需要透過 build.sh 來建制可執行的 binary file

而 avm-sim 也需要做同樣的事情

備註: 需要把 execution 的權限給 build.sh

chmod +x scripts/build.sh

執行 ava-sim

透過以下指令做執行

./scripts/run.sh

建立測試 keystore

curl -X POST --data '{
    "jsonrpc":"2.0",
    "id"     :1,
    "method" :"keystore.createUser",
    "params" :{
        "username":"MYUSERNAME",
        "password":"MYPASSWORD"
    }
}' -H 'Content-Type: application/json' 127.0.0.1:9650/ext/keystore

這裡的 MYUSERNAME, MYPASSWORD 是建立在 AVAX 的帳戶

這邊透過一個已經具有資金的 private key 來鏈結到剛剛建立 AVAX 帳戶

PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN

使用以下指令

curl --location --request POST '127.0.0.1:9650/ext/bc/C/avax' \
--header 'Content-Type: application/json' \
--data-raw '{
    "method": "avax.importKey",
    "params": {
        "username":"MYUSERNAME",
        "password":"MYPASSWORD",
        "privateKey":"PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"
    },
    "jsonrpc": "2.0",
    "id": 1
}'

整合到 MetaMask

新增 Avalanche Local 測試鏈資訊如下

Network Name: Avalanche Local
New RPC URL: http://localhost:9650/ext/bc/C/rpc (for C-chain)
ChainID: 43112
Symbol: AVAX
Explorer: N/A

import AVAX 測試帳戶

0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027

建立 Nodejs project

mkdir hello-avax && cd hello-avax
yarn init -y

安裝 hardhat

yarn add hardhat -D

使用 hardhat 初始化專案

npx hardhat

更新 hardhat.config.js 如下

const { task } = require("hardhat/config")
const { BigNumber } = require("ethers")
require("@nomiclabs/hardhat-waffle")

const FORK_FUJI = false
const FORK_MAINNET = false
const forkingData = FORK_FUJI ? {
  url: 'https://api.avax-test.network/ext/bc/C/rpc',
} : FORK_MAINNET ? {
  url: 'https://api.avax.network/ext/bc/C/rpc'
} : undefined

task("accounts", "Prints the list of accounts", async (args, hre) => {
  const accounts = await hre.ethers.getSigners()
  accounts.forEach((account) => {
    console.log(account.address)
  })
})

task("balances", "Prints the list of AVAX account balances", async (args, hre) => {
  const accounts = await hre.ethers.getSigners()
  for (const account of accounts){
    const balance = await hre.ethers.provider.getBalance(
      account.address
    );
    console.log(`${account.address} has balance ${balance.toString()}`);
  }
})

module.exports = {
  solidity: {
    compilers: [
      {
        version: "0.5.16"
      },
      {
        version: "0.6.2"
      },
      {
        version: "0.6.4"
      },
      {
        version: "0.7.0"
      },
      {
        version: "0.8.0"
      },
      {
        version: "0.8.1"
      }
    ]
  },
  networks: {
    hardhat: {
      gasPrice: 225000000000,
      chainId: !forkingData ? 43112 : undefined,
    },
    local: {
      url: 'http://localhost:9650/ext/bc/C/rpc',
      gasPrice: 225000000000,
      chainId: 43112,
      accounts: [
        "0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027",
        "0x7b4198529994b0dc604278c99d153cfd069d594753d471171a1d102a10438e07",
        "0x15614556be13730e9e8d6eacc1603143e7b96987429df8726384c2ec4502ef6e",
        "0x31b571bf6894a248831ff937bb49f7754509fe93bbd2517c9c73c4144c0e97dc",
        "0x6934bef917e01692b789da754a0eae31a8536eb465e7bff752ea291dad88c675",
        "0xe700bdbdbc279b808b1ec45f8c2370e4616d3a02c336e68d85d4668e08f53cff",
        "0xbbc2865b76ba28016bc2255c7504d000e046ae01934b04c694592a6276988630",
        "0xcdbfd34f687ced8c6968854f8a99ae47712c4f4183b78dcc4a903d1bfe8cbf60",
        "0x86f78c5416151fe3546dece84fda4b4b1e36089f2dbc48496faf3a950f16157c",
        "0x750839e9dbbd2a0910efe40f50b2f3b2f2f59f5580bb4b83bd8c1201cf9a010a"
      ]
    }
  }
}

執行 察看 local account

npx hardhat accounts --network local

執行 察看 local account balance

npx hardhat balances --network local

實作一個 ERC-721 NFT

為了使用 ERC721 標準 Contract 需要使用以下指令安裝套件

yarn add @openzeppelin/contracts

實作 Smart Contract File.sol 如下

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

contract Filet is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721("Filet", "FILET") {}

    function mintTo(address player, string memory tokenURI)
        public
        returns (uint256)
    {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
        _mint(player, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}

這個 Smart Contract 是用來設定 NFT 對應的 storage

裡面的 mintTo 是用來鑄造 NFT 給對應的 player

使用以下指令做 compile

npx hardhat compile

修改 scripts/deploy.js 如下

const {
  Contract,
  ContractFactory
} = require("ethers")
const { ethers } = require("hardhat")

const deploy = async (contractName) => {
  const Contract = await ethers.getContractFactory(contractName)
  const contract = await Contract.deploy()

  await contract.deployed()
  console.log(`${contractName} deployed to: ${contract.address}`)
}

const main = async () => {
  await deploy("Filet")
}

main()
.then(() => process.exit(0))
.catch(error => {
  console.error(error)
  process.exit(1)
})

執行以下指令 deploy smart contract 到 local network

npx hardhat run scripts/deploy.js --network local

透過 hardhat developer Console 與 Smart Contract 互動

npx hardhat console --network local

透過以下指令初始化 Smart Contract 物件

>>> const Filet = await ethers.getContractFactory("Filet")
>>> const filet = await Filet.attach("0x52C84043CD9c865236f11d9Fc9F56aa003c1f922")

透過以下指令察看 accouts

>>> const accounts = await ethers.provider.listAccounts()
>>> accounts

實作上傳 assets 到 nft.storage 與鑄造 NFT 邏輯

安裝 nft.storage 與 mime 套件

yarn add nft.storage mime

實作 scripts/upload.mjs

如下

import { NFTStorage, File } from 'nft.storage'

import mime from 'mime'

import fs from 'fs'

import path from 'path'
import dotenv from 'dotenv'

dotenv.config()

const NFT_STORAGE_KEY = process.env.NFT_STORAGE_API_KEY

async function storeNFT(imagePath, name, description) {
    const image = await fileFromPath(imagePath)

    const nftstorage = new NFTStorage({ token: NFT_STORAGE_KEY })

    return nftstorage.store({
        image,
        name,
        description,
    })
}

async function fileFromPath(filePath) {
    const content = await fs.promises.readFile(filePath)
    const type = mime.getType(filePath)
    return new File([content], path.basename(filePath), { type })
}

async function upload(imagePath, name, description) {
    const result = await storeNFT(imagePath, name, description)
    return result
}

export { upload }

執行 mintTo

>> const _tx = await filet.mintTo(accounts[1], result1.url)

執行完後 發現 accounts[1] 的 balance 更新為 1

確認 owner 為 accounts[1]

取出 token 的 metadata

https://bafyreidpavdpyags6iddajfucdyq6hjrhgeareagfbq3yzsnjknmkgyqmi.ipfs.nftstorage.link/metadata.json


上一篇
從以太坊白皮書理解 web 3 概念 - Day26
下一篇
從以太坊白皮書理解 web 3 概念 - Day28
系列文
從以太坊白皮書理解 web 3 概念32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言