CryptoKitties: KittyBreedingのコントラクト

こんにちは、私はグウェン([https://twitter.com/gwenskiesHere](https://twitter.com/gwenskiesHere))です。今日はCryptoKittiesのKittyBreedingコントラクトをまとめます。

##KittyBreedingの変数

```javascript:KittyBreeding.sol
    event Pregnant(address owner, uint256 matronId, uint256 sireId, uint256 cooldownEndBlock);

    uint256 public autoBirthFee = 2 finney;

    uint256 public pregnantKitties;

    GeneScienceInterface public geneScience;
```
**Pregnant** event is triggeredon the pregnant function.
**autoBirthFee** variable used for gas cost for autoBirthFee.
**pregnantKitties** a counter variable to count number of kitties pregnant the owner possess.
**geneScience** this variable initializes the GeneScienceInterface.


##KittyBreedingの関数

####setGeneScienceAddress()
```javascript:KittyBreeding.sol
    function setGeneScienceAddress(address _address) external onlyCEO {
        GeneScienceInterface candidateContract = GeneScienceInterface(_address);

        require(candidateContract.isGeneScience());

        geneScience = candidateContract;
    }
```
This function is only for accounts with CEO access, this is use when the kitties gave birth to set the token for the new born kitties.
#####parameter:
address _address: user token id

####_isReadyToBreed()
```javascript:KittyBreeding.sol
    function _isReadyToBreed(Kitty _kit) internal view returns (bool) {
        return (_kit.siringWithId == 0) && (_kit.cooldownEndBlock <= uint64(block.number));
    }
```
This function is checks if the kitties are ready to breed.
#####parameter:
Kitty _kit: object of kitty to be breed.
#####return:
bool true or false

####_isSiringPermitted()
```javascript:KittyBreeding.sol
    function _isSiringPermitted(uint256 _sireId, uint256 _matronId) internal view returns (bool) {
        address matronOwner = kittyIndexToOwner[_matronId];
        address sireOwner = kittyIndexToOwner[_sireId];

        return (matronOwner == sireOwner || sireAllowedToAddress[_sireId] == matronOwner);
    }
```
This function is to get permission of the sire can be breed the matron.
#####parameter:
uint256 _sireId: sire token id
uint256 _matronId: matron token id
#####return:
bool true or false

####_triggerCooldown()
```javascript:KittyBreeding.sol
    function _triggerCooldown(Kitty storage _kitten) internal {
        _kitten.cooldownEndBlock = uint64((cooldowns[_kitten.cooldownIndex]/secondsPerBlock) + block.number);

        if (_kitten.cooldownIndex < 13) {
            _kitten.cooldownIndex += 1;
        }
    }
```
This function is trigger if the kitties are ready to breed again.
#####parameter:
Kitty storage _kitten: object of the kitty

####approveSiring()
```javascript:KittyBreeding.sol
function approveSiring(address _addr, uint256 _sireId) external whenNotPaused {
    require(_owns(msg.sender, _sireId));
    sireAllowedToAddress[_sireId] = _addr;
}
```
This function is use to approve request for the sire to breed the matron.
#####parameter:
address _addr: token of the user who owns the sire.
uint256 _sireId: sire token id.

####setAutoBirthFee()
```javascript:KittyBreeding.sol
function setAutoBirthFee(uint256 val) external onlyCOO {
    autoBirthFee = val;
}
```
This function can set the auto birth fee if the user has a control access COO.
#####parameter:
uint256 val: value of the birth fee or gas price.

####_isReadyToGiveBirth()
```javascript:KittyBreeding.sol
function _isReadyToGiveBirth(Kitty _matron) private view returns (bool) {
    return (_matron.siringWithId != 0) && (_matron.cooldownEndBlock <= uint64(block.number));
}
```
This function checks if the matron has a sire and check if the cooldown to give birth ends. This function will also be use in isReadyToGiveBirth().
#####parameter:
Kitty _matron: object of the matron kitty.
#####return:
bool true or false

####isReadyToBreed()
```javascript:KittyBreeding.sol
function isReadyToBreed(uint256 _kittyId) public view returns (bool) {
        require(_kittyId > 0);
        Kitty storage kit = kitties[_kittyId];
        return _isReadyToBreed(kit);
}
```
This function checks if the matron is ready to breed. 
#####parameter:
uint256 _kittyId: id of the kitty to be breed.
#####return:
bool true or false

####isPregnant()
```javascript:KittyBreeding.sol
    function isPregnant(uint256 _kittyId) public view returns (bool) {
        require(_kittyId > 0);
        return kitties[_kittyId].siringWithId != 0;
    }
```
This function check if the kitty is pregnant.
#####parameter:
uint256 _kittyId: id of the kitty to be breed.
#####return:
bool true or false

####_isValidMatingPair()
```javascript:KittyBreeding.sol
    function _isValidMatingPair(Kitty storage _matron, uint256 _matronId, Kitty storage _sire, uint256 _sireId) private view returns(bool) {
        if (_matronId == _sireId) {
            return false;
        }

        if (_matron.matronId == _sireId || _matron.sireId == _sireId) {
            return false;
        }
        if (_sire.matronId == _matronId || _sire.sireId == _matronId) {
            return false;
        }

        if (_sire.matronId == 0 || _matron.matronId == 0) {
            return true;
        }

        if (_sire.matronId == _matron.matronId || _sire.matronId == _matron.sireId) {
            return false;
        }
        if (_sire.sireId == _matron.matronId || _sire.sireId == _matron.sireId) {
            return false;
        }
        return true;
    }
```
This function check if the kitty is breed to itself, to the parent, or to its siblings.
#####parameter:
Kitty storage _matron: object of the matron kitty.
uint256 _matronId: id of the matron kitty.
Kitty storage _sire: object of the sire kitty.
uint256 _sireId: id of the sirekitty.
#####return:
bool true or false

####_canBreedWithViaAuction()
```javascript:KittyBreeding.sol
     function _canBreedWithViaAuction(uint256 _matronId, uint256 _sireId) internal view returns (bool) {
        Kitty storage matron = kitties[_matronId];
        Kitty storage sire = kitties[_sireId];
        return _isValidMatingPair(matron, _matronId, sire, _sireId);
    }
```
This function check if the kitties in auction can be breed.
#####parameter:
uint256 _matronId: id of the matron kitty.
uint256 _sireId: id of the sirekitty.
#####return:
bool true or false

####canBreedWith()
```javascript:KittyBreeding.sol
function canBreedWith(uint256 _matronId, uint256 _sireId) external view returns(bool) {
        require(_matronId > 0);
        require(_sireId > 0);
        Kitty storage matron = kitties[_matronId];
        Kitty storage sire = kitties[_sireId];
        return _isValidMatingPair(matron, _matronId, sire, _sireId) &&
            _isSiringPermitted(_sireId, _matronId);
}
```
This function checks to see if two cats can breed together, including checks for ownership and siring approvals. Does NOT check that both cats are ready for breeding.
#####parameter:
uint256 _matronId: id of the matron kitty.
uint256 _sireId: id of the sirekitty.
#####return:
bool true or false

####_breedWith()
```javascript:KittyBreeding.sol
function _breedWith(uint256 _matronId, uint256 _sireId) internal {

        Kitty storage sire = kitties[_sireId];
        Kitty storage matron = kitties[_matronId];

        matron.siringWithId = uint32(_sireId);

        _triggerCooldown(sire);
        _triggerCooldown(matron);

        delete sireAllowedToAddress[_matronId];
        delete sireAllowedToAddress[_sireId];

        pregnantKitties++;

        Pregnant(kittyIndexToOwner[_matronId], _matronId, _sireId, matron.cooldownEndBlock);
}
```
This function is to initiate breeding, assumes that all breeding requirements have been checked.
#####parameter:
uint256 _matronId: id of the matron kitty.
uint256 _sireId: id of the sirekitty.

####breedWithAuto()
```javascript:KittyBreeding.sol
function breedWithAuto(uint256 _matronId, uint256 _sireId) external payable whenNotPaused {
        require(msg.value >= autoBirthFee);
        require(_owns(msg.sender, _matronId));
        require(_isSiringPermitted(_sireId, _matronId));
        Kitty storage matron = kitties[_matronId];
        require(_isReadyToBreed(matron));
        Kitty storage sire = kitties[_sireId];
        require(_isReadyToBreed(sire));
        require(_isValidMatingPair(
            matron,
            _matronId,
            sire,
            _sireId
        ));
        _breedWith(_matronId, _sireId);
    }
```
This function to breed a Kitty you own (as matron) with a sire that you own, or for which you have previously been given Siring approval. Will either make your cat pregnant, or will fail entirely. Requires a pre-payment of the fee given out to the first caller of giveBirth().
#####parameter:
uint256 _matronId: id of the matron kitty.
uint256 _sireId: id of the sirekitty.

####giveBirth()
```javascript:KittyBreeding.sol
function giveBirth(uint256 _matronId) external whenNotPaused returns(uint256) {
        Kitty storage matron = kitties[_matronId];
        require(matron.birthTime != 0);
        require(_isReadyToGiveBirth(matron));

        uint256 sireId = matron.siringWithId;
        Kitty storage sire = kitties[sireId];
        uint16 parentGen = matron.generation;

        if (sire.generation > matron.generation) {
            parentGen = sire.generation;
        }

        uint256 childGenes = geneScience.mixGenes(matron.genes, sire.genes, matron.cooldownEndBlock - 1);

        address owner = kittyIndexToOwner[_matronId];
        uint256 kittenId = _createKitty(_matronId, matron.siringWithId, parentGen + 1, childGenes, owner);

        delete matron.siringWithId;

        pregnantKitties--;

        msg.sender.send(autoBirthFee);

        return kittenId;
    }
```
This function looks at a given Kitty and, if pregnant and if the gestation period has passed, combines the genes of the two parents to create a new kitten. The new Kitty is assigned to the current owner of the matron. Upon successful completion, both the matron and the new kitten will be ready to breed again. Note that anyone can call this function (if they are willing to pay the gas!), but the new kitten always goes to the mother's owner.
#####parameter:
uint256 _matronId: id of the matron kitty.
#####return:
uint256 kitten id

##コード
KittyBreeding is a contract use to manage Kitty siring, gestation, and birth.

```javascript:KittyBreeding.sol
contract KittyBreeding is KittyOwnership {
    event Pregnant(address owner, uint256 matronId, uint256 sireId, uint256 cooldownEndBlock);

    uint256 public pregnantKitties;

    function setGeneScienceAddress(address _address) external onlyCEO {
        GeneScienceInterface candidateContract = GeneScienceInterface(_address);

        require(candidateContract.isGeneScience());

        geneScience = candidateContract;
    }

    function _isReadyToBreed(Kitty _kit) internal view returns (bool) {
        return (_kit.siringWithId == 0) && (_kit.cooldownEndBlock <= uint64(block.number));
    }

    function _isSiringPermitted(uint256 _sireId, uint256 _matronId) internal view returns (bool) {
        address matronOwner = kittyIndexToOwner[_matronId];
        address sireOwner = kittyIndexToOwner[_sireId];

        return (matronOwner == sireOwner || sireAllowedToAddress[_sireId] == matronOwner);
    }

    function _triggerCooldown(Kitty storage _kitten) internal {
        _kitten.cooldownEndBlock = uint64((cooldowns[_kitten.cooldownIndex]/secondsPerBlock) + block.number);

        if (_kitten.cooldownIndex < 13) {
            _kitten.cooldownIndex += 1;
        }
    }

    function approveSiring(address _addr, uint256 _sireId)
        external
        whenNotPaused
    {
        require(_owns(msg.sender, _sireId));
        sireAllowedToAddress[_sireId] = _addr;
    }

    function setAutoBirthFee(uint256 val) external onlyCOO {
        autoBirthFee = val;
    }

    function _isReadyToGiveBirth(Kitty _matron) private view returns (bool) {
        return (_matron.siringWithId != 0) && (_matron.cooldownEndBlock <= uint64(block.number));
    }

    function isReadyToBreed(uint256 _kittyId) public view returns (bool) {
        require(_kittyId > 0);
        Kitty storage kit = kitties[_kittyId];
        return _isReadyToBreed(kit);
    }

    function isPregnant(uint256 _kittyId) public view returns (bool) {
        require(_kittyId > 0);
        return kitties[_kittyId].siringWithId != 0;
    }

    function _isValidMatingPair(Kitty storage _matron, uint256 _matronId, Kitty storage _sire, uint256 _sireId) private view returns(bool) {
        if (_matronId == _sireId) {
            return false;
        }

        if (_matron.matronId == _sireId || _matron.sireId == _sireId) {
            return false;
        }
        if (_sire.matronId == _matronId || _sire.sireId == _matronId) {
            return false;
        }

        if (_sire.matronId == 0 || _matron.matronId == 0) {
            return true;
        }

        if (_sire.matronId == _matron.matronId || _sire.matronId == _matron.sireId) {
            return false;
        }
        if (_sire.sireId == _matron.matronId || _sire.sireId == _matron.sireId) {
            return false;
        }

        return true;
    }

    function _canBreedWithViaAuction(uint256 _matronId, uint256 _sireId)
        internal
        view
        returns (bool)
    {
        Kitty storage matron = kitties[_matronId];
        Kitty storage sire = kitties[_sireId];
        return _isValidMatingPair(matron, _matronId, sire, _sireId);
    }

    function canBreedWith(uint256 _matronId, uint256 _sireId) external view returns(bool)
    {
        require(_matronId > 0);
        require(_sireId > 0);
        Kitty storage matron = kitties[_matronId];
        Kitty storage sire = kitties[_sireId];
        return _isValidMatingPair(matron, _matronId, sire, _sireId) &&
            _isSiringPermitted(_sireId, _matronId);
    }

    function _breedWith(uint256 _matronId, uint256 _sireId) internal {
        Kitty storage sire = kitties[_sireId];
        Kitty storage matron = kitties[_matronId];

        matron.siringWithId = uint32(_sireId);

        _triggerCooldown(sire);
        _triggerCooldown(matron);

        delete sireAllowedToAddress[_matronId];
        delete sireAllowedToAddress[_sireId];

        pregnantKitties++;

        Pregnant(kittyIndexToOwner[_matronId], _matronId, _sireId, matron.cooldownEndBlock);
    }

    function breedWithAuto(uint256 _matronId, uint256 _sireId) external payable  whenNotPaused {
        
        require(msg.value >= autoBirthFee);

        require(_owns(msg.sender, _matronId));

        require(_isSiringPermitted(_sireId, _matronId));

        Kitty storage matron = kitties[_matronId];

        require(_isReadyToBreed(matron));

        Kitty storage sire = kitties[_sireId];

        require(_isReadyToBreed(sire));

        require(_isValidMatingPair(matron, _matronId, sire, _sireId));

        _breedWith(_matronId, _sireId);
    }

    function giveBirth(uint256 _matronId) external whenNotPaused returns(uint256) {
 
        Kitty storage matron = kitties[_matronId];

        require(matron.birthTime != 0);

        require(_isReadyToGiveBirth(matron));

        uint256 sireId = matron.siringWithId;
        Kitty storage sire = kitties[sireId];

        uint16 parentGen = matron.generation;
        if (sire.generation > matron.generation) {
            parentGen = sire.generation;
        }

        uint256 childGenes = geneScience.mixGenes(matron.genes, sire.genes, matron.cooldownEndBlock - 1);

        address owner = kittyIndexToOwner[_matronId];
        uint256 kittenId = _createKitty(_matronId, matron.siringWithId, parentGen + 1, childGenes, owner);

        delete matron.siringWithId;

        pregnantKitties--;

        msg.sender.send(autoBirthFee);

        return kittenId;
    }
}
```
to see the whole code of CryptoKitties: [https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code](https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code)
want to try CryptoKitties: [https://www.cryptokitties.co/](https://www.cryptokitties.co/)

##結論
CryptoKittiesは、ブロックチェーンとERC721を学ぶ際に役立ちます。CryptoCurrencyのKittyBreedingについてたくさん学ぶことを願っています。
ありがとうございました。