Solidity(六)合约标准ERC-1155

以太坊已经存在了ERC-20和ERC-721两种代币标准;分别用于处理【同质化代币】和【非同质化代币】;然而这两种标准仍然有一些局限性,无法满足复杂的代币经济系统的需求;

例如:在游戏中,可能需要同时管理多种类型的代币,包括同质化代币和非同质化代币;
为了解决这个复杂问题,以太坊2018年退出ERC-1155代币标准、目标是创建一个灵活且高效的代币标准;可以同时管理多种类型的代币;

1 IERC-1155代币接口定义介绍

ERC-1155协议同样需要依赖ERC-165协议;通过IERC-165接口合约实现ERC-1155协议的标准;IERC-1155接口的定义如下:

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
// author:hqd8080
// contracts/IERC1155.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2< 0.9.0;

//IERC-1155接口定义、包含4个时间和6个函数
interface IERC1155 {
//单类代币转账事件
event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value);
//批量转账事件
event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values);
//批量授权事件
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
//_id 种类代币的 URI 发送变化时触发的事件 _value为新的URI
event URI(string _value, uint256 indexed _id);

//安全转账函数
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
//批量安全转账函数
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;
//持仓查询函数,查询 _owner 所拥有的 _id 种类的代币持有量
function balanceOf(address _owner, uint256 _id) external view returns (uint256);
//批量持仓查询函数
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);
//批量授权函数
function setApprovalForAll(address _operator, bool _approved) external;
//批量授权查询函数
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

2 IERC-1155MetadataURI接口

对于每种代币的URI元数据,通过【IERC-1155MetadataURI】接口来定义,该接口的定义如下:

1
2
3
4
5
//IERC1155MetadataURI、对于每种代币的URI元数据
interface IERC1155MetadataURI {
//返回第 _id 种代币的 URI
function uri(uint256 _id) external view returns (string memory);
}

3 IERC-1155Receiver接口

与ERC-721协议类似;为了避免误操作,造成代币损失,ERC-1155要求代币的接收合约需要继承【IERC-1155Receiver】接口,并定义了两个函数,
该接口的定义如下:

1
2
3
4
5
6
7
//IERC1155TokenReceiver、安全转账接口
interface IERC1155TokenReceiver {
//单币种的转账接收函数
function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4);
//批量多币种转账接收函数
function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4);
}

4 实现ERC-1155接口合约程序

下面的合约程序实现了【ERC-1155协议】合约接口;还实现了单币种和多币种的铸造及销毁函数;

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// author:hqd8080
// contracts/ERC1155.sol
// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.20 < 0.9.0;

import "./IERC1155.sol";
import "./IERC165.sol";

//npm install @openzeppelin/contracts@v4.8.2
import "@openzeppelin/contracts/utils/Strings.sol";

//ERC-1155合约,实现IERC-165、IERC-1155、IERC1155MetadataURI
contract ERC1155 is IERC165, IERC1155, IERC1155MetadataURI {

using Strings for uint256;
string public name;
string public symbol;

//owner => id => balance
mapping (address => mapping (uint256 => uint256)) public _balanceOf;
//owner => operator => approved
mapping (address => mapping (address => bool)) public _isApprovedForAll;

//构造函数
constructor(string memory name_, string memory symbol_) {
name = name_;
symbol = symbol_;
}

//安全转账函数
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) override external {
require(msg.sender == _from || _isApprovedForAll[_from][msg.sender], "ERC1155: not approved!");
require(_to != address(0), "ERC1155: to is a zero address");

_balanceOf[_from][_id] -= _value;
_balanceOf[_to][_id] += _value;

emit TransferSingle(msg.sender, _from, _to, _id, _value);

if (_to.code.length > 0) {
require(
IERC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _id, _value, _data) == IERC1155TokenReceiver.onERC1155Received.selector,
"ERC1155: unsaft transfer!"
);
}
}

//批量安全转账函数
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) override external {
require(msg.sender == _from || _isApprovedForAll[_from][msg.sender], "ERC1155: not approved!");
require(_to != address(0), "ERC1155: to is a zero address");
require(_ids.length == _values.length, "ERC1155: _ids length != _values length!");

for (uint256 i = 0; i < _ids.length; i++) {
_balanceOf[_from][_ids[i]] -= _values[i];
_balanceOf[_to][_ids[i]] += _values[i];
}

emit TransferBatch(msg.sender, _from, _to, _ids, _values);

if (_to.code.length > 0 ) {
require(
IERC1155TokenReceiver(_to).onERC1155BatchReceived(msg.sender, _from, _ids, _values, _data) == IERC1155TokenReceiver.onERC1155BatchReceived.selector,
"ERC1155: unsaft transfer!"
);
}
}

//持仓查询函数,查询 _owner 所拥有的 _id 种类的代币持有量
function balanceOf(address _owner, uint256 _id) override external view returns (uint256) {
require(_owner != address(0), "ERC1155: address zero is not a vaild owner!");
return _balanceOf[_owner][_id];
}

//批量持仓查询函数
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) override external view returns (uint256[] memory balances) {
require(_owners.length == _ids.length, "ERC1155: owner length != ids length!");

balances = new uint256[](_owners.length);

unchecked {
for (uint256 i = 0; i < _owners.length; i++) {
balances[i] = _balanceOf[_owners[i]][_ids[i]];
}
}

}

//批量授权函数
function setApprovalForAll(address _operator, bool _approved) override external {
_isApprovedForAll[msg.sender][_operator] = _approved;

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

//批量授权查询函数
function isApprovedForAll(address _owner, address _operator) override external view returns (bool) {
return _isApprovedForAll[_owner][_operator];
}

//检测是否实现了某个接口的函数
function supportsInterface(bytes4 interfaceID) override external pure returns (bool) {
return
interfaceID == type(IERC1155).interfaceId ||
interfaceID == type(IERC1155MetadataURI).interfaceId ||
interfaceID == type(IERC165).interfaceId;
}

//返回第 _id 种代币的 URI
function uri(uint256 _id) override external view returns (string memory) {
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, _id.toString())) : "";
}

function _baseURI() internal view virtual returns (string memory) {
return "";
}

//代币铸造
function _mint(address _to, uint256 _id, uint256 _value, bytes memory _data) internal {
require(_to != address(0), "ERC1155: _to is a zero address");

_balanceOf[_to][_id] += _value;

emit TransferSingle(msg.sender, address(0), _to, _id, _value);

if (_to.code.length > 0) {
require(
IERC1155TokenReceiver(_to).onERC1155Received(msg.sender, address(0), _id, _value, _data) == IERC1155TokenReceiver.onERC1155Received.selector,
"ERC1155: unsaft transfer!"
);
}
}

//批量代币铸造
function _batchMint(address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) internal {
require(_to != address(0), "ERC1155: _to is a zero address");
require(_ids.length == _values.length, "ERC1155: _ids length != _values length!");

for (uint256 i = 0; i < _ids.length; i++) {
_balanceOf[_to][_ids[i]] += _values[i];
}

emit TransferBatch(msg.sender, address(0), _to, _ids, _values);

if (_to.code.length > 0) {
require(
IERC1155TokenReceiver(_to).onERC1155BatchReceived(msg.sender, address(0), _ids, _values, _data) == IERC1155TokenReceiver.onERC1155BatchReceived.selector,
"ERC1155: unsaft transfer!"
);
}
}

//销毁代币
function _burn(address _from, uint256 _id, uint256 _value) internal {
require(_from != address(0), "ERC1155: _from is a zero address");
_balanceOf[_from][_id] -= _value;

emit TransferSingle(msg.sender, _from, address(0), _id, _value);
}

//批量销毁代币
function _batchBurn(address _from, uint256[] calldata _ids, uint256[] calldata _values) internal {
require(_from != address(0), "ERC1155: _from is a zero address");
require(_ids.length == _values.length, "ERC1155: _ids length != _values length!");

for (uint256 i = 0; i < _ids.length; i++) {
_balanceOf[_from][_ids[i]] -= _values[i];
}

emit TransferBatch(msg.sender, _from, address(0), _ids, _values);
}
}

5 自定义ERC-1155代币合约

在ERC-1155实现的合约基础上,开发者可以定义实现自己的ERC-1155标准代币并指定发行量上限;

主要思想是在构造函数中输入多标准代币的自定义名称和简称;然后重写单币种铸造函数多多币种的铸造函数,增加总供应量上限判断逻辑,以及单币种销毁函数和多币种销毁函数;

另外还可以通过重写_baseURI函数,设置开发者自己的URI值;

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
// author:hqd8080
// contracts/MyMultiToken.sol
// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.20 < 0.9.0;

import "./ERC1155.sol";

//自定义ERC1155代币合约
contract MyMultiToken is ERC1155 {
uint256 constant MAX_COUNT = 10000;

//构造函数
constructor() ERC1155 ("MULTITOKEN", "MT") {

}

//代币铸造
function _mint(uint256 _id, uint256 _value, bytes memory _data) external {
require(_id < MAX_COUNT, "MyMultiToken: _id overflow!");

_mint(msg.sender, _id, _value, _data);
}

//批量代币铸造
function _batchMint(uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external {
for (uint256 i = 0; i < _ids.length; i++) {
require(_ids[i] < MAX_COUNT, "MyMultiToken: id overflow!");
}

_batchMint(msg.sender, _ids, _values, _data);
}

//销毁代币
function _burn(uint256 _id, uint256 _value) external {
_burn(msg.sender, _id, _value);
}

//批量销毁代币
function _batchBurn(uint256[] calldata _ids, uint256[] calldata _values) external {
_batchBurn(msg.sender, _ids, _values);
}

//重写_baseURI函数
function _baseURI() internal pure override returns (string memory) {
return "ipfs://xxxxxx";
}

}