Ethereum ERC721 : NFT

ブロックチェーンテクノロジーで今一番興味があるのはNFTです。
EthereumのERC721という規格のコントラクトでの実装が多いということで、試してみました。

以下参考にさせていただきました。
「Ethereum コントラクト開発 ERC721編」
https://zenn.dev/cauchye/articles/ethereum-contract-erc721

まずはHardHatという便利なフレームワークの動作確認とOpenZeppelinというライブラリのインストールをします。

$ npm init
$ npx hardhat init

以下生成されるファイルですが、コマンド実行にあたり内容を理解しておいた方がいい思われる3ファイルですので引用します。
contracts/Greeter.sol

scripts/sample-script.js

test/sample-test.js

$ npx hardhat compile
Downloading compiler 0.8.4
Compiling 2 files with 0.8.4
Compilation finished successfully
$ ls -la artifacts/contracts/Greeter.sol/Greeter.*
-rw-r–r– artifacts/contracts/Greeter.sol/Greeter.dbg.json
-rw-r–r– artifacts/contracts/Greeter.sol/Greeter.json

ローカルネットワークの起動

$ 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

Account #1: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 (10000 ETH)
Private Key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d

Account #2: 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc (10000 ETH)
Private Key: 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
….

別コンソールでアカウント確認コマンド

$ npx hardhat accounts –network localhost
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
0x70997970C51812dc3A010C7d01b50e0d17dc79C8
0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
0x90F79bf6EB2c4f870365E785982E1f101E93b906
….

テスト実行

$ npx hardhat test –network localhost

Greeter
✓ Should return the new greeting once it’s changed (400ms)

1 passing (405ms)

いきなりテスト実行でしたが、パスしていることを確認しました。
(やはりフレームワークの力はすごい)

次に、OpenZeppelinライブラリをインストールします。

$ npm install @openzeppelin/contracts

上にならって3ファイルを用意します。(参考サイトの引用、部分的に編集)
contracts/NFT1.sol

scripts/nft_deploy.js(テストを実行するので使わないが一応用意。$ node scripts/nft_deploy.js で実行)

test/nft-test1.js

テスト実行

$ npx hardhat test test/nft-test1.js –network localhost

NFT1
signer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
batSigner: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
greeter: 0x0165878A594ca255338adfa4d48449f69242Eb8F
transfer1FromSignerToAddressTx tx hash: 0x7063985c48f41ca93e15186c1b54dd54a9f9bc2940b56571c5c68e911605a466
transfer2FromBadSignerToSignerAddress tx hash: 0x27de7322ef4ce73e48961aa882873dca649b1bd31585e0a39579db192e064259
✓ should be able to mint, transferFrom, burn. And it should return appropriate name, symbol, totalSupply, tokenURI, ownerOf, balanceOf (1007ms)

1 passing (1s)

テストパスしたことを確認。(解説は長くなるので省略。ローカルネットの出力メッセージを参考に理解)
ここでtokenURIにアクセスすると以下のようなJSON返すことも意図しましたが、動作には無関係でした。

URIはNFTとして登録できてもその内容が別のものに置き換わったり、変更されていもわからないのではという疑問がわきました。

もう少し調べてみると以下のサイトでipfsというP2Pファイルサーバに登録した画像ファイルの例がありました。
https://www.quicknode.com/guides/solidity/how-to-create-and-deploy-an-erc-721-nft

$ ipfs init
generating ED25519 keypair…done
peer identity: 12D3KooWJpA83HUNUQg9wwTj3qZZaqsCmfRLMb3k9oGUkghNFEGN
initializing IPFS node at /home/k/.ipfs
to get started, enter:

ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme

$ ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme
Hello and Welcome to IPFS!

██╗██████╗ ███████╗███████╗
██║██╔══██╗██╔════╝██╔════╝
██║██████╔╝█████╗ ███████╗
██║██╔═══╝ ██╔══╝ ╚════██║
██║██║ ██║ ███████║
╚═╝╚═╝ ╚═╝ ╚══════╝
….
$ ipfs daemon
Initializing daemon…
go-ipfs version: 0.11.0
Repo version: 11
System version: amd64/linux
Golang version: go1.16.12
….

別コンソールで画像ファイルをadd。

$ ipfs add DECODE-Education.png
added QmR7Tr4CvAp6cfnDDZnLjrQwD5zzRF4g44ASmNCVThhKp7 DECODE-Education.png
7.88 KiB / 7.88 KiB [=======================================================================] 100.00%
$ ipfs add DECODE-Education.png
added QmR7Tr4CvAp6cfnDDZnLjrQwD5zzRF4g44ASmNCVThhKp7 DECODE-Education.png
7.88 KiB / 7.88 KiB [=======================================================================] 100.00%

画像のaddを複数回実行しても同じハッシュなのでこのURL(https://ipfs.io/ipfs/[ハッシュ値])を登録に使えば同一の内容であることは保証されそうです。しかし実体の紛失には対応できないことになります。どこかにバックアップをとっておく必要はありそうです。
(これがSymbolのアグリゲーショントランザクションの機能で話題になっている、オンチェーン・フルデータの実装のことなのでしょう)
現在、NFTについてかなり話題になっていますが、このあたりの不完全ななしくみについてはあまり一般的には議論されていない気がします。

実際に動かしてみて、このあたりよく理解できました。