SPECIALIST

多様な専門性を持つNRIデジタル社員のコラム、インタビューやインサイトをご紹介します。

BACK

NFTトークンのレンタル規格を実装してみた【前半】

NRIデジタルでは、新技術について調査・開発を行う社内活動を推進しています。その取り組みの一環として、新たなビジネス創出チャンスとして注目を集めているブロックチェーン・NFT技術についての調査を行いました。
本記事では、その活動の一部を共有していきます。今回は【前半】として、2022年6月に標準として承認されたばかりのNFTトークン規格「ERC-4907」の概要と、プロトタイプアプリの実装方法について紹介します。

ERC-4907の概要

NFTとは

NFTとは、ブロックチェーン上で発行および取引される「偽造不可な鑑定書・所有証明書付きのデジタルデータ」です。デジタルデータに代替不可能な唯一無二の価値を持たせることを可能にし、特にゲームやアートなどの分野で注目を集めている最新技術の1つです。(詳細は、NFTホワイトペーパー(自民党発表)  NFTの動向整理(消費者庁)参照)
1つ1つのトークンを区別し替えがきかないものとして発行し扱うための規格「ERC-721」が採択され、この「ERC-721」規格を利用して発行されたトークンが「NFT」と呼ばれるようになりました。

NFTの規格「ERC-4907」とは

NFTは、主にERC-721という標準規格に基づいて作成されていますが、ERC-4907はこのERC-721の規格を拡張するための規格です。詳しい実装方法は後述しますが、ERC-721のNFTをレンタルしやすいように、所有者(Owner)が一定期間NFTをレンタルできる使用者(user)とその期間(expires)を設定し、期間が過ぎれば自動で使用者(user)の権利が失効するという仕組みを付与します。
従来の方法でNFTでもレンタルは可能でしたが、NFTを勝手に転売されたり、期限が過ぎても返還してもらえないリスクがあるため、リスク回避のために、担保を取ったり契約を締結する等の手続きが必要でした。一方で、この新規格を使うことで、持ち逃げや転売のリスクがなくなり、煩雑な手続きが不要になるというメリットがあります。

NFTの取引に使われるスマートコントラクトとは

スマートコントラクトとは、ブロックチェーンに保存された改ざん不可能なプログラムのことで、あらかじめ設定されたルールに従って決められた処理を実行します。
NFTは、スマートコントラクトで定められたルールに従って取引されることで、自動的にブロックチェーン上の所有者が書き換えられ、改ざん不可能な取引履歴を持つことが可能です。

プロトタイプアプリの実装(レンタルNFTの実装イメージ紹介)

実装方針

ERC-4907を用いてレンタルNFTのサービスを提供するには幾つかの方法が考えられますが、シンプルなものとして以下の2つのパターンがあります。

  1. スクラッチでERC-4907のコントラクトコードを実装
  2. 外部サービスでERC-4907のコントラクトコードを生成

1つ目のパターンの場合は、ERC-4907で定義されるインターフェースをSolidityコードで実装し、そのインターフェースを活用するコントラクトのコードを実装します。
2つ目のパターンの場合は、いわゆるローコードなどの外部サービスを用いてERC-4907のコードを半自動的に生成し、その外部サービスのライブラリを活用して、主にNFTサービスを提供するUI部分の実装を進めていく方針です。
それでは、ここから、1つ目のスクラッチでコントラクトコードを実装する流れをご紹介します。

スクラッチ実装

スクラッチ実装する場合は、IERC4907というインターフェースを定義して実装する必要があります。

interface IERC4907 {

    // Logged when the user of an NFT is changed or expires is changed
    /// @notice Emitted when the `user` of an NFT or the `expires` of the `user` is changed
    /// The zero address for user indicates that there is no user address
    event UpdateUser(uint256 indexed tokenId, address indexed user, uint64 expires);

    /// @notice set the user and expires of an NFT
    /// @dev The zero address indicates there is no user
    /// Throws if `tokenId` is not valid NFT
    /// @param user  The new user of the NFT
    /// @param expires  UNIX timestamp, The new user could use the NFT before expires
    function setUser(uint256 tokenId, address user, uint64 expires) external;

    /// @notice Get the user address of an NFT
    /// @dev The zero address indicates that there is no user or the user is expired
    /// @param tokenId The NFT to get the user address for
    /// @return The user address for this NFT
    function userOf(uint256 tokenId) external view returns(address);

    /// @notice Get the user expires of an NFT
    /// @dev The zero value indicates that there is no user
    /// @param tokenId The NFT to get the user expires for
    /// @return The user expires for this NFT
    function userExpires(uint256 tokenId) external view returns(uint256);
}

IERC4907はNFT標準規格のERC-721を継承し、NFTレンタルを実現するインターフェースを提供します。

  • UpdateUser:指定NFTをレンタルしているユーザーアドレスや有効期限が変わったときに呼ぶ
  • setUser:指定NFTをレンタルする借主のユーザーアドレスをセットする
  • userOf:指定NFTをレンタルしている借主のユーザーアドレスを返す
  • userExpires:指定NFTのレンタルの有効期限を返す

もちろんこの4つのインターフェースだけでは、NFTレンタルサービスは提供できないため、継承元であるERC-721の規格で提供されるNFTの残高確認や転送などの機能も適宜実装していく必要があります。
NFTをレンタルした人をセットするsetUserの実装例を以下に示します。

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

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "./IERC4907.sol";

contract ERC4907 is ERC721URIStorage, IERC4907 {
  struct UserInfo {
    address user; // ユーザーアドレス
    uint64 expires; // 有効期限日時
  }
  mapping(uint256 => UserInfo) internal _users;
  constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}

  function setUser(uint256 tokenId, address user, uint64 expires) public virtual override {
    // NFTのレンタルを承認できるのはNFTオーナーか許可された承認者のみ
    require(
      _isApprovedOrOwner(msg.sender, tokenId),
      "ERC721: transfer caller is not owner nor approved"
    );
    UserInfo storage info = _users[tokenId];
    info.user = user;
    info.expires = expires;
    emit UpdateUser(tokenId, user, expires);
  }

上述したIERC4907に加え、継承元のERC-721のI/FであるURIStorageをインポートすることで、NFTトークンへのURI設定などNFTが以前から提供する機能を実現できます。
開発を進めていくうえで必要となるのがデバッグですが、OpenZeppelinのTest Helperを用いてJavaScriptコードでテストコードの実装とデバッグが可能です。

require("@openzeppelin/test-helpers/configure")({
  provider: web3.currentProvider,
  singletons: { abstraction: "truffle" },
});

const { constants, expectRevert, expectEvent } = require('@openzeppelin/test-helpers');
const RentablePets = artifacts.require("RentablePets");

contract("TestContract", function (accounts) {
  it("should return the correct UserInfo", async () => {
    const testContract = await TestContract.deployed();
    const expirationDate = 1668390813; // 期限切れの日付をセット
    await testContract.mint(constants.TOKEN_URI);
    var expiredTxHash = await testContract.setUser(1, accounts[1], expirationDate)
    var expiredNFTUser = await testContract.userOf.call(1);
    var expiredNFTDate = await testContract.userExpires.call(1);
    assert.equal(expiredNFTUser, constants.WRONG_ADDRESS, "誤ったユーザーアドレス");
    assert.equal(expiredNFTDate, expirationDate, "誤った有効期限");
    expectEvent(expiredTxHash, "UpdateUser", { tokenId: "1", user: accounts[1], expires: expirationDate.toString()});
  });
});

以上、スクラッチでのコントラクトコードの実装例を紹介しました。

Dapps開発を支えるサードパーティサービス群

上記のようなコードをベースに実際のサービスとして組み立てていく場合には、後述するAlchemyやTruffleなどを活用することでより便利に開発することが可能になります。
スマートコントラクトやNFTなどブロックチェーン技術を活用したサービスは、Decentralized Applications:通称Dappsと呼ばれ、一例として、NFTマーケットプレイスOpenseaや仮想通貨が稼げるゲームAxie Infinityなどがあります。
今回ERC-4907の実装プロトタイプを作成するにあたり、Dapps開発の方法や、フレームワーク・開発プラットフォームの調査を実施したため、その一部をご紹介します。

Dappsのアーキテクチャ

まず、Dappsのアーキテクチャは主に以下3つの要素から成り立っています。

  1. スマートコントラクトを実行するネットワークノード
  2. ネットワークにデプロイされたスマートコントラクト
  3. スマートコントラクトの実行をリクエストするフロントエンド
  4. Tech Blog NFTトークンレンタル規格 1
    参考:Architecture of a dApp(Ethereum)
    Architecture of a dApp – GeeksforGeeks
     
    上記アーキテクチャを全て自前で構築することも可能ですが、ブロックチェーンノードの環境構築は難易度が高く、運用コストもかかるため、開発者にとっては課題となっています。そのため、SDKやAPIなどを提供し、開発ハードルを下げる便利なサービスが数多く登場しています。特に、個人が手軽に開発をする上では、フレームワークや開発プラットフォームを採用するのが一般的です。

    フレームワーク・開発プラットフォーム

    Ganache

    Ganacheは、インストールすることで、ローカルでEthereumプライベートネットワークを構築できる開発フレームワークです。GUI版とCLI版があり、起動時にデフォルトで100ETHが入ったウォレットアカウントを発行します。開発環境のフレームワークとして利用し、Dappsのデプロイを行うことができます。

    Truffle

    スマートコントラクトのコンパイル・デプロイ・デバッグ・テストを支援する開発フレームワークです。ローカルにnpmでダウンロード可能で、VSCodeの拡張機能も用意されています。Ganacheと組み合わせて使用することで、Truffleで作成したスマートコントラクトをGanacheで構築したEtherriumネットワークにデプロイする、という一連の流れを簡単に実装することができます。

    Alchemy

    ブロックチェーン界のAWSを目指し、各種ブロックチェーン開発ツールを提供しているサービスです。アカウントを作成すれば、Alchemyの運用するネットワークノードへのエンドポイント、トークンやNFTに関するデータ取得用WEBAPIなどを利用することができます。

    Infura.io

    Alchemyと同様に、ブロックチェーン開発ツールを提供するサービスです。Metamaskウォレットを提供するConsensys社が運営しています。Alchemyと提供するサービスの違いはほとんどなく、サポートするネットワーク、提供するAPI、ドキュメントや料金プラン体系等が一部異なっています。

    ThirdWeb

    コードを書かずにスマートコントラクトをデプロイできるノーコードツール・プラットフォームです。NFTに必要なmetadataのIPFSへのアップロード機能もあり、特にNFT開発に関する機能については一通り揃っています。Thirdweb提供のSDKを介してwebアプリからスマートコントラクトの実行ができるため、フロント部分を実装すれば、NFTマーケットプレイスのようなサイトも簡単に構築することができます。ERC-4907に早くも対応しており、今回【後半】で記載する外部サービスを利用したプロトタイプ実装ではこちらを採用しました。
     

    これらのサードパーティサービスは、ドキュメントやコミュニティも充実しており、日々アップデートが活発に行われています。無料プランが提供されているものがほとんどなので、積極的に活用して開発に取り組んでみてください。

    【前半】の本記事では、NFTのレンタル規格「ERC-4907」について概要とスクラッチでの実装方法を紹介しました。実用にはまだ難しい部分の多い規格ですが、一時的なNFTの貸与が可能になることで、NFTのユースケースがさらに広がることが予想されます。本記事が規格の理解促進につながれば幸いです。
    【後半】の記事では、より詳細なサービスのユースケースの実装方法、および、ローコードの外部サービスを利用して簡単にコントラクトコードを生成する方法をご紹介します。

    参考資料