コマンドラインベースで自らの Smart Contract を作り試験環境に NFT を Mint するところまでの忘備録
Smart Contract : 契約をバイトコードでプログラムとして定義したもの Deploy : Smart Contract を Blockchain 上に配置すること Mint : Deploy した Smart Contract に従い NFT を発行すること Solidity : Blockchain で走らせる Smart Contract を書くためのプログラム言語 OpenZeppelin : Solidity を使用した Smart Contract 用のライブラリ集 Truffle : Solidity を使用したトークン処理のフレームワーク OpenSea : 世界最大の NFT マーケットプレイス Infura : Blockchain API
接続の際に以下でプロジェクトを作成して取得した Project ID を使用する
https://infura.io/
OpenSea で試験的に結果を確認する場合は Rinkeby という試験用ネットワークを使用する
Rinkeby の使用料金(ガス代)は 0.1 ETH/day なら無料で以下から取得可能
https://rinkebyfaucet.com/
必要なパッケージを追加
sudo apt update sudo apt install nodejs sudo apt install npm sudo npm install -g truffle
バージョン確認
truffle version
作業ディレクトリ初期化
mkdir test cd test truffle init
初期コンパイルテスト
truffle compile
必要なパッケージを追加
npm init -y npm install @openzeppelin/contracts npm install @truffle/hdwallet-provider
以下2つのファイルは差分取得に使用されるため削除はしないこと (see Truffle Tutorial)
## ./contracts/Migrations.sol ## ./migrations/1_initial_migration.js
OpenZeppelin ERC721
https://docs.openzeppelin.com/contracts/api/token/erc721
Smart Contract の実装 (アドレスは適宜編集)
touch ./contracts/Test.sol vi ./contracts/Test.sol ================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract Test is Context, ERC721Burnable, Ownable { constructor(string memory name, string memory symbol) ERC721(name, symbol) {} function _baseURI() internal view virtual override returns (string memory) { return "https://192.168.0.1/nft/"; } function mint(address to, uint256 tokenId) public virtual onlyOwner { _mint(to, tokenId); } } ================================
コンパイル
truffle compile
主な注意点
Smart Contract を Deploy するためのスクリプト
ファイル名の先頭は数字で連番にしておく必要がある
touch ./migrations/2_deploy_contracts.js vi ./migrations/2_deploy_contracts.js ================================ var Test = artifacts.require("Test"); module.exports = async function(deployer) { await deployer.deploy(Test, "Test Contract", "TEST"); const instance = await Test.deployed(); console.log("Contract Address: ", instance.address); }; ================================
Deploy した Smart Contract で Mint するためのスクリプト (アドレスは適宜編集)
最後に callback() すなわち deployer() を宣言しないとスクリプトが終了しないので注意
mkdir scripts touch ./scripts/test_mint_1.js vi ./scripts/test_mint_1.js ================================ var Test = artifacts.require("Test"); const tokenId = 1; module.exports = async function(deployer) { const instance = await Test.deployed(); console.log("Contract Address: ", instance.address); await instance.mint("0x1234...[WALLET_ADDRESS]", tokenId); console.log("Token URI: ", await instance.tokenURI(tokenId)); console.log("Owner: ", await instance.ownerOf(tokenId)); console.log(""); console.log("Done"); deployer(); }; ================================
Truffle Infura
https://trufflesuite.com/guides/using-infura-custom-provider/
Wallet の秘密フレーズ(もしくは暗号鍵も可能)をファイルに登録
vi .secret ================================ orange apple banana ... ================================
設定ファイルを編集 (今回は Rinkeby の試験ネットワークを使用)
あらかじめ Infura で作成しておいた Project ID を以下で定義
vi ./truffle-config.js ================================ const HDWalletProvider = require("@truffle/hdwallet-provider"); const fs = require("fs"); const mnemonic = fs.readFileSync(".secret").toString().trim(); module.exports = { networks: { rinkeby: { provider: () => new HDWalletProvider(mnemonic, "https://rinkeby.infura.io/v3/[YOUR-PROJECT-ID]"), network_id: 4, gas: 5500000, confirmations: 2, timeoutBlocks: 200, skipDryRun: true }, mumbai: { provider: () => new HDWalletProvider(mnemonic, "https://polygon-mumbai.infura.io/v3/[YOUR-PROJECT-ID]"), network_id: 80001, gas: 5500000, confirmations: 2, timeoutBlocks: 200, skipDryRun: true } }, compilers: { solc: { version: "^0.8.0" } } } ================================
コンソール接続が可能なことを確認
truffle console --network rinkeby
トークンで紐づけるメタデータを用意して Web サーバに置く (アドレスは適宜編集)
現状の設定だと tokenId がそのままメタデータのファイル名になる
vi 1 ================================ { "name": "NFT Test #01", "image": "https://192.168.0.1/image/1.png", "external_url": "https://192.168.0.1/", "description": "NFT Test #01", "attributes": [{"trait_type": "Tag", "value": "Test"}] } ================================
Smart Contract を実際にデプロイする
truffle compile --network rinkeby | tee out_compile.log truffle migrate --network rinkeby | tee out_migrate.log
デプロイに関しては 0.0006 + 0.0066 = 0.0072 ETH ほどガス代を使用
Starting migrations... ====================== > Network name: 'rinkeby' > Network id: 4 > Block gas limit: 30000000 (0x1c9c380) 1_initial_migration.js ====================== Deploying 'Migrations' ---------------------- > transaction hash: 0x83fd ... > Blocks: 1 Seconds: 8 > contract address: 0x9005 ... > block number: 1091 ... > block timestamp: 1656 ... > account: 0xD55B ... > balance: 0.3618 ... > gas used: 250154 (0x3d12a) > gas price: 2.500000007 gwei > value sent: 0 ETH > total cost: 0.000625385001751078 ETH Pausing for 2 confirmations... ------------------------------- > confirmation number: 1 (block: 1091 ...) > confirmation number: 2 (block: 1091 ...) > Saving migration to chain. > Saving artifacts ------------------------------------- > Total cost: 0.000625385001751078 ETH 2_deploy_contracts.js ===================== Deploying 'Test' ---------------- > transaction hash: 0xa9946 ... > Blocks: 0 Seconds: 12 > contract address: 0x3248 ... > block number: 1091 ... > block timestamp: 1656 ... > account: 0xD55B ... > balance: 0.3551 ... > gas used: 2639926 (0x284836) > gas price: 2.500000007 gwei > value sent: 0 ETH > total cost: 0.006599815018479482 ETH Pausing for 2 confirmations... ------------------------------- > confirmation number: 1 (block: 1091 ...) > confirmation number: 2 (block: 1091 ...) Contract Address: 0x3248 ... > Saving migration to chain. > Saving artifacts ------------------------------------- > Total cost: 0.006599815018479482 ETH Summary ======= > Total deployments: 2 > Final cost: 0.00722520002023056 ETH
デプロイした Smart Contract で NFT を Mint する
Mint 処理に関しては 0.0002 ETH ほどガス代を使用
truffle exec ./scripts/test_mint_1.js --network rinkeby | tee out_mint.log
以下から結果を確認
https://testnets.opensea.io/account
truffle compile : Smart Contract Compile truffle deploy : Smart Contract Deploy truffle migrate : Smart Contract Compile and Deploy truffle migrate --reset : Smart Contract Compile and Deploy from scratch truffle console : ノードに接続 truffle console --network rinkeby : 設定したノードに接続 truffle develop : 開発用ノードに接続 truffle exec ./scripts/mint.js --network rinkeby : 個別に実行
以下コマンドでデプロイした結果を確認できる (./build/contracts 以下にコンパイル済みの json が存在する必要がある)
truffle console --network rinkeby > let instance = await test.deployed() > instance > let instance_at = test.at("0x1234...[CONTRACT_ADDRESS]") > instance_at > instance.address
OpenSea で Mint すると以下のような属性になる
TokenID = 96503965242968..... Token Tracker = OpenSea Collections (OPENSTORE) Token Standard = ERC-1155 Token From = NullAddress (0x0000000000000000000000000000000000000000) Token To = You
今回の Truffle で Mint すると以下のような属性になる
TokenID = 1 Token Tracker = [None] Token Standard = ERC-721 Token From = NullAddress (0x0000000000000000000000000000000000000000) Token To = You
どのような形であれ Blockchain にいちど登録してしまうと変更や削除は実質的に不可能となるので注意
登録した Smart Contract のバイトコードは Etherscan で全て確認可能
後から Smart Contract を変更するスキームとして以下の様なものも考えられている
https://docs.openzeppelin.com/upgrades-plugins/
発行(Mint)した NFT なら以下の Burn Address に送ると手元からは無くなるが、それでもデータが消えることはない
0x000000000000000000000000000000000000dEaD
ERC721Burnable で Smart Contract を作成していた場合は burn 関数で NullAddress に NFT を送ることは可能
その場合もデータが消えることはなく、あくまで NullAddress の持ち物になるだけ
touch ./scripts/test_burn_1.js vi ./scripts/test_burn_1.js ================================ var Test = artifacts.require("Test"); const tokenId = 1; module.exports = async function(deployer) { const instance = await Test.deployed(); console.log("Contract Address: ", instance.address); console.log("Token URI: ", await instance.tokenURI(tokenId)); console.log("Owner: ", await instance.ownerOf(tokenId)); await instance.burn(tokenId); console.log(""); console.log("Done"); deployer(); }; ================================
truffle exec ./scripts/test_burn_1.js --network rinkeby | tee out_burn.log
Truffle
https://trufflesuite.com/tutorial/
Solidity
https://soliditylang.org/
OpenZeppelin
https://www.openzeppelin.com/