在本系列的第一篇文章中 ,我介紹了非真偽令牌(NFT)的概念以及對ERC721(草案)標準的需求 。 在本文中,我們將首先介紹ERC721標準介面,並分解一些要求。 關於ERC165標準還將有一個簡短但重要的繞道。 “ Todd Quackenbush在Unsplash上顯示的一個工具包的顯示內容顯示了諸如鎚子,斧子,盒子切割器,鐵和手電筒之類的工具 介面和ERC165標準
ERC721標準指出: “每個符合ERC-721的合約都必須實現ERC721和ERC165介面”
如果您從來不需要編寫一個Solidity協議,並與其他開發人員編寫的合約一起工作,您可能會想知道“什麼是介面 。”。 不管你是否想知道,“什麼是ERC621介面。”。 所以讓我們回答這兩個問題。 介面
一個介面基本上是一個抽象契約,但是你唯一可以定義的是未實現的函數。這是一個用Solidity代碼編寫的概要,它確保其他開發人員編寫的合約能夠很好地協同工作,而不必知道彼此的程式碼程式庫。
例如,如果一個介面將函數balanceOf定義為
函數balanceOf(地址_owner)外部視圖返回(uint256);
那麼你知道任何實現該介面的 契約都會有一個稱為balanceOf的函數,它接受一個參數( 地址 )並返回一個值( uint256 )。 你也知道這個函數的可變性是view,這意味著該函數能夠讀取合約狀態,但不能修改它,並且它具有外部修飾符,這對氣體消耗有影響,以及該功能應該如何調用。 如果您的契約函數的修飾符或傳回值與介面中定義的值不匹配,則會導致編譯器給出TypeErrors並且無法編譯。
因此,只需使用一個介面 ,就可以告訴其他開發人員有關您的合約所具有的一些功能以及如何使用它們。 簡單。
但是這提出了一個問題,沒有看看你的合約代碼,其他開發人員怎麼知道你已經使用了給定的介面 。 這個介面的全部重點是我們不必知道對方的程式碼程式庫。答案是: ERC165標準 。 ERC165標準 “什麼。。。 另一個ERC標準。 這個兔子洞有多深。“
別擔心,這是我們將要討論的唯一一個,而且非常簡單 - 它只有一個功能。 ERC165標準只是一種檢查您的合約指紋是否與任何給定介面的指紋相匹配的方法。 我們來看看整個ERC165標準介面:
介面ERC165 { /// @notice查詢合約是否實現了一個介面 /// @param interfaceID介面標識符,如 ///在ERC-165中指定 /// @dev介面標識在中指定 /// ERC-165。 該功能使用不到30,000個氣體。 /// @如果合約實現了`interfaceID`,@return`true` ///和`interfaceID`不是0xffffffff,否則為`false` 函數supportsInterface(bytes4 interfaceID)外部視圖返回(bool); }
因此,您的合約必須有一個函數supportsInterface ,它接受一個表示interfaceID ( bytes4 )的參數,如果該介面受支援,則返回true ( bool),其中interfaceID在ERC165標準中定義為“異或在介面中的所有功能選取器“。
或者用簡單的英語,你可以給這個功能任何介面的指紋( interfaceID ),並且它會告訴你它是否與你的任何契約的手指相匹配。
至於擷取函數選取器,有兩種簡單的方法來完成它。 作為一個例子,我將使用前面的balanceOf函數。 為了節省你滾動,它被定義為:
函數balanceOf(地址_owner)外部視圖返回(uint256){ // ... };
我們最終的ERC721合約已經實現了這個功能,這就是我添加花括弧的原因,但是當涉及到函數選取器時,它並不重要 - 你會明白為什麼。 擷取上述函數的函數選取器的兩種方法是:
this.balanceOf.selector
或手動使用
bytes4(keccak256( “balanceOf(地址)”))
兩者都會返回0x70a08231 ,雖然第一個看起來比較乾淨,但我們偶爾需要第二個,如果介面使用重載函數。 您會注意到函數選取器不關心參數名稱,修飾符,可變性,傳回值或函數的內容。 只是函數名稱和參數類型。 這就是為什麼我說這個功能是否被實現並不重要。
但是我們需要interfaceID中的“介面中所有函數選取器的XOR” 。 讓我們假設一個介面由三個函數組成: function1() , function2()和function3() 。
interfaceID只是:
interfaceID = this.function1.selector ^ this.function2.selector ^ this.function3.selector;
很簡單。 現在請記住,我們開始進行ERC165討論的原因是因為我們的ERC721協議需要實現ERC165介面,所以讓我們來編寫我們的ERC165實現(ERC721協議將繼承它)來封裝它。
當談到Solidity時,我們想要減少使用的氣體。 做不必要的計算會使使用者花費金錢,並浪費網路資源。 ERC165標準實際上要求supportsInterface功能“ 使用少於30,000個氣體”。 因此,每次有人調用supportsInterface ,不要重新計算interfaceID,而是讓我們支援的介面ID儲存在映射中 。
開始我們的合約 , CheckERC165 ,像這樣:
合約CheckERC165是ERC165 { 映射(bytes4 => bool)internal supportedInterfaces;
這樣, supportsInterface函數只需要從映射中返回一個值,下面是整個函數的實現:
函數supportsInterface(bytes4 interfaceID)外部視圖返回(bool){ 返回supportedInterfaces [interfaceID]; }
大。 我們的合約現在實現了ERC165介面中的所有功能(只有一個)。 讓我們將ERC165 interfaceID添加到supportedInterfaces並將此事物帶到一個完整的圓圈。
最近發布了Solidity Version 0.4.22,為我們的constructor函數提供了更簡單的constructor函數名稱。 所以讓我們來構造一個建構函式,並在其中添加ERC165介面的interfaceID。
建構函式()public { supportedInterfaces [this.supportsInterface.selector] = true; }
現在,如果有人使用ERC165標準介面 ( 0x01ffc9a7 )的interfaceID調用supportsInterface ,它將返回true 。
這就是ERC165。 完整的合約可在我的GitHub上找到 。 我們稍後將使用ERC721實現,在處理一般介面時,它也是一個很方便的實現。 ERC721介面
現在我們已經瞭解了介面,讓我們回到ERC721。 您會注意到ERC721標準實際上包含四個不同的介面。 一般ERC721合約的一個主要部分,一個用於可以接收ERC721令牌的合約,另外兩個用於增加額外功能的可選擴充。 我們現在將忽略擴充,並快速查看主介面和接收器。 應付職能和可變性
立即對我而言突出的是ERC721介面中的四個功能具有應付修飾符。 即兩個safeTransferFrom函數, transferFrom和approve 。 每次轉移令牌或授予令牌控制權時,ERC721合約總是應付款並沒有意義。
你可以想象這種情況可能是這種情況 - 如果你為NFT建立市場,那麼毫無疑問你會處理付款 - 但是令牌所有者必須支付的情況只是為了將令牌的控制權交給某人否則很難想象。 事實上, 應付修飾詞的原因並不是很性感,這一切都歸結為可變性。
從ERC721標準的注意事項部分: “易變性保證是弱到強: payable ,隱含不付款, view和pure 。”
因此,通過將這些功能標記為可payable ,這僅僅是作者的一種說法,即對所需的可變性沒有限制。 在這個特定的例子中, payable只是一種明確表示沒有限制的方式。 您的實施可能會更嚴格,但不會更弱。
有兩種方法可以解決這個問題,你可以: 保持這些功能為可支付的,只需返回任何隨事務發送的Ether(或保留它,如果這是您的設計),或者 刪除應付修飾符,但您必須在ERC721 介面副本中執行相同的操作 ,以避免引發TypeError。
從前面記得,函數選取器不受修飾符的影響,所以這不會干擾interfaceID。標記為external功能也是如此,您應該隨時根據需要將其更改為public 。 ERC721TokenReceiver
在結束之前,我想快速介紹ERC721TokenReceiver介面。 這不是我們令牌合約需要繼承的東西。 顧名思義,它是可以接收ERC721令牌的合約介面。
由於我不會在本系列中介紹製作錢包合約,因此可以快速製作一些虛擬錢包,稍後我們將使用這些虛擬錢包進行測試。 一個有效接收者和一個無效的接收者。
就我們的目的而言,唯一重要的資訊是有效ERC721TokenReceiver將實現該功能
函數onERC721Received(地址_from,uint256 _tokenId,位元組資料)外部返回(bytes4);
並返回
bytes4(keccak256(“onERC721Received(address,uint256,bytes)”))
一個無效的將不執行該函數,或者從字面上返回任何其他內容。 所以讓我們定義我們的兩個接收器如下:
合約ValidReceiver是ERC721TokenReceiver { 函數onERC721Received(地址_from,uint256 _tokenId,位元組資料)外部返回(bytes4){ 返回bytes4(keccak256(" onERC721Received(address,uint256,bytes) ")); } bytes4(keccak256(" onERC721Received(address,uint256,bytes) ")); } }
和
合約InvalidReceiver是ERC721TokenReceiver { 函數onERC721Received(地址_from,uint256 _tokenId,位元組資料)外部返回(bytes4){ 返回bytes4(keccak256(" some invalid return data ")); } bytes4(keccak256(" some invalid return data ")); } }
我們不會在很久之後才會使用這些測試,所以請儲存它們並放在口袋裡。 我只是想在我們對介面進行概述的同時對其進行介紹。 包起來
所以畢竟,你應該熟悉介面,並且有一個ERC165實現,你可以使用它來指示你的合約實現了哪些介面。 我們還介紹了ERC721標準中的可變性保證,並且很快寫了一些虛擬錢包合約以供日後測試。
在下一篇文章中,我們將使用到目前為止所介紹的所有內容,並開始編寫實際的ERC721令牌合約。
https://medium.com/coinmonks/jumping-into-solidity-the-erc721-standard-part-2-383438734de5