こんにちは。サトミです。 普段は、dapps開発、ブロックチェーンリサーチなどをしています。 ブロックチェーンしかTweetしないアカウントを最近作りました。 dapps、海外のブロックチェーンニュース...etc=>[@satomin_dev](https://twitter.com/satomin_dev)
本エントリは、CryptoZombiesの備忘録です。
CryptoZombies #1 はこちら
ゾンビが人間を襲う
![preview-zombie.png](https://qiita-image-store.s3.amazonaws.com/0/244700/ce6f1d53-90ac-76ec-f443-a842b999ab35.png)
レッスン2は、ゾンビに餌を与えていきます。
Mapping / Address
- Address
Addressは銀行の口座番号のようなもの。アカウントごとにユニークな値。
- Mapping
データの保管と参照のためのキーバリューストア。
mapping (address => uint) public accountBalance;
※ accountBalanceというデータ格納庫に、Keyがaddress
、Valueがuint
という見方。
pragma solidity ^0.4.19;
contract ZombieFactory {
event NewZombie(uint zombieId, string name, uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
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);
}
}
ここでは、以下のmappingを作成する。
zombieToOwner
:誰がどのゾンビを保有しているのか
ownerZombieCount
:誰が何体のゾンビを保有しているのか
Msg.sender
- Msg.sender
その関数を呼び出したユーザー(またはスマートコントラクト)の addressを参照できる。
pragma solidity ^0.4.19;
contract ZombieFactory {
event NewZombie(uint zombieId, string name, uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
////////////////////////////////////////////////////////
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
////////////////////////////////////////////////////////
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);
}
}
ここでは、ゾンビが作成されると、zombieToOwner
に作成者のaddressをゾンビのidに紐づけて格納。
その後、ownerZombieCount
を増やす。
Require
- Require
条件を満たさない場合はエラーを投げて実行を止める。
pragma solidity ^0.4.19;
contract ZombieFactory {
event NewZombie(uint zombieId, string name, uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
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 {
//該当箇所
require(ownerZombieCount[msg.sender] == 0);
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}
ゾンビを気軽に何体も作られないように、アドレスごとに1体のみという条件をつける。
継承 / Import
contract 子のコントラクト is 親のコントラクト {
}
親のコントラクトにアクセスできるようになる。
importも忘れずに。
import './親のコントラクト.sol'
Storage / Memory
- Storage
ブロックチェーンにデータを書き込む
- Memory
ブロックチェーンにデータを書き込まず、一時的な保存
Gasがかからない。
pragma solidity ^0.4.19;
import "./zombiefactory.sol";
contract ZombieFeeding is ZombieFactory {
function feedAndMultiply(uint _zombieId, uint _targetDna) public {
//該当箇所
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
}
}
ここでは、feedAndMultiply
関数内で、myZombie
というStorageに親のコントラクトで作成したzombies
配列を格納する。
Internal /External
- Internal
private
+ 継承したコントラクトにもアクセスできる。
- External
外部からのみ呼び出すことができる。
継承元であるZombieFactory
の_createZombie
関数はprivate
だったので、
feedAndMultiply
関数内でも呼び出せるようにInternal
に変更する。
ゾンビにCrypto Kittiesを捕食させる。
衝撃が走る。
- Interface
別のコントラクトとのやりとりする際に、定義する。
ここでは、Crypto KittiesのgetKitty
関数を取りに行く。
ckAddress
はCrypto Kittiesのコントラクトアドレス。
このckAddress
を使って、kittyContract
を初期化する。
pragma solidity ^0.4.19;
import "./zombiefactory.sol";
contract KittyInterface {
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
);
}
contract ZombieFeeding is ZombieFactory {
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
KittyInterface kittyContract = KittyInterface(ckAddress);
function feedAndMultiply(uint _zombieId, uint _targetDna) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
_createZombie("NoName", newDna);
}
}
捕食時に欲しい情報は、getKitty
関数内の10番目のuint256 genes
なので、
_kittyId
でkittyContract.getKitty
を呼び出し、genes
に格納。
最後にfeedAndMultiply
に'kitty'をパラメータとして設定する。
pragma solidity ^0.4.19;
import "./zombiefactory.sol";
contract KittyInterface {
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
);
}
contract ZombieFeeding is ZombieFactory {
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
KittyInterface kittyContract = KittyInterface(ckAddress);
// 該当箇所
function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
// ifで最後の2桁を99にして、kittyを判別。
if (keccak256(_species) == keccak256("kitty")) {
newDna = newDna - newDna % 100 + 99;
}
_createZombie("NoName", newDna);
}
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
// 該当箇所
feedAndMultiply(_zombieId, kittyDna, "kitty");
}
}