【CryptoZombiesを触ってみた】Lesson5で学んだこと
【CryptoZombiesとは】
Solidityでスマートコントラクトの構築を学習できるサイト
日本語で丁寧に説明してくれるので、プログラミングやったことない人でも学習できる。
【はじめに】
Solidityは未経験ですが、JavaScriptは触ったことがあります。
そんな人が自分用の学習メモとして書いています。
【学んだこと】
ERC20とERC721
ERC(Ethereum Request for Comments)はイーサリアムのトークン規格。ERC20はトークンの送金、残高照会、アドレス毎の残高記録など、トークンを発行するのに必要な機能について定められている。ERC20で作成されたトークン同士は互換性があるので連携しやすい(実装が容易になる)。ERC721は同じトークンでも、それぞれがユニークであると仮定され異なる特徴を持たせることができる。ERC721であれば、同じトークン(ゾンビ)でもレベルが異なれば別物として扱うことができる。
ERCについてはこちらの説明が分かりやすいです。
dappsmarket.net
複数継承
Solidityではカンマ区切りで複数の継承ができる。
contract newContract is Contract1, Contract2 { }
ERC721の実装
ERC721を継承し各関数をオーバーライドして使用する。
import "./erc721.sol"; // ERC721を継承 contract newContract is ERC721 { // balanceOf:addressを受け取り、そのaddressのトークン保有量を返す。 function balanceOf(address _owner) public view returns (uint256 _balance); // ownerOf:トークンIDを受け取り、その所有者のaddressを返す。 function ownerOf(uint256 _tokenId) public view returns (address _owner); // transfer:トークン所有者が送り先(address)、送るトークン(トークンID)を指定し所有権を移転する。 // 処理の最後にTransferイベントを呼び出す必要がある。 function transfer(address _to, uint256 _tokenId) public; // approve:トークン所有者が送り先(address)、送るトークン(トークンID)を指定し、コントラクトのmappingに記録する。 // transferと異なり、approveを実行しただけで所有権の移転はしない。takeOwnershipを実行した際に移転する。 // 処理の最後にApprovalイベントを呼び出す必要がある。 function approve(address _to, uint256 _tokenId) public; // takeOwnership:トークンの受取人が呼び出す。approveで記録したmappingから受取人のアドレスを検索し、該当のトークンの所有権を移転する。 function takeOwnership(uint256 _tokenId) public; }
オーバーフローとアンダーフロー
オーバーフローとは四則演算の結果が許容範囲を上回ること。
uint8 number = 255;
// uint8の最大値である255を上回るのでオーバーフローが発生し、結果は0となる。
number++;
アンダーフローとは四則演算の結果が許容範囲を下回ること。
uint8 number = 0;
// uint8の最小値である0を下回るのでアンダーフローが発生し、結果は255となる。
number--;
SafeMath
SafeMathはOpenZeppelinのライブラリ。OpenZeppelinについてはこちらの記事でも紹介しています。
SafeMathはadd(加算)、sub(減算)、mul(乗算)、div(除算)の4つの関数を持ち、オーバーフローやアンダーフローの脆弱性に対応している。SafeMathを使用して四則演算を行えばオーバーフローやアンダーフローが発生する演算を行う前にエラーで返してくれる。基本的に四則演算を行う場合はSafeMathを使用した方が安全。
library SafeMath { function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } }
SafeMathの使い方
// uint256にSafeMathの関数を追加する(使用できるようにする)。 using SafeMath for uint256; // aがSafeMathの関数の1つ目の引数として渡される。 uint256 a = 5; uint256 b = a.add(3); // 5 + 3 = 8 uint256 c = a.mul(2); // 5 * 2 = 10
※uint16やuint32の場合、ライブラリ内でuint256に変換されるので脆弱性に対応出来ていない。この場合、uint16やuint32用のSafeMathライブラリを作成する必要がある。
assert
偽(false)の場合、エラーを発生させる。関数呼び出しが失敗した場合、ユーザーにガスの残りを返却しない。
require
真(true)の場合、エラーを発生させる。関数呼び出しが失敗した場合、ユーザーにガスの残りを返却する。
コメント
JavaScriptに似ている
// 1行コメントの場合 /* 複数行 コメント の場合 */ // natspec /// @title contractのタイトル /// @author contractの作成者 /// @notice contractが何を行うのか説明 contract Math { /// @notice 関数が何を行うのか説明 /// @param x 1つ目の引数の説明 /// @param y 2つ目の引数の説明 /// @return z 戻り値の説明 /// @dev 開発者向けの詳細な説明 function multiply(uint x, uint y) returns (uint z) { z = x * y; } }
【できたもの】
ゾンビが4体に増えた。ゾンビの名前が変更できるようになった。