目录

​一、前言​

​二、批准Approve​

​1、讲解​

​2、实战1​

​1.要求​

​2.代码​

​3、实战2-takeOwnership​

​1.要求​

​2.代码​


一、前言

看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。

我们接着来讲ERC-721实战。

二、批准Approve

1、讲解

使用 ​​approve​​​ 或者 ​​takeOwnership​​ 的时候,转移有2个步骤:

1.所有者用新主人的 ​​address​​​ 和所有者希望新主人获取的 ​​_tokenId​​​ 来调用 ​​approve。​

2.新主人用 ​​_tokenId​​​ 来调用 ​​takeOwnership​​,合约会检查确保他获得了批准,然后把代币转移给他。

因为这发生在2个函数的调用中,所以在函数调用之间,我们需要一个数据结构来存储什么人被批准获取什么。 

2、实战1

1.要求

1.首先,让我们来定义一个映射 ​​zombieApprovals​​​。它应该将一个 ​​uint​​​ 映射到一个 ​​address​​。

这样一来,当有人用一个 ​​_tokenId​​​ 调用 ​​takeOwnership​​ 时,我们可以用这个映射来快速查找谁被批准获取那个代币。

​2.​​​在函数 ​​approve​​​ 上, 我们想要确保只有代币所有者可以批准某人来获取代币。所以我们需要添加修饰符 ​​onlyOwnerOf​​​ 到 ​​approve​​。

3.函数的正文部分,将 ​​_tokenId​​​ 的 ​​zombieApprovals​​​ 设置为和 ​​_to​​ 相等。

4.最后,在 ERC721 规范里有一个 ​​Approval​​​ 事件。所以我们应该在这个函数的最后触发这个事件。(参考 ​​erc721.sol​​​ 来确认传入的参数,并确保 ​​_owner​​​ 是 ​​msg.sender​​)

2.代码

pragma solidity >=0.5.0 <0.6.0;

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

contract ZombieOwnership is ZombieAttack, ERC721 {

mapping (uint => address) zombieApprovals;

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

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

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

function transferFrom(address _from, address _to, uint256 _tokenId) external payable {
require (zombieToOwner[_tokenId] == msg.sender || zombieApprovals[_tokenId] == msg.sender);
_transfer(_from, _to, _tokenId);
}

// 1. Add function modifier here
function approve(address _approved, uint256 _tokenId) external payable onlyOwnerOf(_tokenId){
// 2. Define function here
zombieApprovals[_tokenId] = _approved;
emit Approval(msg.sender, _approved, _tokenId);

}


}

3、实战2-takeOwnership

我们还有一个函数kakeOwnership,用于检查确保msg.sender已被批准来提取这个代币或僵尸,确认即调用_transfer:

1.要求

1.用一个 ​​require​​​ 句式来检查 ​​_tokenId​​​ 的 ​​zombieApprovals​​​ 和 ​​msg.sender​​​ 相等。这样如果 ​​msg.sender​​ 未被授权来提取这个代币,将抛出一个错误。

2.为了调用 ​​_transfer​​​,我们需要知道代币所有者的地址(它需要一个 ​​_from​​​ 来作为参数)。我们可以在我们的 ​​ownerOf​​ 函数中来找到这个参数。

所以,定义一个名为 ​​owner​​​ 的 ​​address​​​ 变量,并使其等于 ​​ownerOf(_tokenId)​​。

3.最后,调用 ​​_transfer​​​, 并传入所有必须的参数。(在这里你可以用 ​​msg.sender​​​ 作为 ​​_to​​, 因为代币正是要发送给调用这个函数的人)。

2.代码

pragma solidity >=0.5.0 <0.6.0;

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

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 {
// start here
require(zombieApprovals[_tokenId] == msg.sender);
address owner = ownerOf(_tokenId);
_transfer(owner, msg.sender, _tokenId);
}
}