こんばんは、エンジニアの黒岩(@kro96_xr)です。
バックエンドを中心にフロントエンドやらインフラやら色々担当しています。
突然ですが、最近NFTやらWeb3やら良くも悪くも盛り上がっていますね。
個人的に色々思うところはありつつ、技術としてはキャッチアップしてかねばならない…と言うわけでまずは完全に理解した状態を目指しつつ検証した内容をブログに認めていこうと思います。
なお、本記事の内容は個人的に触ったものであり弊社開発物と一切関係がありませんのでご承知おきください。(一応ね)
前置き
NFTって結局なんなのよ
言葉の定義は色々あるかと思いますが、技術的には「ERC721等の規格を満たすスマートコントラクトから生成されたトークン」であると私は理解しました。ERC721という規格を満たす実装をすることで一意のトークンIDを持つ、つまり非代替性を持つ=Non Fungibleなトークンになるわけです。巷でよく見るNFTアートと呼ばれる画像はあくまでトークンに紐づいたメタデータ(トークンからリンクされた画像)であり、NFTそのものではないと言えるかもしれません。
ちなみにERC1155という代替性トークンと非代替性トークンを同時に扱える規格もあり、一概にERC721に従わなくてはならないわけではないようです。
じゃあスマートコントラクトってなんなのよ
どうやらNFTを理解するにはスマートコントラクトとは何かということを理解しないといけなさそうです。
ではこのスマートコントラクトとはなんなのでしょうか?
検索してみると自動販売機に例えている説明を目にしました。
実際に自動販売機を例にとって、順を追って説明します。(私が、100円でコーラを買う場合)
100円を投入すると、コーラが買える自動販売機を業者が設置(前項目、図解の①、契約の事前定義)
私が自動販売機に100円を投入する(前項目、図解の④、決済)
コーラのボタンを押す(前項目、図解の②、イベント発生)
自動販売機からコーラが出てくる(前項目、図解の③、契約執行・価値移転)
当たり前じゃないか!馬鹿にするな!
と、怒られるかもしれませんが、これがスマートコントラクトの原点です。
“スマートコントラクトの原点は自動販売機”というお話し ~スマートコントラクトは執行可能な契約である(英LDTP)~ | 一般社団法人日本暗号通貨技能検定協会より引用
…要するに「決まった命令に対して決まった処理を行い結果を返すプログラム」というイメージでしょうか。
引用した例で言うと、下記のようになります。
- 自動販売機を機業者が設置=コントラクトをデプロイ
- 自動販売機に100円を入れてボタンを押す=コントラクトに命令を送る
- 自動販売機から飲み物が出てくる=処理結果が返ってくる
ん?「特定の命令=入力に対して特定の出力を返す」ってことはただの関数とも言えるのでは?
ということでサンプルコードを見てみましょう。
pragma solidity ^0.4.0; contract SingleNumRegister { uint storedData; function set(uint x) public{ storedData = x; } function get() public constant returns (uint retVal){ return storedData; } }
スマートコントラクトを作成し実行する - Ethereum入門より引用
このコードを見る限りオブジェクト指向言語における「クラス」のようなものみたいですね。なんとなく自分でも書ける気がしてきました。
スマートコントラクトを書いてみよう
それでは一旦NFTとERC721のことは忘れてコントラクトを実装してみましょう。
コントラクトを実装するために使用する言語がSolidityになります。
イーサリアムはEVM(Ethereum Virtual Machine)というプログラム実行環境を持っており、EVM上でコントラクトを実行します。
Solidityはコンパイラを通してEVM上で動作するコントラクトのバイトコードを生成することができます。
開発環境
今回はサクッと試してみたかったのでRemix IDEを使用しました。
こちらはブラウザ上で動くIDEであり、テスト環境へのデプロイも簡単にできる優れものです。
というわけで文字列をコントラクト上に保存して、それを取得、更新できる簡単なプログラムを書いてみました。
Solidityの記法は下記を参照してください。
コントラクト指向言語Solidity詳解 - Ethereum入門
// SPDX-License-Identifier: MIT pragma solidity ^0.8.14; contract HelloWorld { // コントラクトに保存される変数 string greeting = "Hello, world!"; // 保存されている文字列を返す関数 function getGreeting() public view returns (string memory) { return greeting; } // 文字列の更新を行う関数 function setGreeting(string memory _str) public { greeting = _str; } }
こちらをコンパイルします。
SOLIDITY COMPILERタブを開き、コンパイラのバージョンをコード内で指定したものと合わせて"Compile {ファイル名}"をクリックします。
するとコントラクタが実行できるようになるので実行してみましょう。
今回はJavaScript VMという開発環境を使用して実際のブロックチェーン上へのデプロイは行いません。
DEPLOY & RUN TRANSACTIONSタブを開きEnvironmentをJavaScript VMにしてdeployボタンを押すと…
Deploys Contractsにコントラクトが追加されます。
ここからテストができます。
Solidityなるほどな pic.twitter.com/DdsKPoVanh
— kro/クロ (@kro96_xr) 2022年6月1日
ということで非常に簡単ですが、Solidityを使ったコントラクトの作成ができました。
ERC721に対応してみよう
では、実際にNFTに対応するためにERC721への対応をしていきたいと思います。
これらの規格に対応する場合はOpenZeppelinなどのライブラリを利用するのが一般的なようです。
このライブラリを使用することで非常に簡単に実装することが出来ます。
では、下記のようなコードをRemix IDEに入れて同様にコンパイル、デプロイしてみます。
// SPDX-License-Identifier: MIT pragma solidity ^0.8.14; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract KrocksNFT is ERC721, Ownable { constructor() ERC721("Krocks NFT", "KRONFT") {} function _baseURI() internal pure override returns (string memory) { return ""; } function safeMint(address to, uint256 tokenId) public onlyOwner { _safeMint(to, tokenId); } }
とりあえずトークンの名前とオーナーは確認できますね。
このトークンはまだmintされていないのでsafeMintを実行します。
Environmentの下にテスト用のアカウント一覧があるので適当なアカウントのアドレスをコピーします。
toにコピーしたアドレスを、tokenIdに数値を入力して"transact"ボタンを押すと…
無事mintされて指定されたアドレスにトークンが付与されました!
分かりづらいですが、0xB〜がコントラクトのオーナー、0xA現在の所有者になります。
さらにsafeTransferFromを実行すると….
トークンが移動しました!
以上、トークンの生成、移動が実行できることが確認できました。
まだ擬似環境で検証しただけですが、技術的観点からNFTが少し理解できたかなと思います。
とりあえず...
Crypto Zombies をやりましょう。
おわりに
以上、今回はコントラクトの実装を試してみました。
コントラクトの実装は初めてだったのですが、バックエンドエンジニアと親和性が高いのではないかと思いました。
ブロックチェーン上に残る状態変数が公開データベースのようなイメージで捉えているのですがこの感覚は正しいのでしょうか…?
今後は
- プライベートイーサリアムネットワーク上へのデプロイ
- もう少し複雑なコントラクトの実装とフロントエンドとの連携(Dapps?)
- Hardhatを使った開発
など引き続き試していきたいなと考えています。
参考文献
以下の記事を参考にさせていただきました、ありがとうございます!
ブロックチェーンEthereum入門 3 | NTTデータ先端技術株式会社
コントラクト指向言語Solidity詳解 - Ethereum入門