この記事は
この記事は 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兆円";
有効数字
通貨をやりとりする時の最小単位を定義します。
例えば有効数字18にすれば最小保有数は0.1¹⁸になります。
Etheriumは18桁になっていますのでEtheriumとのやりとりを行う場合
余計な計算を入れないためにも18桁に合わせておくのが良いでしょう。
変数名 | 説明 | 例 |
---|---|---|
decimal | 有効数字 | 18 |
uint8 public decimals = 18;
また後の計算を簡単にするために10¹⁸を変数に持っておきます。
変数名 | 説明 | 例 |
---|---|---|
_unit | 1/有効数字 | 10¹⁸ |
uint256 _unit = 1000000000000000000;
通貨の発行量上限
上限額は通貨の価値戦略の基本になります。
実装次第で、発行量上限を持たせずに無限に発行することや、特定のロジックに基づいて動的に上限を変動する事も可能です。
例えばBitCoinの用にある上限まで漸近的に増えているトークンや毎年発行量が増えていくようなトークンを作ることができます。
5000兆円トークンは適当に5000兆個を上限にしています。
変数名 | 説明 | 例 |
---|---|---|
decimal | 有効数字 | 5000000000000000 * _unit |
Ethereumでトークンを購入する
ここから関数の実装になります。
まずは新しいユーザーがトークンを手に入れるために、Ethereumをトークンに変える関数を用意します。
交換レート
交換するためにはEthereum/Tokenを決める必要があります。
以下の例では定額 1GSC=0.001ETH としていますが、トークンの在庫に応じて動的にレートが変わるようにロジックを組んでも面白いと思います。
uint256 public buyPrice = 0.001 ether;
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);
}
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;
}
その他の実装
オーナー機能
ユーザーの凍結やレートの調整など管理系の機能も併せて実装しておきます。
/// @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;
}
数値の正負について
計算前後でやたらと負数のチェックが入っていますが、これは購入や交換の引数に負の値を入れて保有量をマイナスにしたり
相手のトークンを奪ったりといったバグを防ぐためにやっています。
例外発生時のロールバックについて
処理中にExceptionが発生した場合は処理全体がロールバックされるようです(Gasはマイナーに消費される)。
この辺上手く使えばもうちょっと完結に安全なコードを書けそうですね。
感想
実際に実装してみるとふわっとしていた「スマートコントラクト」へのイメージが大分明確になりました。
コードだけ書いておけばインフラを全く気にしないでコードが動くという意味では、新しいサーバーレスの形と言えそうです。
一方で独立したラムダ関数だけでサービスを設計するような難しさはあるので、このあたりをどう解決するかが課題になると思います。