Solidityによるコントラクトの作成(2) - オプション取引
前回はSolidityの使い方について説明しました。
今回はSolidityを用いてオプション取引を実装してみましょう。
コントラクト概要
今回はコール・オプションを実装します。コール・オプションとはある資産を買う権利のことです。(ちなみにある資産を売る権利をプット・オプションといいます)。
コール・オプションのカラムは以下のようにします。
key | value |
---|---|
資産 | ビットコイン |
数量 | 1 |
権利行使価格 | ¥50,000 |
プレミアム | ¥5,000 |
期日 | 1463127335 |
売り手 | 0xfffe72483eb0bc804295af2416d2aeb4fa15a8bc |
買い手 | 0x0e7b3774402f2f19a05e7a279238a4a1fe5e2bc0 |
※1463127335は2016/05/13 8:15am(UTC)のUNIX TimeStampです
以下が完成形のコードです。
contract CallOption{ address public buyer; address public seller; uint public btc_amount; uint public btc_price; uint public premium; uint public exercise_date; mapping (address => uint) public balanceOfJPY; mapping (address => uint) public balanceOfBTC; function CallOption( address _seller, uint _seller_jpy, uint _seller_btc, uint _btc_price, uint _btc_amount, uint _premium, uint _exercise_date ){ seller = _seller; balanceOfJPY[seller] = _seller_jpy; balanceOfBTC[seller] = _seller_btc; btc_price = _btc_price; btc_amount = _btc_amount; premium = _premium; exercise_date = _exercise_date; } function Respond (address _buyer, uint _buyer_jpy) { buyer = _buyer; balanceOfJPY[buyer] = _buyer_jpy; if (balanceOfJPY[buyer] < premium + btc_amount * btc_price) throw; balanceOfJPY[buyer] -= premium; balanceOfJPY[seller] += premium; } function Expire (uint _current_btc_price, uint _current_time){ if (_current_time < exercise_date) throw; if (_current_btc_price < btc_price) throw; balanceOfJPY[buyer] -= btc_price * btc_amount; balanceOfJPY[seller] += btc_price * btc_amount; balanceOfBTC[buyer] += btc_amount; balanceOfBTC[seller] -= btc_amount; } function () { throw; } }
- CallOption:初期化(今回は、オプションの発行主体は常に売り手とします。)
- Respond:買い手の登録
- Expire:オプション実行
コントラクト登録
まずコードをコンパイルします。
> var source = "contract CallOption{ address public buyer; address public seller; uint public btc_amount; uint public btc_price; uint public premium; uint public exercise_date; mapping (address => uint) public balanceOfJPY; mapping (address => uint) public balanceOfBTC; function CallOption( address _seller, uint _seller_jpy, uint _seller_btc, uint _btc_price, uint _btc_amount, uint _premium, uint _exercise_date ){ seller = _seller; balanceOfJPY[seller] = _seller_jpy; balanceOfBTC[seller] = _seller_btc; btc_price = _btc_price; btc_amount = _btc_amount; premium = _premium; exercise_date = _exercise_date; } function Respond (address _buyer, uint _buyer_jpy) { buyer = _buyer; balanceOfJPY[buyer] = _buyer_jpy; if (balanceOfJPY[buyer] < premium + btc_amount * btc_price) throw; balanceOfJPY[buyer] -= premium; balanceOfJPY[seller] += premium; } function Expire (uint _current_btc_price, uint _current_time){ if (_current_time < exercise_date) throw; if (_current_btc_price < btc_price) throw; balanceOfJPY[buyer] -= btc_price * btc_amount; balanceOfJPY[seller] += btc_price * btc_amount; balanceOfBTC[buyer] += btc_amount; balanceOfBTC[seller] -= btc_amount; } function () { throw; }}" > var compiledSource = eth.compile.solidity(source)
続いて、コントラクトをEthereumのネットワークに送信します。
> var abiDefinition = compiledSource.CallOption.info.abiDefinition > var compiledContract = eth.contract(abiDefinition) > var seller = "0xfffe72483eb0bc804295af2416d2aeb4fa15a8bc" > var buyer = "0x0e7b3774402f2f19a05e7a279238a4a1fe5e2bc0" > var contract = compiledContract.new(seller, 100000, 1, 50000, 1, 1463127335, {from: seller, data: compiledSource.CallOption.code, gas:1000000})
以下のようにコントラクトが送信されました。
しかしまだマイニングされていないので、アドレスは割り当てられていません。
> contract { address: undefined, transactionHash: "0xf194ae6298b85b7fa6b0a9b75d115181ffb44ef546e4da9bcd970b943b01fa02" }
マイニングが完了すると以下のようになります。
アドレスが付与されるているのがわかります。
> contract { address: "0x0bc2c62ecc340e0a4606d087f2d9d9afe0ef16d6", transactionHash: "0xf194ae6298b85b7fa6b0a9b75d115181ffb44ef546e4da9bcd970b943b01fa02", Expire: function(), Respond: function(), allEvents: function(), balanceOfBTC: function(), balanceOfJPY: function(), btc_amount: function(), btc_price: function(), buyer: function(), exercise_date: function(), premium: function(), seller: function() }
Respondメソッド
初期化が完了しました。続いてRespondメソッドを実行しましょう。
以下がコードです。
第一引数は買い手のアドレス、第二引数は買い手のJPY残高を表しています。
function Respond (address _buyer, uint _buyer_jpy) { buyer = _buyer; balanceOfJPY[buyer] = _buyer_jpy; if (balanceOfJPY[buyer] < premium + btc_amount * btc_price) throw; balanceOfJPY[buyer] -= premium; balanceOfJPY[seller] += premium; }
以下の条件文は、もしも買い手のJPY残高が (ビットコインの購入費+プレミアム) より少ない場合は、このメソッドは無効にするものです。
if (balanceOfJPY[buyer] < premium + btc_amount * btc_price) throw;
メソッドを呼び出しましょう。
> contract.Respond.sendTransaction(buyer, 100000, {from: buyer, gas:1000000})
まだマイニングが完了していないので、売りてと買い手のJPY残高は変化していません。
> contract.balanceOfJPY(seller) 100000 > contract.balanceOfJPY(buyer) 0
マイニング完了後は以下のように残高が変化します。プレミアム分だけ売り手の残高が増えているのがわかります。
> contract.balanceOfJPY(seller) 105000 > contract.balanceOfJPY(buyer) 95000
Expireメソッド呼び出し
最後にExpireメソッドを実行しましょう。これは期日を過ぎたら、契約内容に沿って取引を実効するメソッドです。
function Expire (uint _current_btc_price, uint _current_time){ if (_current_time < exercise_date) throw; if (_current_btc_price < btc_price) throw; balanceOfJPY[buyer] -= btc_price * btc_amount; balanceOfJPY[seller] += btc_price * btc_amount; balanceOfBTC[buyer] += btc_amount; balanceOfBTC[seller] -= btc_amount; }
期日に達してない場合は無効とする条件文です。
if (_current_time < exercise_date) throw;
現在のBTC価格が、権利行使価格より安い場合は無効とする条件文です。
if (_current_btc_price < btc_price) throw;
メソッドを呼び出します。
contract.Expire.sendTransaction(55000, 1463127340, {from:seller, gas:1000000}
BTC価格は¥55000(>¥50000)、現在時刻は1463127340(>1463127335)であるため、契約は実行されるはずです。
マイニング前
> contract.balanceOfBTC(seller) 1 > contract.balanceOfBTC(buyer) 0 > contract.balanceOfJPY(seller) 105000 > contract.balanceOfJPY(buyer) 95000
マイニング後
> contract.balanceOfBTC(seller) 0 > contract.balanceOfBTC(buyer) 1 > contract.balanceOfJPY(seller) 155000 > contract.balanceOfJPY(buyer) 45000
契約条件に沿って、売り手と買い手のJPY残高とBTC残高が変化しているのが確認出来ました。