cryptozombies全攻略二
第二章.映射mapping和地址address
我们现在要存储僵尸的所有权
就是说
每个僵尸都属于一个用户
一个用户有多个僵尸
所以我们要用映射来反应这个关系
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
第一个mapping的key是uint,也就是僵尸的id
value是address,也就是用户的地址
第二个mapping的key是address,用户地址
value是uint,是用户拥有的僵尸的数量
第三章.msg.sender
solidity中,有一些全局变量,可以被所有函数调用
msg.sender获取的是当前调用者的address
我们现在修改_createZombie方法,
把僵尸分配给调用这个函数的用户
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);
}
zombieToOwner的key为id,value是地址
ownerZombieCount取出key为msg.sender的int,然后加1,
也就是数量count+1
第四章.require
require的功能是
当require中的条件不满足的时候
就抛出错误,并且停止执行
现在我们修改一下createRandomZombie方法
如果ownerZombieCount[msg.sender]的值为0,才执行
否则就停止执行
也就是说,执行这个方法的时候
看一下这个地址的count,
也就是这个用户拥有的僵尸数量
如果不为0,就停止,
没有僵尸的情况下,才可以创建僵尸
require(ownerZombieCount[msg.sender]==0)
看下完整代码
function createRandomZombie(string _name) public {
require(ownerZombieCount[msg.sender]==0);
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
第五章.继承
一个合约Son,继承另一个合约Father
那么子合约就可以调用父合约中的方法
contract ZombieFeeding is ZombieFactory{
}
第六章.引入
我们刚刚是把ZombieFeeding这个子合约和父合约写在同一个文件中
如果代码很多,会比较冗长
那么我们可以把这个子合约单独拿出来放在另一文件中
比如
刚刚那个父合约ZombieFactory,我们就放在一个叫
zombiefactory.sol的文件中
然后我们再新建一个文件
叫做zombiefeeding.sol
这个文件就只放ZombieFeeding这个合约
然后我们需要导入一下zombiefactory.sol这个文件
pragma solidity ^0.4.19;
import "./zombiefactory.sol";
contract ZombieFeeding is ZombieFactory {
}
第七章.storage和memory
Storage变量是指永久存储在区块链中的变量
Memory变量则是临时的
当外部函数对某合约调用完成时,内存型变量即被移除
function feedAndMultiply(uint _zombieId, uint _targetDna) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
}
首先用require方法判断msg.sender
根据_zombieId,从zombieToOwner这个mapping中取出address
然后比较msg.sender与address
如果相同,
那么说明当前调用这个方法的用户,是这个僵尸的主人
否则就停止执行
第八章.结合
刚刚我们判断了地址,
然后根据id把从zombies里取出zombie对象
然后现在
我们要根据参数_targetDna和zombie对象的dna
生成一个新的dna
这个新的dna就是新的僵尸的dna
function feedAndMultiply(uint _zombieId, uint _targetDna) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
_targetDna = _targetDna % dnaModulus;
uint newDna = (_targetDna + myZombie.dna) /2;
_createZombie("NoName", newDna);
}
这里我们先将_targetDna取余dnaModulus得到一个标准的16位的dna
然后将_targetDna与原僵尸的dna进行取平均值
然后得到一个新的dna
然后我们根据这个新的dna
生成一个新的僵尸
第九章.函数可见性
我们刚刚写了feedAndMultiply函数
在这个函数中调用了_createZombie方法
但是我们发现,
function _createZombie(string _name, uint _dna) private
这个函数是private函数
所以实际上我们不能成功调用这个函数
但是我们不能修改成public,因为那样所有人都可以调用createZombie
那么我们应该怎么做
那么除了private和public
还有2个描述函数可见性的修饰词,
就是internal和external
internal和private类似,
但是,子合约可以调用父合约的internal方法
external和public类似,
但是external函数只能在合约之外调用
也就是不能被合约内的其他函数调用
所以这边我们把private改成internal
function _createZombie(string _name, uint _dna) internal {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
NewZombie(id, _name, _dna);
}
第十章.合约交互
如果我们的合约需要和区块链上的其他合约进行交互
我们需要interface接口
举个例子,假设区块链上有这样一个合约
contract LuckyNumber {
mapping(address => uint) numbers;
function setNum(uint _num) public {
numbers[msg.sender] = _num;
}
function getNum(address _myAddress) public view returns (uint) {
return numbers[_myAddress];
}
}
然后如果我们的合约需要调用这个LuckyNumber合约里的方法
我们需要定义这个合约的interface接口
contract NumberInterface{
function getNum(address _myAddress) public view returns(uint);
}
现在,我们尝试一下与区块链上的CryptoKitties这个应用进行交互
CryptoKitties应用里有这样的一个函数getKitty
就是获取一只加密猫CryptoKitty的数据
function getKitty(uint256 _id) external view returns(...){}
我们可以看到,
就是传入一只kitty的id,
然后获取这只kitty的数据
现在我们来试一下
先来写接口
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
);
}
第十一章.使用接口
刚刚我们定义好了接口KittyInterface
现在我们来用一下
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
KittyInterface kittyContract = KittyInterface(ckAddress);
ckAddress就是CryptoKitties的合约的地址
然后我们传入到KittyInterface中就可以了
第十二章.处理多返回值
getKitty方法是多返回值,一共返回了10个值
那么我们要进行接收
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
feedAndMultiply(_zombieId, kittyDna);
}
我们定义一个uint,kittyDna,来接收kitty的uint256 genes
也就是kitty这个应用也有一个genes属性,来表示kitty的基因
前面的返回值我们都不要,直接用,逗号忽略
然后只接收最后一个返回值,然后指向kittyDna
然后我们用zombieId和kittyDna来调用feedAndMultiply
第十三章.种族
我们修改一下dna,
如果是kitty类型的僵尸
那么dna最后两位就是99
if (keccak256(_species) == keccak256("kitty")) {
newDna = newDna - newDna % 100 + 99;
}
第二大章完成了
看下完整代码
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 (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");
}
}