0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CryptoZombies #2 備忘録

Posted at

こんにちは。サトミです。 普段は、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なので、
_kittyIdkittyContract.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

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?