日別アーカイブ: 2022年6月6日

Hardhatを使ってNFTを触ってみる

先日会社の飲み会で、デザイナーの人とたまたま同席になったのでNFTに関して軽く聞いてみた。
結論からすると、デザイナーって言ってもWebデザインやアプリのデザインばかりでいわゆる”絵”を書いていない。”絵”を書けないからWebデザインやってる的な話をされていた。

そうは言っても、私よりセンスあるだろうしやってみればいいじゃないって感じでNFTに関して軽く話をしたんだけど、エンジニアの人たちは難しいことをやっているんだな、って感じの印象しか与えることができなかった気がする。

といっても、NFTに関しては大雑把な話しか私も把握していないわけで、直接的に話の筋とは関係ないが、独自コントラクトを書いてみて理解を深められればと思った。

Hardhat・環境作成

数年前にBlockchain関連を触ったときには、Truffle と Ganache を使って開発を行った記憶があるが、今は Hardhat という開発環境を使う形でも出来るようなので、せっかくだから試してみることにする

Hardhat
https://hardhat.org/

早速、Hardhatをnpm経由でインストールする

PS C:\dev\base-nft> npm init --yes
PS C:\dev\base-nft> npm install --save-dev hardhat
PS C:\dev\base-nft> npm install @openzeppelin/contracts web3
PS C:\dev\base-nft> npx hardhat
You are using a version of Node.js that is not supported by Hardhat, and it may work incorrectly, or not work at all.

Please, make sure you are using a supported version of Node.js.

To learn more about which versions of Node.js are supported go to https://hardhat.org/nodejs-versions
888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

Welcome to Hardhat v2.9.7

? What do you want to do? ...
> Create a basic sample project
  Create an advanced sample project
  Create an advanced sample project that uses TypeScript
  Create an empty hardhat.config.js
  Quit

上記のような形となり、Hardhatで何をするのか選択肢が出てくるので、「Create a basic sample project」を選択する。

ちなみにnpm install で hardhat をすると警告が出てくる

npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: 'hardhat@2.9.7',
npm WARN EBADENGINE   required: { node: '^12.0.0 || ^14.0.0 || ^16.0.0' },
npm WARN EBADENGINE   current: { node: 'v18.1.0', npm: '8.8.0' }
npm WARN EBADENGINE }

node.js バージョン互換が取れていないということのようで、このまま突き進むとHardhat起動できないので16系に切り替えておく必要があった

ここでHardhatが問題なく起動するかを確認するためにtest実行してみる

PS C:\dev\base-nft>npx hardhat test
Error HH12: Trying to use a non-local installation of Hardhat, which is not supported.
Please install Hardhat locally using npm or Yarn, and try again.

エラーが出てきた。。。
Hardhatの公式を見てみるも特に解決せず、Stackoverflowでも同じ問題が出ていた

Can’t properly install hardhat using Powershell! Persistent Error HH12: Trying to use a non-local installation of Hardhat, which is not supported

あれこれと試してみて、結果としてはPowershellではなくコマンドプロンプトで実行したらうまくいった。謎い。

コントラクトの構築

コントラクトに関しては基本的にopenzeppelinえ用意されているものを利用する

pragma solidity ^0.8.0;

import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";

contract BaseNft is ERC721PresetMinterPauserAutoId {
    constructor() ERC721PresetMinterPauserAutoId("BaseNft", "nft", "https://raw.githubusercontent.com/krote/BaseNft/main/metadata/")
    {}
}

ERC721PresetMinterPauserAutoIdに渡している第3引数にMetadataの格納先を指定する必要があって、今回はGithubを指定してみた。

ローカルネットワークへのデプロイ

まずは、Hardhatを使用してローカルネットワークのノードを立ち上げる

C:\Dev\base-nft>npx hardhat node
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/

Accounts
========

WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.

Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
<<略>>

起動すると、ノードのアドレスとテスト利用可能なアカウントを20個ほど作ってくれる。これが後々利用するPublicKeyとPrivateKeyとなる

下記の様にdeploy.jsを用意

const hh = require("hardhat");

async function main() {
    const BaseNft = await hh.ethers.getContractFactory("BaseNft");
    const nft = await BaseNft.deploy();

    await nft.deployed();

    console.log("Deployed at: ", nft.address);
}

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

nodeを起動したコマンドプロンプトとは別のコマンドプロンプトにてdeploy.jsを実行する

C:\Dev\base-nft>npx hardhat run --network localhost scripts/deploy.js
Compiled 23 Solidity files successfully
Deployed at:  0x5FbDB2315678afecb367f032d93F642f64180aa3

無事にDeploy成功して、スマートコントラクトのアドレスを入手できた。
では続いてMintだ

Mint

Mint用にscripts/mint.jsを用意する

const Web3 = require("web3");

// CONTRACTをDeployした際のnodeアドレス
const CONTRACT_ADDRESS = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
// PUBLIC_KEY, PRIVATE_KEY は本来Metamaskなどを接続した際に取得可能なもの
// ここではHardhat起動時に作られたアカウントを固定で書く
const PUBLIC_KEY = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
// Deploy時に表示されたアドレスは127.0.0.1:8545だがlocalhostとする
const PROVIDER_URL = "http://localhost:8545";


async function mintNFT() {
    const web3 = new Web3(PROVIDER_URL);
    const contract = require("../artifacts/contracts/BaseNft.sol/BaseNft.json");
    const nftContract = new web3.eth.Contract(contract.abi, CONTRACT_ADDRESS);
    const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest");
    const tx = {
        from: PUBLIC_KEY,
        to: CONTRACT_ADDRESS,
        nonce: nonce,
        gas: 500000,
        data: nftContract.methods.mint(PUBLIC_KEY).encodeABI(),
    };

    const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY);
    signPromise
        .then((signedTx) => {
            const tx = signedTx.rawTransaction;
            if( tx !== undefined ){
                web3.eth.sendSignedTransaction(tx, function(err, hash) {
                    if( !err ) {
                        console.log("The hash of your transaction is:", hash);
                    } else {
                        console.log("Something went wrong when submittion your transaction:", err);
                    }
                });
            }
            console.log("row36");
        })
        .catch( (err) => {
            console.log("Promise failed:", err);
        });
}

mintNFT();

先に入手しているPublicKeyとPrivateKey。Deployしたアドレスをそれぞれ当てはめている。これを実行するとNFTを発行することが出来る

C:\Dev\base-nft>npx hardhat run --network localhost scripts/mint.js
The hash of your transaction is: 0xbdf81461a5416fe9c6903dba1d6780d576cf979b74ab87a43e1e7e0658d723fc      

これでMintされたことになるわけだけど、実際のところGithubの当該アドレスには特になにかおいているわけではないけど問題なくMintの処理自体は行われている。

そう考えると、指定したアドレスと言う文字列に対してBlockchain上のアドレスを割り当てているだけということで、色々と危うい仕組みなんだなぁということがよく分かる。

とりあえず、ここで使ったソースはGithubにおいた

https://github.com/krote/BaseNft