CryptoZombies #2 備忘録





<br/>
こんにちは。サトミです。
普段は、dapps開発、ブロックチェーンリサーチなどをしています。
ブロックチェーンしかTweetしないアカウントを最近作りました。
dapps、海外のブロックチェーンニュース...etc=>[@satomin_dev](https://twitter.com/satomin_dev)



本エントリは、CryptoZombiesの備忘録です。


#### CryptoZombies #1 は[こちら](https://qiita.com/SATOMIN/items/6b747a3cfc1be5f36a3b)



# ゾンビが人間を襲う

<br/>
![preview-zombie.png](https://qiita-image-store.s3.amazonaws.com/0/244700/ce6f1d53-90ac-76ec-f443-a842b999ab35.png)

レッスン２は、ゾンビに餌を与えていきます。





# 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");
    }

}
```


#### DONEです、お疲れ様でした。

#### 次回： 「Solidityの高度なコンセプト」



---


#### bportal

[![bportal_service@2x.png](https://qiita-image-store.s3.amazonaws.com/0/244700/3f0585a7-e151-1e16-30d7-2c8da4406a8a.png)](https://bportal.jp)