ERC721を活用した、全てのロジックとデータをSolidityで管理する方法(後編)

本記事はERC721を活用した、全てのロジックとデータをSolidityで管理する方法(前編)の後編です。
全てのロジックとデータをSolidityで実装しようとすると記述量がものすごい増え、一瞬でカオスになってしまうので、前編ではマスタデータをSolidityで管理する際にコード量を減らしてシンプルする方法をまとめました。
後編ではロジックをシンプルにする手法をまとめてみたいと思います。

手法1. OpenZeppelinのRoleコントラクトを使い、分離したロジック側コントラクトからマスタデータ管理用コントラクトにアクセスする

前編にある「マスタデータ関連ロジックとその他ロジックの分離」を単純にそのまま実行してしまうと、分離したロジック側のコントラクトから、マスタデータ管理用コントラクト内のデータを更新する系のfunctionを呼ぶことができなくなります。
そこで、OpenZeppelinのRoleコントラクトを継承したManagerRoleを作成し、マスタデータ用コントラクトにManagerRoleコントラクトを継承させます。
これにより、Managerとして設定されたコントラクトからもデータを更新ができるようになります。

// CatMaster.sol
import "./modules/MasterDataModule.sol";
import "./utils/ManagerRole.sol";

contract CatMaster is MasterDataModule, ManagerRole {

    struct Cat { string name; uint256 age;}
    Cat[] public cats;

    function createCat(string name, uint256 age) external onlyManager {
        Cat memory cat;
        cat.name = name;
        cat.age = age;
        uint256 catId = cats.push(cat) - 1;
        _mint(msg.sender, catId);
    }

    :

}

ただしこの手法を用いると、コントラクトオーナーであれば好きなアドレスをManagerに設定でき、コントラクトオーナーが好き勝手できてしまうイメージを持たれてしまう可能性があるので、純粋に資産として扱うERC20やERC721のようなコントラクトでは用いないほうが良いでしょう。

手法2. Factoryパターンの導入

前述のCatMasterコントラクトのCatのような簡単な構造体を管理するコントラクトであれば、1つのコントラクト内で複数のデータを管理しても問題ないと思いますが、例えばERC20のような複雑なデータを複数管理する場合は、1つのコントラクト内で実装しようとすると一瞬でカオスになります。
そこで Factoryパターンの出番です。
例えばERC20を複数管理するとした場合に、ERC20のコントラクト自体はそのまま通常通りデプロイします。
そのデプロイした際のコントラクトアドレスをFactoryコントラクト内で管理することで、Factoryコントラクトを通じて任意のコントラクトを参照できるようにします。

// TokenFactory.sol
contract TokenFactory {
    address[] public contracts;

    function addToken(address _contractAddress public returns (bool) {
        contracts.push(_contractAddress);
        return true;
    }

    function contractOf(uint256 _tokenId) public constant returns(address) {
        return contracts[_tokenId];
    }
}

上記コントラクトでは、管理対象のコントラクト(ERC20)のデプロイはコントラクト外で実施し、アドレス登録の処理だけをFactoryコントラクト内で実行することを想定しています。
デプロイ自体もFactoryコントラクト内で実行することも可能ですが、gas limitに引っかかってデプロイできないことが多いので、デプロイ自体はコントラクト外で実施したほうが無難と思われます。

手法3. ロジックの共通化、切り出し

コードの量を減らすために些細な処理でも共通化できる部分は共通モジュール化し、必要に応じて継承して使うようにします。
これは他の言語でも当たり前のことですが、Solidityにおいては別の意味でも重要となります。
Solidityでfunction内のロジックが複雑化して使用する変数が増えると、すぐに下記のエラーが発生します。
Stack too deep, try removing local variables.
このエラーは処理を共通化して外出ししていると起きにくくなるので、Solidityにおいて処理の共有化や切り出しはとても重要になります。

まとめ

前編、後編とで様々なSolidityの実装における工夫や懸念点をまとめました。
これによりSolidityで全てのロジックとデータを管理するコントラクトを開発する際に、比較的簡単に実装できるようになると思います。
是非試してみてください。


Qiitaでも書いています
ERC721を活用した、全てのロジックとデータをSolidityで管理する方法(後編)

The following two tabs change content below.
Akihiro Tanaka

Akihiro Tanaka

Smart Contract Engineer
Since 2009, I have been a software engineer at Accenture for 9 years, managing, designing, and developing many services, mainly web and mobile apps.
In 2013, I met Bitcoin and started to work on blockchain-related development in 2018, developing an entertainment DApp for underground idols, a blockchain analysis tool, and an STO platform.
Currently, I am working as a Smart Contract Engineer at Secured Finance, developing a DeFi product.

WEB: https://tanakas.org/