仮想通貨コントラクトを作成してみる
どうも、ぺろりんです。
「はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)」のつづきです。
このシリーズでは、前回はSolidityの仕様についてまとめました。
この本もようやく実践編にきました!
今回は、教科書の実践編で基本的な仮想通貨コントラクトを作成するところをBrowser-Solidity上でやっていきます。
ここではこれまで通り、JavaScript VMで実行するのでオンライン上で完結した環境です。
仮想通貨コントラクトのデプロイ
教科書通りのコードで「OreOreCoin」なるものをデプロイしていきます。
が、そのままのコードだとこんな感じでコンパイル時にエラーが出ます。

すべて警告で、内容的には以下の4種類です。(キャプチャの順番とは前後していますが、あとの説明用にこの順で載せています)
これら4種類の警告について、それぞれ対処してみましょう。
Warning: No visibility specified. Defaulting to "public".
「public」のところは以前調べたのと同じで、以下のように「public」を入れると消えました。
Warning: Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead.
これも以前調べたのと同じです。
以下のように、コンストラクタのところで「function OreOreCoin(…」としていたのを「constructor(…」に直すと上手くいきました。
Warning: "throw" is deprecated in favour of "revert()", "require()" and "assert()".
「throw」ではなく、「revert()」、「require()」、「assert()」の使用が推奨されているみたいです。
たとえば「require()」なら、どうやら
if(<条件>) throw
と
require(<throwのときに各条件の反対>)
が同等の意味になるようです。(「反対」の意味は、おそらく補集合と考えると良さそう)
「throw」を一番簡単に書き換えるとすると、「throw」を「revert()」に置き換えれば上手くいきそうです。
実際、
を
に書き換えて再度コンパイルしてみると、この部分のWarningが消えました。
これに関しては、参考に挙げたSolidityのassertとrequireとrevertの違い(アルゴリズムとかオーダーとか)にとても分かりやすくまとめられていました。
最近のバージョンのSolidityでは「throw」が「revert()」に自動で変換されるようです。
参考サイトにあった内容をさらに短くまとめておくと、以下の内容のようです。
・revert(<メッセージ>) → ここでコントラクトは停止して実行前に戻す。残りのガスは返却。条件は指定できない。
・require(<条件>) → ここでコントラクトは停止して実行前に戻す。残りのガスは返却。条件を指定する。
・assert(<条件>) → ここでコントラクトは停止して実行前に戻す。残りのガスも消費。条件を指定する。
(参考)
・ERC20 Token Standard Interface を使った Smart Contractの実装について(Qitta)
・Error creating contracts on Ethereum wallet(StackeEchange)
・Solidityのassertとrequireとrevertの違い(アルゴリズムとかオーダーとか)
・Error handling: Assert, Require, Revert and Exceptions(Solidity)
Warning: Invoking events without "emit" prefix is deprecated.
さしあたり、解決策としては
の冒頭に「emit」を付けて
とすれば、この部分のWarningが消えました。
これでようやく怒られないコードになりました\(^o^)/
(追記:2018/9/3)--
参考に挙げた「‘emit’ keyword in Solidity(Medium)」によると、これはv0.4.21以降で推奨される書式で、イベントの呼び出しを明示するためのようです。
明示する理由の1つがDAO事件とありますが、いまいちピンとこない。。
ちょっと調べた感じではこの辺の関連を見つけられなかったので、分かる方ぜひ教えてください。
--(追記ここまで)
(参考)
・‘emit’ keyword in Solidity(Medium)
・Invoking events without “emit” prefix is deprecated in Transfer(msg.sender, _to, _value);(StackExchenge)
・【備忘録】【Solidity】【Truffle】エラー:Warning: Invoking events without "emit" prefix is deprecated.(katonobo’s blog)
・Warning: Invoking events without "emit" prefix is deprecated #8(GitHub)
OreOreCoinをデプロイしてみる
ようやくコンパイルが(ほぼ)綺麗に通ったので、この状態でデプロイしてみます。
(実はまだメッセージは残るんですが、無害そうなのでとりあえず先に進みます。)
Deployのところを開いて、テキスト通り値を入力しつつtransactします。


うまいことデプロイできました。

ボタンを押してコンストラクタの値を取得してみると、デプロイ時に入力した内容が反映されていることが確認できます。

balanceOfでユーザーアカウントごとのOreOreCoin残高も確認してみましょう。
まずはデプロイしたユーザーアカウントです。

この「…a733c」(以下、「ユーザーA」と呼ぶことにします)というアカウントアドレスを、「"」(ダブルクォーテーション)で囲んでbalanceOfのところに入力してbalanceOfボタンを押します。ちゃんとユーザーAが 10000 [oc]持っていることがわかります。


次に、別の「…c160c」(以下、「ユーザーB」と呼ぶことにします)というユーザーアカウントについて、残高確認をしてみましょう。

OreOreCoinの送金はまだしていないので、ユーザーA以外のアカウントは残高 0 [oc]のはずです。
先ほどと同様の手順で、今度はユーザーBのアカウントアドレスでbalanceOfしてみると、たしかに残高が 0 [oc]であることが確認できます。


OreOreCoinを送金してみる
デプロイがうまくいって、現状の値確認もできたので、今度は送金してみましょう。
ユーザーAからユーザーBへの送金を試みてみます。
失敗例
transferのところに、送り先であるユーザーBアドレスと、送りたい値( 2000 [oc])を入力してtransactします。
ここで、送り主のアドレスはグローバル変数msg.senderにより、現在の送信主アドレスを自動取得するため、特に入力はしません。

で、やってみましたが、うまくいかない……

画像にあるエラーメッセージを見てみると、「from」のところがなぜかユーザーBになっている。。
どうやら原因は、先ほどbalanceOfしたときの流れでやっていたので、このときに取得したユーザーBのmsg.senderの値が残っていたか、もしくはBrowser-SolidityのAccount欄アカウントを間違ってユーザーBにしたままtransferやってたせいでtransferのときに取得するmsg.senderの値がユーザーBになってしまったかで、残高 0 [c]のユーザーBから送金しようとしていました。
たぶん後者の、Account欄の問題かな……?

いずれにしろ、msg.senderでユーザーBのアドレスを取得してtransferしようとしたことが原因でした。
そして、まだOreOreCoinを持っていないユーザーBからの送金はできないと、エラーになりました。
成功例
原因がわかったので、Browser-SolidityのAccount欄をユーザーAにしつつ、念のためもう一度ユーザーAについてbalanceOfを実行してから、あらためてユーザーBへ 2000 [oc]送金してみたら、今度はうまく送金できました。



ユーザーA、ユーザーBそれぞれについて、送金後の残高をbalanceOfで確認してみます。
まずはユーザーA。

つづいてユーザーB。

ユーザーAからユーザーBへ、2000 [oc]がちゃんと送金できたことが確認できました。
まとめ
今回は、「はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)」の実践編にあるOreOreCoinという、簡単な仮想通貨のサンプルコードをデプロイして、状態確認と送金をやってみました。
テキストのサンプルコードをそのままコンパイルすると4種類の警告が出たので、それらの対処もしてみました。
送金はいい感じに教育的な内容で(笑)失敗しましたが、エラー内容をもとに対応してうまく解決できました。
警告とかエラーの対処が、やっぱり勉強になるなぁとあらためて実感しました。
ではでは今回はこのへんで。
「はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)」のつづきです。
このシリーズでは、前回はSolidityの仕様についてまとめました。
この本もようやく実践編にきました!
今回は、教科書の実践編で基本的な仮想通貨コントラクトを作成するところをBrowser-Solidity上でやっていきます。
ここではこれまで通り、JavaScript VMで実行するのでオンライン上で完結した環境です。
仮想通貨コントラクトのデプロイ
教科書通りのコードで「OreOreCoin」なるものをデプロイしていきます。
が、そのままのコードだとこんな感じでコンパイル時にエラーが出ます。

すべて警告で、内容的には以下の4種類です。(キャプチャの順番とは前後していますが、あとの説明用にこの順で載せています)
Warning: No visibility specified. Defaulting to "public".
Warning: Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead.
Warning: "throw" is deprecated in favour of "revert()", "require()" and "assert()".
Warning: Invoking events without "emit" prefix is deprecated.
これら4種類の警告について、それぞれ対処してみましょう。
Warning: No visibility specified. Defaulting to "public".
「public」のところは以前調べたのと同じで、以下のように「public」を入れると消えました。
function OreOreCoin(uint256 _supply, string _name, string _symbol, uint8 _decimals) public {
function transfer(address _to, uint256 _value) public {
Warning: Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead.
これも以前調べたのと同じです。
以下のように、コンストラクタのところで「function OreOreCoin(…」としていたのを「constructor(…」に直すと上手くいきました。
constructor(uint256 _supply, string _name, string _symbol, uint8 _decimals) public {
Warning: "throw" is deprecated in favour of "revert()", "require()" and "assert()".
「throw」ではなく、「revert()」、「require()」、「assert()」の使用が推奨されているみたいです。
たとえば「require()」なら、どうやら
if(<条件>) throw
と
require(<throwのときに各条件の反対>)
が同等の意味になるようです。(「反対」の意味は、おそらく補集合と考えると良さそう)
「throw」を一番簡単に書き換えるとすると、「throw」を「revert()」に置き換えれば上手くいきそうです。
実際、
if (balanceOf[msg.sender] < _value) throw;
if (balanceOf[_to] + _value < balanceOf[_to]) throw;
を
if (balanceOf[msg.sender] < _value) revert();
if (balanceOf[_to] + _value < balanceOf[_to]) revert();
に書き換えて再度コンパイルしてみると、この部分のWarningが消えました。
これに関しては、参考に挙げたSolidityのassertとrequireとrevertの違い(アルゴリズムとかオーダーとか)にとても分かりやすくまとめられていました。
最近のバージョンのSolidityでは「throw」が「revert()」に自動で変換されるようです。
参考サイトにあった内容をさらに短くまとめておくと、以下の内容のようです。
・revert(<メッセージ>) → ここでコントラクトは停止して実行前に戻す。残りのガスは返却。条件は指定できない。
・require(<条件>) → ここでコントラクトは停止して実行前に戻す。残りのガスは返却。条件を指定する。
・assert(<条件>) → ここでコントラクトは停止して実行前に戻す。残りのガスも消費。条件を指定する。
(参考)
・ERC20 Token Standard Interface を使った Smart Contractの実装について(Qitta)
・Error creating contracts on Ethereum wallet(StackeEchange)
・Solidityのassertとrequireとrevertの違い(アルゴリズムとかオーダーとか)
・Error handling: Assert, Require, Revert and Exceptions(Solidity)
Warning: Invoking events without "emit" prefix is deprecated.
さしあたり、解決策としては
Transfer(msg.sender, _to, _value);
の冒頭に「emit」を付けて
emit Transfer(msg.sender, _to, _value);
とすれば、この部分のWarningが消えました。
これでようやく怒られないコードになりました\(^o^)/
(追記:2018/9/3)--
参考に挙げた「‘emit’ keyword in Solidity(Medium)」によると、これはv0.4.21以降で推奨される書式で、イベントの呼び出しを明示するためのようです。
明示する理由の1つがDAO事件とありますが、いまいちピンとこない。。
ちょっと調べた感じではこの辺の関連を見つけられなかったので、分かる方ぜひ教えてください。
--(追記ここまで)
(参考)
・‘emit’ keyword in Solidity(Medium)
・Invoking events without “emit” prefix is deprecated in Transfer(msg.sender, _to, _value);(StackExchenge)
・【備忘録】【Solidity】【Truffle】エラー:Warning: Invoking events without "emit" prefix is deprecated.(katonobo’s blog)
・Warning: Invoking events without "emit" prefix is deprecated #8(GitHub)
OreOreCoinをデプロイしてみる
ようやくコンパイルが(ほぼ)綺麗に通ったので、この状態でデプロイしてみます。
(実はまだメッセージは残るんですが、無害そうなのでとりあえず先に進みます。)
Deployのところを開いて、テキスト通り値を入力しつつtransactします。


うまいことデプロイできました。

ボタンを押してコンストラクタの値を取得してみると、デプロイ時に入力した内容が反映されていることが確認できます。

balanceOfでユーザーアカウントごとのOreOreCoin残高も確認してみましょう。
まずはデプロイしたユーザーアカウントです。

この「…a733c」(以下、「ユーザーA」と呼ぶことにします)というアカウントアドレスを、「"」(ダブルクォーテーション)で囲んでbalanceOfのところに入力してbalanceOfボタンを押します。ちゃんとユーザーAが 10000 [oc]持っていることがわかります。


次に、別の「…c160c」(以下、「ユーザーB」と呼ぶことにします)というユーザーアカウントについて、残高確認をしてみましょう。

OreOreCoinの送金はまだしていないので、ユーザーA以外のアカウントは残高 0 [oc]のはずです。
先ほどと同様の手順で、今度はユーザーBのアカウントアドレスでbalanceOfしてみると、たしかに残高が 0 [oc]であることが確認できます。


OreOreCoinを送金してみる
デプロイがうまくいって、現状の値確認もできたので、今度は送金してみましょう。
ユーザーAからユーザーBへの送金を試みてみます。
失敗例
transferのところに、送り先であるユーザーBアドレスと、送りたい値( 2000 [oc])を入力してtransactします。
ここで、送り主のアドレスはグローバル変数msg.senderにより、現在の送信主アドレスを自動取得するため、特に入力はしません。

で、やってみましたが、うまくいかない……

画像にあるエラーメッセージを見てみると、「from」のところがなぜかユーザーBになっている。。
どうやら原因は、先ほどbalanceOfしたときの流れでやっていたので、このときに取得したユーザーBのmsg.senderの値が残っていたか、もしくはBrowser-SolidityのAccount欄アカウントを間違ってユーザーBにしたままtransferやってたせいでtransferのときに取得するmsg.senderの値がユーザーBになってしまったかで、残高 0 [c]のユーザーBから送金しようとしていました。
たぶん後者の、Account欄の問題かな……?

いずれにしろ、msg.senderでユーザーBのアドレスを取得してtransferしようとしたことが原因でした。
そして、まだOreOreCoinを持っていないユーザーBからの送金はできないと、エラーになりました。
成功例
原因がわかったので、Browser-SolidityのAccount欄をユーザーAにしつつ、念のためもう一度ユーザーAについてbalanceOfを実行してから、あらためてユーザーBへ 2000 [oc]送金してみたら、今度はうまく送金できました。



ユーザーA、ユーザーBそれぞれについて、送金後の残高をbalanceOfで確認してみます。
まずはユーザーA。

つづいてユーザーB。

ユーザーAからユーザーBへ、2000 [oc]がちゃんと送金できたことが確認できました。
まとめ
今回は、「はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)」の実践編にあるOreOreCoinという、簡単な仮想通貨のサンプルコードをデプロイして、状態確認と送金をやってみました。
テキストのサンプルコードをそのままコンパイルすると4種類の警告が出たので、それらの対処もしてみました。
送金はいい感じに教育的な内容で(笑)失敗しましたが、エラー内容をもとに対応してうまく解決できました。
警告とかエラーの対処が、やっぱり勉強になるなぁとあらためて実感しました。
ではでは今回はこのへんで。
Keyword : ブロックチェーンBrowser-SoliditySolidityEthereumイーサリアム仮想通貨