この記事は

この記事は Speee Advent Calendar 2017 10日目の記事です。
9日目は @yamakei7323 による 顔を認識したい。 でした。

 スマートコントラクトの設計と実装

スマートコントラクトの勉強のため5000兆円トークンというジョーク通貨を作って最近社内でバラまいているのですが
https://github.com/iida-hayato/gosenchouen

今回は実際に実装してみてのスマートコントラクトのポイントをまとめたいと思います。

コードはこちら。
https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol

通貨の名前と単位

まずは通貨の名前を決めましょう。
通貨の顔になるので素敵な名前をつけてあげます。

変数名 説明
name 通貨 5000兆円
symbol 単位 GSC
    string public constant symbol = "GSC";
    string public constant name = "5000兆円";

https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol#L31-L32

有効数字

通貨をやりとりする時の最小単位を定義します。

例えば有効数字18にすれば最小保有数は0.1¹⁸になります。

Etheriumは18桁になっていますのでEtheriumとのやりとりを行う場合
余計な計算を入れないためにも18桁に合わせておくのが良いでしょう。

変数名 説明
decimal 有効数字 18
    uint8 public decimals = 18;

https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol#L34

また後の計算を簡単にするために10¹⁸を変数に持っておきます。

変数名 説明
_unit 1/有効数字 10¹⁸
    uint256 _unit = 1000000000000000000;

https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol#L35

通貨の発行量上限

上限額は通貨の価値戦略の基本になります。
実装次第で、発行量上限を持たせずに無限に発行することや、特定のロジックに基づいて動的に上限を変動する事も可能です。
例えばBitCoinの用にある上限まで漸近的に増えているトークンや毎年発行量が増えていくようなトークンを作ることができます。

5000兆円トークンは適当に5000兆個を上限にしています。

変数名 説明
decimal 有効数字 5000000000000000 * _unit

https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol#L35

Ethereumでトークンを購入する

ここから関数の実装になります。
まずは新しいユーザーがトークンを手に入れるために、Ethereumをトークンに変える関数を用意します。

交換レート

交換するためにはEthereum/Tokenを決める必要があります。
以下の例では定額 1GSC=0.001ETH としていますが、トークンの在庫に応じて動的にレートが変わるようにロジックを組んでも面白いと思います。

    uint256 public buyPrice = 0.001 ether;

https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol#L155

buyメソッド

上記で指定したレートでEthereumでトークンを購入します。
最初はownerが全ての在庫トークンを保有しているのでそこから払い出すような形になります。

変数名 説明
owner ownerのWallet
msg.sender 送信者のWallet
msg.value 送られてきたEthereumの数(単位ETH)
    function buy() payable public {
        require(msg.value > 0);
        require(msg.value >= buyPrice);
        uint256 amount = msg.value.div(buyPrice);

        _transfer(owner, msg.sender, amount);        
        owner.transfer(msg.value);
    }

https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol#L204-L212

5000兆円には実装していないのですが、同じやりかたでトークンをEthereumにsellする機能を作ることも可能です。動的にレートが変動する機能と併せて擬似的な取引所をつくれそうです。

Wallet間でTokenを交換する

トークンの流通を実装するために、ユーザー間でトークンをやりとり出来るようにします。

変数名 説明
from 送り元のWallet
to 送り先のWallet
value トークンの数(単位GSC)
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= allowance[_from][msg.sender]);
        allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value);
        _transfer(_from, _to, _value);
        return true;
    }

https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol#L102-L107

その他の実装

オーナー機能

ユーザーの凍結やレートの調整など管理系の機能も併せて実装しておきます。

    /// @notice `freeze? Prevent | Allow` `target` from sending & receiving tokens
    /// @param target Address to be frozen
    /// @param freeze either to freeze it or not
    function freezeAccount(address target, bool freeze) onlyOwner public {
        frozenAccount[target] = freeze;
        FrozenFunds(target, freeze);
    }

    /// @notice Allow users to buy tokens for `newBuyPrice` eth
    /// @param newBuyPrice Price users can buy from the contract
    function setPrices(uint256 newBuyPrice) onlyOwner public {
        buyPrice = newBuyPrice;
    }

https://github.com/iida-hayato/gosenchouen/blob/master/gosenchouen.sol#L192-L201

数値の正負について

計算前後でやたらと負数のチェックが入っていますが、これは購入や交換の引数に負の値を入れて保有量をマイナスにしたり
相手のトークンを奪ったりといったバグを防ぐためにやっています。

例外発生時のロールバックについて

処理中にExceptionが発生した場合は処理全体がロールバックされるようです(Gasはマイナーに消費される)。
この辺上手く使えばもうちょっと完結に安全なコードを書けそうですね。

感想

実際に実装してみるとふわっとしていた「スマートコントラクト」へのイメージが大分明確になりました。
コードだけ書いておけばインフラを全く気にしないでコードが動くという意味では、新しいサーバーレスの形と言えそうです。
一方で独立したラムダ関数だけでサービスを設計するような難しさはあるので、このあたりをどう解決するかが課題になると思います。