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],
''
);
一次交易就可以支付各航空公司的代幣。棒極了!