248-cryptozombies全攻略三









cryptozombies全攻略三








第一章.智能协议的永固性
我们看下这段代码
contract ZombieFeeding is ZombieFactory {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;

  KittyInterface kittyContract = KittyInterface(ckAddress);

  ......
}

这里的ckAddress是写死的,
那么如果CryptoKitties的合约的地址更改了(虽然几乎不可能发生)
那么我们的合约也完全错误了
所以我们把这里的ckAddress写成动态的
改成运行时再设定地址
contract ZombieFeeding is ZombieFactory {

  KittyInterface kittyContract;

  function setKittyContractAddress(address _address) external{
    kittyContract=KittyInterface(_address);
  }

  ......
}










第二章.ownable contracts
我们刚刚的方法setKittyContractAddress是external
所以是不安全的
所以我们需要制定合约的"所有权"

构造函数
function Ownable()是一个constructor构造函数
构造函数不是必须的,它与合约同名,
构造函数只有合约被创建的时候,才执行一次

函数修饰符
modifier onlyOwner()
修饰符和函数很类似
但是是用来修饰其他的已有函数的
在其他语句执行前,先检查一下条件

Ownable合约基本都会这样
1.创建合约,构造函数,将owner设置为msg.sender
2.加上一个修饰符onlyOwner,限制陌生人访问,将访问某些函数的权限锁定在owner上.
3.允许将合约所有权转让给他人

大多数人开发solidity DApp,都会复制Ownable
然后从它继承出子类,然后进行开发
现在我们需要把setKittyContractAddress限制为onlyOwner

现在我们让ZombieFactory继承Ownable
然后导入一下
import "./ownable.sol";

contract ZombieFactory is Ownable{
...
}









第三章.onlyOwner
我们已经继承了Ownable
然后用onlyOwner来修饰setKittyContractAddress方法
  function setKittyContractAddress(address _address) external onlyOwner{
    kittyContract = KittyInterface(_address);
  }











第四章.Gas
我们给僵尸添加2个新的属性
level 和 readyTime
    struct Zombie {
        string name;
        uint dna;
        uint32 level;
        uint32 readyTime;
    }











第五章.时间单位
我们添加一个冷却周期的设定
让僵尸两次攻击或捕猎之间必须等待1天
我们定义一个uint
uint cooldownTime = 1 days;

    function _createZombie(string _name, uint _dna) internal {
        uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime))) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }

现在,当我们创建Zombie并添加到zombies的时候
需要_name,_dna,1,uint32(now + cooldownTime)
1指的是level,等级
uint32(now + cooldownTime)指的是readyTime
也就是把readyTime设置为当前时间再加上1天.










第六章.冷却
我们创建2个函数
  function _triggerCooldown(Zombie storage _zombie) internal {
    _zombie.readyTime = uint32(now + cooldownTime);
  }

  function _isReady(Zombie storage _zombie) internal view returns (bool) {
      return (_zombie.readyTime <= now);
  }

先看_triggerCooldown,传入Zombie storage
这相当于传入引用,可以直接操作
代替了传递id,然后再去查找僵尸
然后设置zombie的readyTime为当前时间加上1天

再看isReady,也是传入Zombie storage
我们比较zombie的readyTime
如果小于等于当前时间
那么就是已经冷却完成了,返回true
反之则是还没有冷却完,还没有准备好,返回false
这个方法没有操作其他东西,只是获取
所以我们加上一个view













第七章.公有函数和安全性
我们需要对冷却时间进行检查

  function feedAndMultiply(uint _zombieId, uint _targetDna, string species) internal {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    require(_isReady(myZombie));
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(species) == keccak256("kitty")) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
    _triggerCooldown(myZombie);
  }

1.对用户进行检查
2.根据id获取zombie对象
3.检查冷却时间,如果为false,说明这个僵尸还未冷却
...
最后,创建新的zombie,
然后需要设置一下这个新zombie的冷却时间











第八章.函数修饰符
先来个例子
mapping (uint => uint) public age;

modifier olderThan(uint _age, uint _userId) {
  require(age[_userId] >= _age);
  _;
}

function driveCar(uint _userId) public olderThan(16, _userId) {
  //...
}

我们来看这段代码
olderThan是一个modifier函数修饰符
然后修饰了driveCar这个函数
那么就先执行olderThan里的代码,判断age
满足条件后,再执行driveCar中的代码

那么我们来写一下modifier
  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

参数传入_level等级,_zombieId
根据zombieId取出zombie对象,判断这个zombie的等级
是否大于等于参数_level
如果满足,则继续执行
如果不满足,则终止执行










第九章.修饰符
contract ZombieHelper is ZombieFeeding {

  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

  function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].name = _newName;
  }

  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].dna = _newDna;
  }

}
首先来一个modifier函数修饰符aboveLevel
这个函数修饰符的意思就是在某个等级之上
也就是如果在指定的等级之上,则继续执行
否则,就停止执行

然后来看changeName方法,修改名字
参数是zombieId,和newName 
用aboveLevel来修饰这个函数
所以,满足了大于2,才会继续执行
首先判断这个zombie的主人是不是该用户
然后修改名字

修改dna也是一样的道理
用aboveLevel修饰,
如果大于20,才会继续执行
判断主人
然后修改dna











第十章.利用'view'函数节省Gas
现在我们添加一个功能getZombiesByOwner
也就是查看这个用户的所有僵尸
实现这个功能只需要从区块链中读取数据
所以可以是一个view函数

view函数不花gas
因为view函数不会真正改变区块链上的任何数据,只是读取
所以用view来标记一个函数
就是告诉web3.js,
运行这个函数只需要查询本地以太坊节点
不需要在区块链上创建一个事务
事务需要运行在每个节点上,所以花费gas

现在创建getZombiesByOwner函数
  function getZombiesByOwner(address _owner) external view returns(uint[]) {

  }











第十一章.存储storage
solidity使用storage存储是非常昂贵的
写入的操作尤其贵
这是因为,
无论是写入还是更改一段数据,
都将永久性的写入区块链
所有的节点上都需要去存入这些数据,这是需要成本的

在内存中声明数组
在数组后面加上memory关键字
表明这个数组仅仅在内存中创建
不需要写入外部存储
而且在函数调用结束的时候,它就释放了

举个例子
function getArray() external pure returns(uint[]) {
  // 初始化一个长度为3的内存数组
  uint[] memory values = new uint[](3);
  // 赋值
  values.push(1);
  values.push(2);
  values.push(3);
  // 返回数组
  return values;
}
注意,内存数组必须用长度参数创建
目前不支持array.push()之类的方法调整数组大小

  function getZombiesByOwner(address _owner) external view returns(uint[]) {
    uint[] memory result = new uint[](ownerZombieCount[_owner]);
    return result;
  }

创建getZombiesByOwner方法,参数传入地址address,
external view来进行修饰
返回uint[] 数组

之前我们定义过一个mapping映射ownerZombieCount
我们根据key键_owner,来获取count
然后创建uint[]数组,用memory来修饰
uint[] memory result= new uint[]();











十二章.for循环
我们现在要实现getZombiesByOwner函数
一种简单的解决方案就是
在ZombieFactory中存入主人和僵尸数组的映射
也就是这样
mapping (address => uint[]) public ownerToZombies;

然后我们每次创建僵尸的时候,
就执行ownerToZombies[owner].push(zombieId)
然后getZombiesByOwner可以这样
function getZombiesByOwner(address _owner) external view returns(uint[]){
	return ownerToZombies[_owner];
}
也就是从ownerToZombies映射中
直接取出uint[]数组
也就是zombieId数组

但是这个做法是有问题的
每一次都需要做写的操作


使用for循环
举个例子先,创建一个偶数数组
function getEvens() pure external returns(uint[]) {
  uint[] memory evens = new uint[](5);
  // 在新数组中记录序列号
  uint counter = 0;
  // 在循环从1迭代到10:
  for (uint i = 1; i <= 10; i++) {
    // 如果 `i` 是偶数...
    if (i % 2 == 0) {
      // 把它加入偶数数组
      evens[counter] = i;
      //索引加一, 指向下一个空的‘even’
      counter++;
    }
  }
  return evens;
}

现在我们来重新写getZombiesByOwner函数
通过一个for循环遍历所有的僵尸
将用户地址address和僵尸的主人进行比较
把它们放到result数组里面

  function getZombiesByOwner(address _owner) external view returns(uint[]) {
    uint[] memory result = new uint[](ownerZombieCount[_owner]);
    uint counter = 0;
    
    for(uint i = 0; i < zombies.length; i++){
      if(zombieToOwner[i] == _owner){
        result[counter] = i;
        counter++;
      }
    }

    return result;
  }











 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值