こんにちは。サトミです。 普段は、dapps開発、ブロックチェーンリサーチなどをしています。 ブロックチェーンしかTweetしないアカウントを最近作りました。 dapps、海外のブロックチェーンニュース...etc=>[@satomin_dev](https://twitter.com/satomin_dev)
本エントリは、CryptoZombiesの備忘録です。
CryptoZombies #1 はこちら
ゾンビが人間を襲う

レッスン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");
    }
}

