252-cryptozombies全攻略五









cryptozombies全攻略五









第一章.以太坊代币
一个代币,在以太坊基本上就是遵循一些共同规则的智能合约
也就是它实现了所有其他代币共享的一组标准函数
例如
transfer(address _to, uint256 _value)
balanceOf(address _owner)

在智能合约内部,通常有一个映射
mapping(address => uint256) balances
用于追踪每个地址还有多少余额

所以基本上一个代币只是一个追踪
谁拥有多少该代币的合约
和一些可以让那些用户
将他们的代币转移到其他的地址的函数

pragma solidity ^0.4.19;

import "./zombieattack.sol";

contract ZombieOwnership is ZombieAttack {

}










第二章.ERC721标准,多重继承
在实现一个代币合约的时候,
我们要做的是将接口复制到它自己的solidity文件并导入
import "./erc721.sol"
然后,让我们的合约继承它
然后我们用一个函数定义来重写每个方法

pragma solidity ^0.4.19;

import "./zombieattack.sol";
import "./erc721.sol";

contract ZombieOwnership is ZombieAttack, ERC721 {

  function balanceOf(address _owner) public view returns (uint256 _balance) {
   
  }

  function ownerOf(uint256 _tokenId) public view returns (address _owner) {
   
  }

  function transfer(address _to, uint256 _tokenId) public {

  }

  function approve(address _to, uint256 _tokenId) public {

  }

  function takeOwnership(uint256 _tokenId) public {

  }
}











第三章.balanceOf和ownerOf
balanceOf
  function balanceOf(address _owner) public view returns (uint256 _balance) {
  }
传入一个address,然后返回这个address有多少个代币
在我们的例子中,我们的代币就是 僵尸

ownerOf
  function ownerOf(uint256 _tokenId) public view returns (address _owner) {
  }
传入一个代币id作为参数,
我们的例子中,就是一个僵尸id
然后返回这个代币的拥有者的address地址

在我们的DApp中已经有了一个mapping映射存储了这个信息
所以我们只用一行return语句来实现这个函数
  function balanceOf(address _owner) public view returns (uint256 _balance) {
    return ownerZombieCount[_owner];
  }

  function ownerOf(uint256 _tokenId) public view returns (address _owner) {
    return zombieToOwner[_tokenId];
  }










第四章.重构
我们需要把我们自己写的modifier,ownerOf修改成别的名字
不然跟ERC721里的ownerOf就重复了
  modifier onlyOwnerOf(uint _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    _;
  }











第五章.ERC721标准转移
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
第一种方法
代币的拥有者调用transfer方法,
传入他想转移到的address和他想转移的代币的_tokenId

第二种方法
代币拥有者首先调用approve,
然后该合约会存储谁被允许提取代币,
通常存储到一个mapping(uint256 => address)里面
然后,当有人调用takeOwnership时
合约会检查msg.sender是否得到拥有者的批准来提取代币
如果是,就将代币转移给他

transfer和takeOwnership都将包含相同的转移逻辑
只是以相反的顺序
一种是代币的发送者调用函数
一种是代币的接收者调用它

  function _transfer(address _from, address _to, uint256 _tokenId) private {
    ownerZombieCount[_to]++;
    ownerZombieCount[_from]--;
    zombieToOwner[_tokenId] = _to;
    Transfer(_from, _to, _tokenId);
  }

_to接收者数量++
_from发送者数量--
根据_tokenId找到用户地址address,改成接收者的地址_to
这样的话,这个僵尸_tokenId,就属于_to这个用户了
然后调用Transfer(_from, _to, _tokenId);











第六章.转移
  function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    _transfer(msg.sender, _to, _tokenId);
  }








第七章.批准
现在我们来实现approve
我们使用approve或者takeOwnership的时候
转移有2个步骤
1.作为所有者,用新主人的address和我们希望他获取的_tokenId来调用approve
2.新主人用_tokenId来调用takeOwnership,合约会检查确保他获得了批准,然后把代币转移给他

contract ZombieOwnership is ZombieAttack, ERC721 {

  mapping (uint => address) zombieApprovals;

  function balanceOf(address _owner) public view returns (uint256 _balance) {
    return ownerZombieCount[_owner];
  }

  function ownerOf(uint256 _tokenId) public view returns (address _owner) {
    return zombieToOwner[_tokenId];
  }

  function _transfer(address _from, address _to, uint256 _tokenId) private {
    ownerZombieCount[_to]++;
    ownerZombieCount[_from]--;
    zombieToOwner[_tokenId] = _to;
    Transfer(_from, _to, _tokenId);
  }

  function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    _transfer(msg.sender, _to, _tokenId);
  }

  function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    zombieApprovals[_tokenId] = _to;
    Approval(msg.sender, _to, _tokenId);
  }

  function takeOwnership(uint256 _tokenId) public {
    
  }
}

zombieApprovals映射,_tokenId对应一个address
approve方法,参数传入_to和_tokenId
首先,把zombieApprovals中_tokenId对应的地址改成_to
然后调用Approval,
from --  msg.sender
to   --  _to
tokenId  --  _tokenId











第八章.takeOwnership
takeOwnership,只是简单地检查以确保msg.sender已经被批准来提取这个代币
如果确认了,就执行_transfer

  function takeOwnership(uint256 _tokenId) public {
    require(zombieApprovals[_tokenId] == msg.sender);
    address owner = ownerOf(_tokenId);
    _transfer(owner, msg.sender, _tokenId);
  }

第一步,确定msg.sender也就是当前调用者,是否是_tokenId对应的approvals地址
第二步,根据ownerOf方法,找到_tokenId的主人
然后调用_transfer
from 	-- owner
to	-- msg.sender
_tokenId  --  _tokenId











第九章.预防溢出
什么是溢出
假设我们有一个uint8,只能存储8bit的数据
这意味着我们能存的最大数字就是 十进制255
uint8 number = 255;
number++;

如果我们这样加1了,那么number就等于0了
或者 下溢
uint8 number = 0;
number--;
这样的话,number就等于255了

使用SafeMath
为了防止这些情况,OpenZeppelin建立了一个叫做SafeMath的库
默认情况下可以防止这些问题

什么是库呢
一个库就是solidity中一种特殊的合约,
其中一个有用的功能就是给原始数据类型增加一些方法
比如SafeMath
我们可以使用 using SafeMath for uint256这样的语法
SafeMath有4个方法
add,sub,mul,div

代码:
using SafeMath for uint256;
uint256 a = 5;
uint256 b = a.add(3);
uint256 c = a.mul(2);










第十.SafeMath
把代码中的++和--修改成add和sub
  function _transfer(address _from, address _to, uint256 _tokenId) private {
    ownerZombieCount[_to] = ownerZombieCount[_to].add(1);
    ownerZombieCount[msg.sender] = ownerZombieCount[msg.sender].sub(1);
    zombieToOwner[_tokenId] = _to;
    Transfer(_from, _to, _tokenId);
  }











 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值