Solidity(五)合约标准ERC-721

以太币、比特币这样的代币均属于【同质化代币】;即每个代币都是一样的;没有什么区别、均可以用于交换和转账;然而在现实生活中存在着很多物品是非同质化的;

比如:名画、艺术品、古董等;因为每一件物品都是不相同的;因此以太坊提出ERC-721代币标准;用于抽象和描述飞同质化的实物;

【非同质化代币】的特点:

  • 独特性:唯一、比如:艺术品、游戏道具、房产
  • 所有权:每个代币都有一个所有权;可用于交易和转移
  • 可编程性:可使用智能合约根据应用场景创建各种飞同质化的应用和场景
  • 元数据:每个ERC-721都可以关联元数据、用于描述代币的属性和特征、通常以URI的形式存储

1 IERC-165接口定义介绍

ERC-165合约:标准接口监控协议;用于发布和检测智能合约实现了那些接口;在IERC-165接口合约中实现了ERC-165的逻辑描述、接口合约只有一个函数;下面介绍的ERC-721依赖于ERC-165合约;

1
2
3
4
5
6
7
8
9
// author:hqd8080
// contracts/IERC165.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2< 0.9.0;

interface IERC165 {
//检测是否实现了某个接口的函数
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

2 IERC-721接口定义介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// author:hqd8080
// contracts/IERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2< 0.9.0;

//ERC-721依赖于ERC-165
interface IERC165 {
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

//安全转账接口
interface ERC721TokenReceiver {
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes memory _data) external returns(bytes4);
}

//IERC-721接口定义
interface IERC721 is IERC165 {

event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); //转账事件
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); //授权事件
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); //批量授权事件

//余额查询
function balanceOf(address _owner) external view returns (uint256);
//查询某个飞同质化代币的所有者
function ownerOf(uint256 _tokenId) external view returns (address);
//安全转账函数
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) external payable;
//安全转账函数重载
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
//转移飞同质化代币的所有权
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
//授权操作
function approve(address _approved, uint256 _tokenId) external payable;
//批量授权
function setApprovalForAll(address _operator, bool _approved) external;
//获取某个非同质化代币的授权信息
function getApproved(uint256 _tokenId) external view returns (address);
//判断是否已经对某个地址授权
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

另外两个可选接口:

  • IERC721Metadata: 定义查询元数据的相关信息
  • IERC721Enumerale:定义查询代币的唯一信息标识

3 实现ERC-721接口合约程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// author:hqd8080
// contracts/ERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2< 0.9.0;

import "./IERC721.sol";

//ERC-721合约,实现IERC-721接口函数
contract ERC721 is IERC721 {

mapping (bytes4 => bool) supportsInterfaces;
bytes4 invalID = 0xffffffff;
bytes4 constant ERC165ID = 0x01ffc9a7;
bytes4 constant ERC721ID = 0x80ac58cd;

mapping (address => uint256) ercTokenCount;
mapping (uint256 => address) ercTokenOwner;
mapping (uint256 => address) ercTokenApproved;
mapping (address => mapping (address => bool)) ercOperatorForAll;

address private owner;

constructor() {
_registerInterface(ERC165ID);
_registerInterface(ERC721ID);

owner = msg.sender;
}

//授权
modifier canOperator(uint256 _tokenId) {
address _owner = ercTokenOwner[_tokenId];
require(msg.sender == _owner || ercOperatorForAll[owner][msg.sender], "ERC721: permission denied");
_;
}

//转账
modifier canTransfer(uint256 _tokenId, address _from) {
address _owner = ercTokenOwner[_tokenId];
require(_owner == _from, "ERC721: token owner is not _from");
require(msg.sender == owner || ercTokenApproved[_tokenId] == msg.sender || ercOperatorForAll[owner][msg.sender], "ERC721: permission denied");
_;
}

//铸造代币,新定义的函数不属于IERC-721接口
function mint(address _to, uint256 _tokenId, bytes memory data) external {
require(msg.sender == owner, "ERC721: caller is not owner");
require(_to != address(0), "ERC721: to is a zero address");
require(ercTokenOwner[_tokenId] == address(0), "ERC721: _tokenId. already exists");

ercTokenOwner[_tokenId] = _to;
ercTokenCount[_to] += 1;

bytes4 result = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _to, _tokenId, data);
require(result == ERC721TokenReceiver.onERC721Received.selector, "ERC721: result not equal onERC721Received interfaceID");

emit Transfer(address(0), _to, _tokenId);
}

//注册接口
function _registerInterface(bytes4 interfaceID) internal {
supportsInterfaces[interfaceID] = true;
}

//ERC-156函数
function supportsInterface(bytes4 interfaceID) override external view returns (bool) {
require(interfaceID != invalID, "ERC721: invalid interfaceID");
return supportsInterfaces[interfaceID];
}

//ERC-721部分函数
function balanceOf(address _owner) override external view returns (uint256) {
return ercTokenCount[_owner];
}

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

function approve(address _approved, uint256 _tokenId) override canOperator(_tokenId) external payable {
ercTokenApproved[_tokenId] = _approved;

emit Approval(msg.sender, _approved, _tokenId);
}

function setApprovalForAll(address _operator, bool _approved) override external {
ercOperatorForAll[msg.sender][_operator] = _approved;

emit ApprovalForAll(msg.sender, _operator, _approved);
}

function getApproved(uint256 _tokenId) override external view returns (address) {
return ercTokenApproved[_tokenId];
}

function isApprovedForAll(address _owner, address _operator) override external view returns (bool) {
return ercOperatorForAll[_owner][_operator];
}

function transferFrom(address _from, address _to, uint256 _tokenId) override external payable {
_transferFrom(_from, _to, _tokenId);
}

function _transferFrom(address _from, address _to, uint256 _tokenId) internal canTransfer(_tokenId, _from) {
ercTokenOwner[_tokenId] = _to;
ercTokenCount[_from] -= 1;
ercTokenCount[_to] += 1;
//取消授权
ercTokenApproved[_tokenId] = address(0);

emit Transfer(_from, _to, _tokenId);
}

function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) override external payable {
_safeTransferFrom(_from, _to, _tokenId, data);
}

function safeTransferFrom(address _from, address _to, uint256 _tokenId) override external payable {
_safeTransferFrom(_from, _to, _tokenId, "");
}

function _safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) internal {
_transferFrom(_from, _to, _tokenId);

bytes4 result = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, data);
require(result == ERC721TokenReceiver.onERC721Received.selector, "ERC721: result not equal onERC721Received interfaceID");
}
}