文章目录
- 1、NFT - 维基百科
- 2、IERC1155MetadataURI
- 3、IERC1155Receiver
- 4、IERC1155
- 5、ERC1155
- 6、NFT1155
- 7、开源地址
1、NFT - 维基百科
ERC-1155 标准于2018年6月由Witek Radomski、Andrew Cooke、Philippe Castonguay、James Therien、Eric Binet及Ronan Sandford提出。此标准可针对单一应用合约,提供同质代币、非同质代币(用ERC-1155建构ERC-721资产)及其他结构类型(例如半同质代币,semi-fungible tokens)的多元组合。相较于ERC-721每创立一个代币ID就对应一个单独智能合约资产,在ERC-1155多元代币标准下,每个代币ID可以代表一系列不同类型的资产,并附加量化区块以标示钱包中各类型资产的数量;同类型资产可以互换,要转让多少数量也可以。
ERC-1155 标准下的NFT结构上更有弹性:拥有元数据、供应量与其他属性;新功能包括:可一次传输多种代币类型以节省交易成本、也可同时进行多个代币交易(托管/原子交换)而无需逐一批准每个合约、可在单一应用合约中注明并混搭多种FT、NFT及SFT。
2、IERC1155MetadataURI
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// ERC1155的可选接口,加入了uri()函数查询元数据
interface IERC1155MetadataURI {
// 返回第`id`种类代币的URI
function uri(uint256 id) external view returns (string memory);
}
3、IERC1155Receiver
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./IERC165.sol";
interface IERC1155Receiver is IERC165 {
// 接受ERC1155安全转账`safeTransferFrom`
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
// 接受ERC1155批量安全转账`safeBatchTransferFrom`
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
4、IERC1155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./IERC165.sol";
// https://eips.ethereum.org/EIPS/eip-1155[EIP].
interface IERC1155 is IERC165 {
// 单类代币转账事件
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 value
);
// 批量代币转账事件, ids和values为转账的代币种类和数量数组
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
// 批量授权事件, 当`account`将所有代币授权给`operator`时释放
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
// 当`id`种类的代币的URI发生变化时释放,`value`为新的URI
event URI(string value, uint256 indexed id);
// 持仓查询,返回`account`拥有的`id`种类的代币的持仓量
function balanceOf(address account, uint256 id) external view returns (uint256);
// 批量持仓查询,`accounts`和`ids`数组的长度要想等。
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
// 批量授权,将调用者的代币授权给`operator`地址。释放{ApprovalForAll}事件.
function setApprovalForAll(address operator, bool approved) external;
// 批量授权查询,如果授权地址`operator`被`account`授权,则返回`true`
function isApprovedForAll(address account, address operator) external view returns (bool);
// 安全转账,将`amount`单位`id`种类的代币从`from`转账给`to`. 释放{TransferSingle}事件.
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
// 批量安全转账, 释放{TransferBatch}事件
function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}
5、ERC1155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./IERC1155MetadataURI.sol";
import "./Address.sol";
import "./Strings.sol";
import "./IERC165.sol";
// ERC1155多代币标准 https://eips.ethereum.org/EIPS/eip-1155
contract ERC1155 is IERC165, IERC1155, IERC1155MetadataURI {
using Address for address;
using Strings for uint256;
// Token名称
string public name;
// Token代号
string public symbol;
// 代币种类id 到 账户account 到 余额balances 的映射
mapping(uint256 => mapping(address => uint256)) private _balances;
// address 到 授权地址 的批量授权映射
mapping(address => mapping(address => bool)) private _operatorApprovals;
// 构造函数,初始化`name` 和`symbol`, uri_
constructor(string memory name_, string memory symbol_) {
name = name_;
symbol = symbol_;
}
function supportsInterface(bytes4 interfaceId)
public view virtual override returns (bool) {
return
interfaceId == type(IERC1155).interfaceId ||
interfaceId == type(IERC1155MetadataURI).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
// 持仓查询 实现IERC1155的balanceOf,返回account地址的id种类代币持仓量。
function balanceOf(address account, uint256 id)
public view virtual override returns (uint256) {
require(account != address(0), "ERC1155: address zero is not a valid owner");
return _balances[id][account];
}
// @dev 批量持仓查询
function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
public view virtual override returns (uint256[] memory) {
require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; ++i) {
batchBalances[i] = balanceOf(accounts[i], ids[i]);
}
return batchBalances;
}
// 批量授权,调用者授权operator使用其所有代币
function setApprovalForAll(address operator, bool approved)
public virtual override {
require(msg.sender != operator, "ERC1155: setting approval status for self");
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
// 查询批量授权.
function isApprovedForAll(address account, address operator)
public view virtual override returns (bool) {
return _operatorApprovals[account][operator];
}
// 安全转账,将`amount`单位的`id`种类代币从`from`转账到`to`
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data)
public virtual override {
address operator = msg.sender;
// 调用者是持有者或是被授权
require(
from == operator || isApprovedForAll(from, operator),
"ERC1155: caller is not token owner nor approved"
);
require(to != address(0), "ERC1155: transfer to the zero address");
// from地址有足够持仓
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
// 更新持仓量
unchecked {
_balances[id][from] = fromBalance - amount;
}
_balances[id][to] += amount;
// 释放事件
emit TransferSingle(operator, from, to, id, amount);
// 安全检查
_doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
}
// 批量安全转账,将`amounts`数组单位的`ids`数组种类代币从`from`转账到`to`
function safeBatchTransferFrom(
address from, address to, uint256[] memory ids,
uint256[] memory amounts, bytes memory data)
public virtual override {
address operator = msg.sender;
// 调用者是持有者或是被授权
require(
from == operator || isApprovedForAll(from, operator),
"ERC1155: caller is not token owner nor approved"
);
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
require(to != address(0), "ERC1155: transfer to the zero address");
// 通过for循环更新持仓
for (uint256 i = 0; i < ids.length; ++i) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
unchecked {
_balances[id][from] = fromBalance - amount;
}
_balances[id][to] += amount;
}
emit TransferBatch(operator, from, to, ids, amounts);
// 安全检查
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
}
// 锻造
function _mint(address to, uint256 id, uint256 amount, bytes memory data)
internal virtual {
require(to != address(0), "ERC1155: mint to the zero address");
address operator = msg.sender;
_balances[id][to] += amount;
emit TransferSingle(operator, address(0), to, id, amount);
_doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
}
// 批量铸造
function _mintBatch(address to, uint256[] memory ids,
uint256[] memory amounts, bytes memory data)
internal virtual {
require(to != address(0), "ERC1155: mint to the zero address");
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
address operator = msg.sender;
for (uint256 i = 0; i < ids.length; i++) {
_balances[ids[i]][to] += amounts[i];
}
emit TransferBatch(operator, address(0), to, ids, amounts);
_doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
}
// 销毁
function _burn(address from, uint256 id, uint256 amount) internal virtual {
require(from != address(0), "ERC1155: burn from the zero address");
address operator = msg.sender;
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][from] = fromBalance - amount;
}
emit TransferSingle(operator, from, address(0), id, amount);
}
// 批量销毁
function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts)
internal virtual {
require(from != address(0), "ERC1155: burn from the zero address");
require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
address operator = msg.sender;
for (uint256 i = 0; i < ids.length; i++) {
uint256 id = ids[i];
uint256 amount = amounts[i];
uint256 fromBalance = _balances[id][from];
require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
unchecked {
_balances[id][from] = fromBalance - amount;
}
}
emit TransferBatch(operator, from, address(0), ids, amounts);
}
// 安全转账检查
function _doSafeTransferAcceptanceCheck( address operator, address from, address to,
uint256 id, uint256 amount, bytes memory data) private {
if (to.isContract()) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
revert("ERC1155: ERC1155Receiver rejected tokens");
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1155: transfer to non-ERC1155Receiver implementer");
}
}
}
// ERC1155的批量安全转账检查
function _doSafeBatchTransferAcceptanceCheck(
address operator, address from, address to,
uint256[] memory ids, uint256[] memory amounts, bytes memory data) private {
if (to.isContract()) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
revert("ERC1155: ERC1155Receiver rejected tokens");
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1155: transfer to non-ERC1155Receiver implementer");
}
}
}
// 返回id种类代币的uri,存储metadata,类似ERC721的tokenURI.
function uri(uint256 id) public view virtual override returns (string memory) {
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, id.toString())) : "";
}
// 计算{uri}的BaseURI,uri就是把baseURI和tokenId拼接在一起,需要开发重写.
function _baseURI() internal view virtual returns (string memory) {
return "";
}
}
6、NFT1155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./ERC1155.sol";
contract NFT1155 is ERC1155 {
uint256 constant MAX_ID = 10000;
constructor() ERC1155("NFT1155", "NFT1155"){
}
function _baseURI() internal pure override returns (string memory) {
return "ipfs://QmcPGA3kecSMfHcv8i6wpgB2RkPA1FSVqu6AZY3rKNmxuj/";
}
function mint(address to, uint256 id, uint256 amount) external {
require(id < MAX_ID, "id overflow");
_mint(to, id, amount, "");
}
function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts) external {
for(uint256 i = 0; i < ids.length;i++){
require(ids[i] < MAX_ID, "id overflow");
}
_mintBatch(to, ids, amounts, "");
}
}
测试运行:
7、开源地址
gitee 开源地址。