5
0

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 5 years have passed since last update.

ZeppelinOSでUpgradableなERC721トークンを実装する

Last updated at Posted at 2019-02-24

はじめに

基本的にEthereumに一度デプロイされたコントラクトはアップグレードできないため、コードにバグがあるとプロダクトにとって致命傷となってしまいます。
ですが、Proxyパターンを利用することによりアップグレード可能なコントラクトを作成することが可能になります。

Proxyパターンの理解は以下の記事が非常に分かりやすくまとめてくださっているので、まずはこちらをご一読の上、続きをご覧いただければと思います。

■アップグレード可能なスマートコントラクトを実現する具体的なアプローチ
https://zoom-blc.com/how-to-develop-upgradable-contracts

今回はZeppelinOSというアップグレード可能なコントラクトを実装するためのフレームワークを利用してERC721トークンを作っていきます。(ZeppelinOSを使わなくてもProxyContractを自分で書くことで実現できますが、その作業が不要になったりと何かと嬉しいです)

まずは成果物

成果物はこちらに上げています。
https://github.com/shiruco/upgradable-ERC721-sample

内容を簡単に説明します。
以下、ローカル環境のプライベートチェーンで動作させるものとします。

  1. まず SampleToken (ERC721) をデプロイ。
  2. 続けてSampleTokenのアップグレード版 SampleToken_v1 (ERC721)をデプロイ。
  3. SampleTokenの実装がSampleToken_v1に変更されていることを確認

という流れです。

開発環境

私の開発環境は以下のようになっています。もし実際に試されるのであればバージョンを揃えていただいた方がいいかもしれません。

  • ZeppelinOS 2.2.0
  • Truffle v5.0.4 (core: 5.0.4)
  • Solidity v0.5.0 (solc-js)
  • Ganache CLI v6.3.0 (ganache-core: 2.4.0)
  • Node v11.3.0

ZeppelinOSのインストールはこちら。 (私はグローバルに入れました)

npm install -g zos
or
yarn global add zos

中身の解説

contractディレクトリの中には SampleTokenSampleToken_v1 というコントラクトが入っています。それぞれのコードは以下のようになっており、差分はversion情報を返す関数の返却値が違うのみです。(SampleTokenはv0を。SampleToken_v1はv1を返します)

SampleToken

中身について軽く触れておくと、zos-libopenzeppelin-ethというライブラリを使用しています。zos-libはアップグレード可能なコントラクトを記述するためのライブラリで、constructorの代わりにinitialize関数を実装します。
openzeppelin-ethはセキュアなスマートコントラクトを実装するためのライブラリです。 openzeppelin-solidityとの違いにちょっと混乱しましたが、リポジトリのREADMEを見ると、openzeppelin-solidityとの大きな違いはopenzeppelin-ethはアップグレードする可能性のあるコントラクトになっているようです。ZeppelinOSを利用するならopenzeppelin-ethを利用したほうがよいでしょう。

The main difference is that all contracts in this package are potentially upgradeable
:
All in all, you should use this package instead of openzeppelin-solidity if you are managing your project via ZeppelinOS.


pragma solidity ^0.5.0;

import 'zos-lib/contracts/Initializable.sol';
import 'openzeppelin-eth/contracts/token/ERC721/ERC721Full.sol';
import 'openzeppelin-eth/contracts/token/ERC721/ERC721Mintable.sol';

contract SampleToken is Initializable, ERC721Full, ERC721Mintable {

  function initialize(string memory name, string memory symbol) public initializer {
    ERC721.initialize();
    ERC721Enumerable.initialize();
    ERC721Metadata.initialize(name, symbol);
    ERC721Mintable.initialize(msg.sender);
  }

  function version() public view returns (string memory) {
    // set version
    string memory ver = "v0";

    return ver;
  }
}

SampleToken_v1


pragma solidity ^0.5.0;

import "zos-lib/contracts/Initializable.sol";
import 'openzeppelin-eth/contracts/token/ERC721/ERC721Full.sol';
import 'openzeppelin-eth/contracts/token/ERC721/ERC721Mintable.sol';

contract SampleToken_v1 is Initializable, ERC721Full, ERC721Mintable {

  function initialize(string memory name, string memory symbol) public initializer {
    ERC721.initialize();
    ERC721Enumerable.initialize();
    ERC721Metadata.initialize(name, symbol);
    ERC721Mintable.initialize(msg.sender);
  }

  function version() public view returns (string memory) {
    // set version
    string memory ver = "v1"; <=== ここが違う

    return ver;
  }
}

zosコマンドでデプロイ

では実際にこのコントラクトをデプロイして動作を確認していきます。
npmインストール後、下記のコマンドでZeppelinOSプロジェクトを初期化します。
プロジェクト名は好きに指定してください。

$ zos init {プロジェクト名}

初期化が終わるとプロジェクトルート直下にzos.jsonが生成されます。zos.jsonはZeppelinOSプロジェクトのコントラクト情報やバージョン情報などが記載されています。

続けて下記コマンドでZeppelinOSプロジェクトにSampleTokenコントラクトを追加します。

$ zos add SampleToken

次にSampleTokenをデプロイするのですが、デプロイ先として今回はローカルでプライベートチェーンを立てておきます。ganache-cliを利用して、7545番ポートで立ち上げます。

$ ganache-cli -p 7545 -d

準備が整ったので、下記コマンドでデプロイします。networkにはlocalを指定します。これはtruffle-config.jsに定義されている情報を参照します。

$ zos push --network local

成功するとzos.dev-{ネットワークID}.jsonファイルが生成されます。中身をみると、SampleTokenコントラクトにaddressが振られており、無事デプロイされていることが分かります。(この時点ではproxiesフィールドの中身は空です)
続いてこのコントラクトのProxyContractをデプロイしていきます。ProxyContractをデプロイするには以下のコマンドで行います。引数にnamesymbolを渡しています。

$ zos create SampleToken --init initialize --args="SampleToken,STKN" --network local

無事にデプロイされると、zos.dev-{ネットワークID}.jsonのproxiesフィールドにProxyContractのaddressが入っているはずです。また、implementationがSampleTokenのaddressを指しています。
これでProxyContractを通してSampleTokenにアクセスできると思います。実際に試してみます。

$ truffle console --network local
truffle(local)> token = await SampleToken.at(<proxy-address>)
truffle(local)> token.version()
'v0'

version関数でv0が返ってきたのでうまくいっているようです。

ではSampleTokenをアップグレードします。SampleToken_v1がアップグレード版となっており、これを同様にデプロイします。(SampleToken_v1をSampleTokenのエイリアスとして追加し、デプロイしています。)

$ zos add SampleToken_v1:SampleToken
$ zos push --network local

ProxyContractはすでにデプロイされているのでcreateではなく、updateします。

$ zos update SampleToken --network local

これでアップグレード作業は完了です。
zos.dev-{ネットワークID}.jsonのproxiesフィールドにあるimplementationのアドレスがSampleToken_v1のaddressを指すように変更された)

ではもう一度確認してみます。

$ truffle console --network local
truffle(local)> token = await SampleToken.at(<proxy-address>)
truffle(local)> token.version()
'v1'

v1が返ってきたので、無事アップグレードされていました。

Truffleでデプロイ

上記はzosコマンドでデプロイしていきましたが、サンプルにはmigrationファイルが用意されており、truffle migrate でも確認できるようになっていますので、試してみてください。

$ zos init {プロジェクト名}
$ truffle migrate --network local
$ truffle console --network local
truffle(local)> token = await SampleToken.at(<proxy-address>)
truffle(local)> token.version()
'v1'

参考

https://blog.zeppelinos.org/proxy-patterns/
https://zoom-blc.com/how-to-develop-upgradable-contracts

5
0
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
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?