2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

レベルアップするNFTを作る!!!〜メタデータを更新できるようにしよう〜

Last updated at Posted at 2023-03-23

前書き

プログラミング、スマートコントラクトともに初学者のものです。最近NFTを発行できるスマートコントラクトを作成できるようになったのですが、メタデータを更新してレベルアップできるNFTを作れるようになったら面白いなと思ったので、今回はそのようなスマートコントラクトを作成していこうと思います。

※NFT発行のためのコードとremix ideの操作方法を一通り理解している前提で進めます。

環境

Remix ide
solidity 0.8.9

完成物のイメージ

SVGのオンチェーンNFTをミントする関数とメタデータを更新する関数を備えているスマートコントラクト。
SVGで現在のレベルを出力させます。こんな感じ

<svg width="350" height="50" fill="none" xmlns="http://www.w3.org/2000/svg">
  <text x="0" y="30" font-family="Verdana" font-size="20" fill="black">
    level:具体的な数字
  </text>
</svg>

どのようにレベルアップ(メタデータの更新)するか

Open ZeppelinのERC721のライブラリに _setTokenURI() という関数がありますが、この関数を一つのtokenIdに対して複数回呼び出すことによってレベルアップ(メタデータの更新)を実現します。

コード

level-up-nft.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Base64.sol";

contract levelUpNFT is ERC721URIStorage {
    // CountersとStringsの使用を宣言
    using Counters for Counters.Counter;
    using Strings for uint256;

    // idごとにトークンのレベルを管理するマッピングを定義
    mapping (uint256 => uint256) public idToLevel;

    //tokenIdを状態変数として定義(NFTをミントするたびにインクリメントされます。)
    Counters.Counter private _lastTokenId;

    //コンストラクタを定義
    constructor() ERC721("LevelUpNFT", "LEVELUP") {}

    //ミント関数を定義
    function nftMint() public {
        //tokenIdをインクリメント
        _lastTokenId.increment();
        //NFTをミント
        _safeMint(msg.sender, _lastTokenId.current());

        //NFTのコンテンツとしてのsvgとメタデータを関数から取得
        bytes memory svg = _createSVG(0);
        bytes memory metadata = _createMetadata(svg);

        //uriを作成
        string memory uri = string(abi.encodePacked("data:application/json;base64,", Base64.encode(metadata)));
        //発行したNFTにコンテンツを付与
        _setTokenURI(_lastTokenId.current(), uri);

        //マッピングに要素を追加
        idToLevel[_lastTokenId.current()] = 0;
    }

    //簡単なメタデータを定義
    function _createMetadata(bytes memory svg) pure private returns(bytes memory){
        return(
            abi.encodePacked(
            '{"image": "data:image/svg+xml;base64,',
            Base64.encode(svg),
            '"}'
            )
        );
    }

    //SVGを返す関数を定義
    function _createSVG(uint256 level) pure private returns(bytes memory) {
        bytes memory contentSVG = abi.encodePacked(
            '<svg width="350" height="50" fill="none" xmlns="http://www.w3.org/2000/svg">',
            '<text x="0" y="30" font-family="Verdana" font-size="20" fill="black">level:',
            Strings.toString(level),
            '</text>',
            '</svg>'
        );
        return contentSVG;
    }

    //URIを再設定する関数を定義(レベルアップ関数)
    function levelUp(uint256 tokenId) public {
        address owner = ownerOf(tokenId);
        require(owner == msg.sender);

        //レベルをインクリメント
        uint256 newLevel = idToLevel[tokenId] + 1;
        idToLevel[tokenId] = newLevel;

        //NFTのコンテンツとしてのsvgとメタデータを関数から取得
        bytes memory newSvg = _createSVG(newLevel);
        bytes memory metadata = _createMetadata(newSvg);
        //uriを作成
        string memory newURI = string(abi.encodePacked("data:application/json;base64,", Base64.encode(metadata)));

        _setTokenURI(tokenId, newURI);
    }
}

肝となるコード

上記のコードの idToLevelマッピングlevelUp関数 が今回のプロジェクトの肝です。

tokenIdごとのレベルをmappingで管理

// idごとにトークンのレベルを管理するマッピングを定義
    mapping (uint256 => uint256) public idToLevel;

tokenIdごとにレベルをmappingで管理します。

メタデータを更新する

//URIを再設定する関数を定義(レベルアップ関数)
    function levelUp(uint256 tokenId) public {
        address owner = ownerOf(tokenId);
        require(owner == msg.sender);

        //レベルをインクリメント
        uint256 newLevel = idToLevel[tokenId] + 1;
        idToLevel[tokenId] = newLevel;

        //NFTのコンテンツとしてのsvgとメタデータを関数から取得
        bytes memory newSvg = _createSVG(newLevel);
        bytes memory metadata = _createMetadata(newSvg);
        //uriを作成
        string memory newURI = string(abi.encodePacked("data:application/json;base64,", Base64.encode(metadata)));

        _setTokenURI(tokenId, newURI);
    }

requireで引数のトークンが関数実行者のものか確認します。idToLevelマッピングから引数のtokenIdのレベルを取得しインクリメントします。それをmappingに格納しなおします。インクリメントしたレベル(newLevel)を引数としてSVGを作成し、新たなメタデータを作成します。

動作確認

1.コンパイルと仮装環境へのデプロイ
2.nftMint関数を実行(最初のtokenIdは1です)
3.引数を1としてtokenURI関数でURIを取得し、ブラウザのフォームに入力。以下のようなページが出力されます。
スクリーンショット 2023-03-24 0.14.22.png
4.data:image/~ をコピーしブラウザのフォームの入力します。以下のように出力されます。
image.png
5.levelUp関数を実行します。
6.3, 4を再び実行すると以下のように出力されます。image.png

無事レベルアップ(メタデータの更新)が確認されました!!!

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?