Solidityを学ぶ際、CryptoZombiesというチュートリアルを利用すると習得が早いです。
この記事では一緒にCryptoZombies進めていきながらSolidityを学んでいきましょう。
複数回にわたって紹介していくつもりです。
目次
事前準備:CryptoZombiesの公式サイトへ
それではこちらへアクセスしてみましょう。
https://cryptozombies.io/jp/course/
「始めましょう。無料です。」をクリック
右上のSign Inしておきます。その後、1 ゾンビファクトリーの作成から順に進めていきます。
チャプター1は概要なので飛ばします。
チャプター 2: コントラクト
Solidityのソースコードは全て”version pragma”で始まり、contractの中にプログラムを書いていきます。
pragma solidity ^0.4.19; // 1. solidityのバージョンを入力 // 2. コントラクトを定義 contract ZombieFactory { }
チャプター 3: 状態変数と整数
Solidityの変数は指定なしで定義した場合、ブロックチェーンに書き込まれます。
uintというのは符号なし整数という型です。
その他にもこのような型が存在します。
string:文字列
address:アドレス(ウォレットのアドレス等)
bool:真偽(true or false)
pragma solidity ^0.4.19; contract ZombieFactory { // dnaDigitsを定義 uint dnaDigits = 16; }
チャプター 4: 数式演算
他のプログラム言語と同じように足し算引き算などの計算ができます。
加算(足し算): x + y
減算(引き算): x – y,
乗算(掛け算): x * y
除算(割り算): x / y
剰余(余り): x % y
指数演算子: x ** y(xのy乗)
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; // dnaModulusという名前の uintを作成し、10のdnaDigits乗に設定 uint dnaModulus = 10**dnaDigits; }
チャプター 5: 構造体
構造体の理解はやや難しいが、例えば人間のクローンを作るためのテンプレートをイメージすると良いかも。
人間のクローンをつくるときにあらかじめ必要な情報があるはず。例えば名前、DNA…とか。
…この例だとゾンビだけど。そして、構造体は変数で紹介したuintのような型としても使えます。(後ほど紹介)
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; // 1.Zombieという名前のstructを作成 struct Zombie { // 2.プロパティを2種類設定 name (string)、と、dna (uint) string name; uint dna; } }
チャプター 6: 配列
チャプター3で紹介した変数には、1つの情報しか入れることが出来ません。
x=1のような形ですね。
複数の情報を入れたい場合には配列が役に立ちます。
変数が1つの箱だとしたら、配列はその箱をに仕切りをつけて分けたイメージが近いですね。
チャプター5の構造体も配列にできる。配列の書き方は以下の通り。
型[] 変数名
型の右隣に[]をつけ、半角スペースを空けて変数名を書くと、その変数は配列になります。
※publicは一旦気にせずでOK。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } // zombiesというパブリックな配列を定義 Zombie[] public zombies; }
チャプター 7: 関数の宣言
実際にコントラクトを実装する際、必要となってくるのが関数です。
例えば、トークンを発行する等もfunctionとして記述する必要があります。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; // createZombieという関数を定義 引数としては_name (string)と、_dna (uint)をセット function createZombie(string _name, uint _dna) { } }
チャプター 8: 構造体と配列の操作
実際に構造体(テンプレート)に沿った配列をつくり、その中に情報を入れていきます。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function createZombie(string _name, uint _dna) { // zombiesという配列にpush(情報をつめる)する。 // Zombieの構造体(テンプレート)に沿って情報を入れる nameとdna // ちなみに引数には_アンダースコアを入れるのが通例 zombies.push(Zombie(_name, _dna)); } }
チャプター 9: Private / Public 関数
privateとpublicについて学ぶ。
例えばpublicを関数や変数につけると、異なるコントラクトからpublicが付いている関数・変数へアクセスすることができます。
privateの場合は同じコントラクト内でしかアクセスできません。
※何もつけなければpublic状態なのでご注意を。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; // createZombie関数をprivateに変更 function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } }
チャプター 10: さらに関数を続けるぞ
ここでは戻り値と修飾子について説明します。
戻り値というのは関数を実行したときにその答えを取得するためのものです。
例えば1+1のような足し算をする関数があったとすると、その答えが知りたいですよね。
その答えを戻したい(返したい)ので、戻り値または返り値といいます。
そして、修飾子に関しては読み取り専用のview と pureというものがあります。
違いとしては、viewはfunction外の変数を参照できる。pureはfunction外の変数は参照できない。
returns (戻り値の型) といった書き方をします。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } // _generateRandomDna関数を作成 // 引数にstring型の_str // 戻り値にuint型 // トランザクション(ブロックチェーンへの書き込み)が発生しない読み取り専用関数なのでviewをつける function _generateRandomDna(string _str) private view returns(uint) { } }
チャプター 11: Keccak256と型キャスト
このチャプターではKeccak256と型キャストについて学びます。
Keccak256は、ハッシュ化(ランダムな文字列に変換)する関数のこと。
Keccak256(値) でハッシュ化できる。
型キャストは型を変換する関数のこと。
例えばuint型をuint8型にしたいときにおこなう。
uint8(uint型の値)
こうするとuint型の値がuint8型に変換される。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } function _generateRandomDna(string _str) private view returns (uint) { // _strのkeccak256ハッシュを取得 // uintに型キャストして、 randというuintに格納 uint rand = uint(keccak256(_str)); // 上で求めた値のdnaModulusによる剰余(%)を return return rand % dnaModulus; } }
チャプター 12: 統合
ゾンビの名前やユーザーの名前をインプットできて、ランダムなDNAでゾンビを作れるpublic関数を作成するという課題が出されます。
今回までに作成した関数を使用するメイン関数を作っていきます。
pragma solidity ^0.4.19; contract ZombieFactory { uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { zombies.push(Zombie(_name, _dna)); } function _generateRandomDna(string _str) private view returns (uint) { uint rand = uint(keccak256(_str)); return rand % dnaModulus; } // createRandomZombieという名前の public 関数を作成 // _name (string)というパラメーターを設定 function createRandomZombie(string _name) public { // _nameで_generateRandomDnaを実行 // randDnaという名前で uintに格納 uint randDna = _generateRandomDna(_name); // _createZombie 関数を実行し、_nameとrandDnaを引数として渡す _createZombie(_name, randDna); } }
チャプター 13: イベント
イベントとはフロントエンドへ情報を伝達するためのもの。
例えばこの処理結果はこうだったとか、送金成功したよとかそんな感じのことを伝えます。
pragma solidity ^0.4.19; contract ZombieFactory { // NewZombieという名前のeventを宣言 // zombieId (uint)、 name (string)、 dna (uint)の値を渡す event NewZombie(uint zombieId , string name, uint dna); uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string _name, uint _dna) private { // array.push() - 1 がゾンビのI( 0,1,2...と付与される) uint id = zombies.push(Zombie(_name, _dna)) - 1; // _createZombie関数を編集し、zombies配列に新しいゾンビを追加したら NewZombieイベント発生 NewZombie(id, _name, _dna); } function _generateRandomDna(string _str) private view returns (uint) { uint rand = uint(keccak256(_str)); return rand % dnaModulus; } function createRandomZombie(string _name) public { uint randDna = _generateRandomDna(_name); _createZombie(_name, randDna); } }
チャプター 14: Web3.js
これまでのチャプターで作成したコントラクトに、フロントエンドからどうやってアクセスし実行するのか説明が載っている。
コメントが所々分かりづらいので、個人的な見解で書き換えます。
// コントラクトにアクセスする方法: var abi = /* buildフォルダ内>該当コントラクト名.json>abi内に記載されている内容を別途abi.jsファイルへ入れ読み込む */ var ZombieFactoryContract = web3.eth.contract(abi) var contractAddress = /* 実行するコントラクトのアドレスを入れる */ // ZombieFactoryをコントラクトのpublic関数とイベントにアクセスできるようにする。 var ZombieFactory = ZombieFactoryContract.at(contractAddress) // 入力テキストを取得する類のイベント $("#ourButton").click(function(e) { var name = $("#nameInput").val() // `createRandomZombie`関数を呼び出す ZombieFactory.createRandomZombie(name) }) // `NewZombie`イベントをリッスンしてUIを更新 var event = ZombieFactory.NewZombie(function(error, result) { if (error) return generateZombie(result.zombieId, result.name, result.dna) }) // ゾンビのdnaを取得して画像を更新 function generateZombie(id, name, dna) { let dnaStr = String(dna) // 16桁未満の場合はDNAの先頭に0をつける while (dnaStr.length < 16) dnaStr = "0" + dnaStr let zombieDetails = { // 最初の2桁は頭の部分。頭部は7種類用意してあるから、%7して // 0から6の番号を取得したら、そこに1を足して1から7にする // これを基にして、"head1.png" から"head7.png"までの // 画像ファイルを用意する部分だ: headChoice: dnaStr.substring(0, 2) % 7 + 1, // 次の2桁は目の部分だ。11種類用意してある eyeChoice: dnaStr.substring(2, 4) % 11 + 1, // シャツの部分は6種類用意してある: shirtChoice: dnaStr.substring(4, 6) % 6 + 1, // 最後の6桁は色の部分だ。 CSSのフィルタを使用して更新できる。 // 360度の色相回転(hue-rotate): skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360), eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360), clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360), zombieName: name, zombieDescription: "A Level 1 CryptoZombie", } return zombieDetails }
レッスン1は終わりです、お疲れ様でした!
次の講座はこちら。
Solidity入門基礎編【2】 - CryptoZombiesで学習 -