見出し画像

SBT(SoulBoundToken)の作り方

SBT(Soul Bound Token)ご存知ですか?
一言で表すと、「譲渡できないNFT」です。
SBTの概要・用途に関してはkinjoさんのnoteにとても分かりやすくまとまっていますので、SBTに関する知識に不安がある方はまず読んで頂ければと思います!

このnoteの主題は「SBTを作ってみる」です。
テストネット上にSBTを作成し、Openseaで確認するところまでやっていこうと思います。初めてのnote執筆になりますので、文章力に関しては多めに見ていただけると幸いです笑
質問・コメント・(抗議)はTwitterにお願いします!


事前準備

  • MetaMaskのダウンロード

今回はMetaMaskを使用します。MetaMaskを未だPCにセットアップしていない方はこちらからブラウザの拡張機能をダウンロードしてください。

  • Goeriテストネットの設定

Goeriというイーサリアムネットワークのテストネットを利用します。テストネットを利用することで実際のETHを使用することなく同様の環境でテストを行うことができます。

画像のようにネットワークの選択欄で「Goeriテストネットワーク」を選択してください。

Goeriテストネットを選択できない方は「設定>高度な設定>テストネットワークを表示」からテストネットワークの表示をonにしてください。

  • Goeriテストネット用のETHを取得

Alchemyで24時間ごとに0.2ETHを取得できます。
テキストボックスにウォレットアドレスを入力し、「Send Me ETH」をクリックでETHを取得できます。

以上で事前準備は終了です!!
それでは実際にSBTを作っていきましょう!

SBT作成

SBTを作成するにあたってこちらの海外の方のブログを参考にしました。

STEP1 : OpenZeppelin Wizardでベースのコードを作成する

OpenZeppelinはトークンを作成する際に重宝するツールです。
トークンに求められる最低限の機能のコードがまとまっています。

今回はNFTを作成するので「ERC721」規格を選択し、左側の「FEATURES」にて必要な機能を追加していきます。下画像のように「Mintable」「Votes」「Ownable」を追加しましょう。また、「Base URI」に「https://www.myapp.com/」を入力します。

STEP2 : Remix立ち上げ

RemixはSolidity言語のコントラクトの作成・コンパイル・実行を行うことのできる開発環境です。環境構築不要で使用可能です!

STEP1で作成したコードをRemixで編集していきます。
OpenZeppelinのコード上部にある「Open in Remix」からRemixを開くことができます。

以下のようなコードが表示されていれば成功です!

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

// Made with Love by Dennison Bertram @Tally.xyz

import "@openzeppelin/contracts@4.6.0/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts@4.6.0/access/Ownable.sol";
import "@openzeppelin/contracts@4.6.0/utils/cryptography/draft-EIP712.sol";
import "@openzeppelin/contracts@4.6.0/token/ERC721/extensions/draft-ERC721Votes.sol";
import "@openzeppelin/contracts@4.6.0/utils/Counters.sol";

contract MyToken is ERC721, Ownable, EIP712, ERC721Votes {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("MyToken", "MTK") EIP712("MyToken", "1") {}

    function _baseURI() internal pure override returns (string memory) {
        return "<https://www.myapp.com/>";
    }

    function safeMint(address to) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
    }

    // The following functions are overrides required by Solidity.

    function _afterTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Votes)
    {
        super._afterTokenTransfer(from, to, tokenId);
    }
}

この段階でDAOの投票で利用可能な一般的なNFTのソースコードの作成が完了しました!
次のSTEPでSBTの最大の特徴である「譲渡不可」という部分を実装します。

STEP3 : 「譲渡不可」要素の追加

以下の関数を追加することで「譲渡不可」要素を実装することができます。

function _beforeTokenTransfer(address from, address to, uint256 tokenId)
  internal override(ERC721, ERC721Votes)
  {
      require(from == address(0), "Err: token is SOUL BOUND");
      super._beforeTokenTransfer(from, to, tokenId);
   }
}

require(from == address(0), "Err: token is SOUL BOUND");の部分に関して、「from」(トランザクションの送信者)が「address(0)」(ゼロアドレス)か否かを確認し、送信者がゼロアドレスで無い場合、つまり既にNFTを保有している人が誰かに送信しようとする場合は"Err: token is SOUL BOUND"となり、送信できない形になっています。
NFTを最初にミントする際はゼロアドレスからの送信となります。

最終的なコードは以下の通りです。

// SPDX-License-Identifier: MIT
pragma solidity ^ 0.8 .4;
// Made with Love by Dennison Bertram @Tally.xyz
import "@openzeppelin/contracts@4.6.0/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts@4.6.0/access/Ownable.sol";
import "@openzeppelin/contracts@4.6.0/utils/cryptography/draft-EIP712.sol";
import "@openzeppelin/contracts@4.6.0/token/ERC721/extensions/draft-ERC721Votes.sol";
import "@openzeppelin/contracts@4.6.0/utils/Counters.sol";

contract MyToken is ERC721, Ownable, EIP712, ERC721Votes {
    using Counters
    for Counters.Counter;
    Counters.Counter private _tokenIdCounter;
    constructor() ERC721("MyToken", "MTK") EIP712("MyToken", "1") {}

    function _baseURI() internal pure override returns(string memory) {
        return "<https://www.myapp.com/>";
    }

    function safeMint(address to) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
    }
    // The following functions are overrides required by Solidity.
    function _afterTokenTransfer(address from, address to, uint256 tokenId)
    internal
    override(ERC721, ERC721Votes) {
        super._afterTokenTransfer(from, to, tokenId);
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
    internal
    override(ERC721) {
        require(from == address(0), "Err: token is SOUL BOUND");
        super._beforeTokenTransfer(from, to, tokenId);
    }
}

STEP4 : SBTの発行

それでは、STEP3で完成したソースコードをコンパイル・デプロイし、SBTを発行していきましょう!

①画像赤枠をクリックしてソースコードをコンパイルする。

②デプロイ用画面に移動・デプロイ

テスト用のETHが反映されていない場合は「事前準備」においてETHを受け取ったアカウントと現在有効になっているアカウントが同じものか確認をお願いします。

「Deploy」を行うとMetaMaskが起動し、トランザクション実行の承認が求められます。

下画像のようなアウトプットが返って来れば成功です!

③SBTを発行
コントラクトをデプロイすると、「Deployed Cpntracts」からコントラクト内の関数を実行可能になります。
今回はSBTを発行したいので「SafeMint」を実行します。

下画像のようなアウトプットが返って来れば成功です!

STEP5 : OpenSeaで確認

作成したSBTをOpenSeaで確認し、実際に譲渡不可になっているのか試してみましょう!

テストネット用OpenSeaのリンクはこちらです。
リンクを開くと下画像のようなページに遷移すると思います。
「MyToken」が今回作成したSBTです!
譲渡不可か否かを試したいので「・・・」をクリック後、「転送」を選択してください。

画面右側に表示される「続行」を選択後、転送用のページに遷移すると思います。
転送用のアドレスとして任意のアドレスを入力し、転送を試みてください!(アドレスを複数お持ちでない方は0xE15967C015C68c7E26F606411f9174376002aDcC
このアドレス宛に転送を試みてください)

転送した際、画面右下に「Execution reverted」と表示されれば成功です!

まとめ

いかがだったでしょうか?
私の拙い説明に最後までお付き合い頂きありがとうございました

一人でも多くの方にSBTの作成経験を持っていただいて、SBTを用いた面白いユースケースが生まれてくることを期待しています!

次回はSBTのミントサイトを作成してみようかなと思います


この記事が気に入ったらサポートをしてみませんか?