ERC-1155是ERC-20和ERC-721的升级规范,它允许在一个交易中发送多种不同的代币,就像同时转账人民币和美元。ERC-1155以在区块链游戏中的广泛使用而闻名,但它其实也适合有很多其他的应用场景。在这个教程中我们将学习ERC-1155规范约定的主要接口,并利用openzepplin实现一个用于航空业的ERC-1155多重代币。
用自己熟悉的语言学习 以太坊DApp开发 : Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart
1、什么是ERC-1155?
ERC-1155引入的关键功能是多代币支持。让我们通过航空代币来理解这一点的重要性。
假设我们处在一个世界上,每个航空公司都有自己的代币。现在,我想预订一架环球航班,其中有来自三家不同航空公司的三架转机航班。要支付机票,我需要进行三笔单独的交易,并分别为每笔交易使用不同的代币付款。
这个过程中许多事情可能会出错。如果我为航班1付费,然后已经预订了航班2怎么办?现在,我必须找到一个新的连接。在具有集中货币的现实世界中,这项工作是由在线旅行社(OTA)或更传统的传统旅行社完成的。但是,在我们的示例中,必须创建一个新的智能合约,该合约可以连续执行所有三笔交易,将其部署,然后调用交易功能。只为一次飞行就这么付费实在是有点复杂,不是吗?
好在我们可以创建了一个包含多个航空公司的单独代币的单一多代币合约,这正是ERC-1155允许我们执行的操作。我们可以添加任意种类的代币,而不仅仅是一个航空公司的代币。下面让我们更深入地了解ERC-1155的功能。
2、ERC-1155功能和特点
- 批量转移:一次调用即可转移多个资产。
- 批次余额:一次调用即可获取多个资产的余额。
- 批量批准:批准所有令牌到一个地址。
- EIP-165支持:声明支持的接口。
- 钩子接口:提供代币接受钩子接口。
- NFT支持:如果供应量仅为1,则将其视为NFT。
- 安全转移规则:安全转移的规则集
3、ERC-1155批量转账
批量转账与常规ERC-20转账非常相似。让我们看一下常规的ERC-20 transferFrom函数:
// ERC-20
function transferFrom(address from, address to, uint256 value) external returns (bool);
ERC-1155的唯一区别是,我们将值作为数组传递,并且还传递了id数组。例如,给定_ids= [3,6,13]和_values= [100,200,5],则转账结果为:
- 将ID为3的100个代币从_from转移到_to。
- 将ID为6的200个代币从_from转移到_to。
- 将ID为13的5个代币从_from转移到_to。
在ERC-1155中只有transferFrom,没有transfer。要将其像ERC20转账一样使用,只需将from地址设置为调用该函数的地址即可。
为什么将其称为“安全” -BatchTransferFrom? -有关安全转移规则,请参见下面的第7节。
为什么不返回布尔值?-交易失败时将回滚,与我们之前讨论的SafeERC-20实现相同。
什么是字节数据字段?-就像ERC-777一样,你可以传入任意数据,这些数据也将传递给接收钩子。
// ERC-1155 function safeBatchTransferFrom( address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data ) external;
4、ERC-1155批量查询余额
在ERC-1155中,与ERC-20 balanceOf对应的调用同样支持批量查询。提醒一下,这是ERC-20版本:
// ERC-20
function balanceOf(address owner) external view returns (uint256);
对于余额查询来说,甚至更简单,我们可以在一个调用中检索多个余额。传入所有者数组,然后传入代币ID数组。
// ERC-1155
function balanceOfBatch(
address[] calldata _owners,
uint256[] calldata _ids
) external view returns (uint256[] memory);
例如,给定_ids = [3,6,13]和_owners = [0xbeef ...,0x1337 ...,0x1111 ...],返回值为:
[
balanceOf(0xbeef...),
balanceOf(0x1337...),
balanceOf(0x1111...)
]
5、ERC-1155批量授权
批准授权与ERC-20略有不同。在ERC1155中无需设置授权金额,只需调用setApprovalForAll将操作员设置为批准或未批准即可。
// ERC-1155
function setApprovalForAll(
address _operator,
bool _approved
) external;
可以调用isApprovedForAll方法读取当前的授权状态。如你所见,它是全部或全部。你无法定义要批准的代币数量,也不能指定授权哪种代币。
// ERC-1155
function isApprovedForAll(
address _owner,
address _operator
) external view returns (bool);
这是有意的设计,目的是保持使用的简单性。你只能批准一个地址的所有内容。如果需要对特定批准进行更精细的控制,请查看EIP-1761。
6、EIP-165支持
如果还不了解EIP-165是什么,请查看此教程。简而言之, EIP165规范了智能合约如何声明其支持的接口。例如,智能合约是否支持接收ERC-1155代币?如果是这样,则相应的supportsInterface功能必须存在,并且对于该合约返回true。
这直接将我们带到了下一个概念:钩子。
7、EIP-1155的接收钩子
有了EIP-165支持,ERC-1155仅支持智能合约的接收钩子。钩子函数必须返回一个预定义的4字节magic值,该值指定为:
bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))
当接收合约返回此值时,假定合约接受转账并知道如何处理ERC-1155代币,那么合约就不再会出现卡死的代币了。
function onERC1155BatchReceived(
address _operator,
address _from,
uint256[] calldata _ids,
uint256[] calldata _values,
bytes calldata _data
) external returns(bytes4);
8、ERC-1155 非同质化代币支持
如果代币的发行量为1,那么这种代币本质上是非同质化代币(NFT)。按照ERC-721的标准,你可以定义元数据URL。客户端可以读取和修改该URL,详细内容请参见此处。
9、ERC-1155安全转账规则
在前面的说明中,我们已经谈到了一些安全的转账规则。但是,让我们看一下最重要的规则:
- 必须批准调用方可以消费该_from地址持有的代币,否则调用方必须为_from。
- 在出现如下状况时,转账交易必须回滚:
- _to 地址为0。
- _ids与_values长度不同
- 代币持有人在_ids中的任何余额都低于_values发送给接收者的相应金额。
- 发生任何其他错误。
注意:包括钩子在内的所有批处理功能也作为不带批处理的版本存在。这样做是出于提高gas效率的考虑,考虑仅转移一种资产仍可能是最常用的方式。为了简单起见,我们省略了它们。名称相同,只需删除Batch
前缀即可。
10、基于ERC-1155的航空代币实现
我们利用文档齐备并且经过审核的Openzeppelin合约库来实现基于ERC-1155的航空代币。通过npm安装它们或直接在Remix中导入Github URL。可以在此处查看Openzeppelin ERC-1155的文档。
我们之前的航空公司示例可以这样实现:
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
contract AirlineTokens is ERC1155 {
address public governance;
uint256 public airlineCount;
modifier onlyGovernance() {
require(msg.sender == governance, "only governance can call this");
_;
}
constructor(address governance_) public ERC1155("") {
governance = governance_;
airlineCount = 0;
}
function addNewAirline(uint256 initialSupply) external onlyGovernance {
airlineCount++;
uint256 airlineTokenClassId = airlineCount;
_mint(msg.sender, airlineTokenClassId, initialSupply, "");
}
}
这就是我们所需要的。现在可以调用:
AirlineTokens.safeBatchTransferFrom(
myBuyerAddress, sellerAddress,
[airlineId1, airlineId2, airlineId3],
[firstFlightPrice, secondFlightPrice, thirdFlightPrice],
''
);
一次交易就可以支付各航空公司的代币。棒极了!