
我們在《區(qū)塊鏈殺手級應(yīng)用落地想象(上)》中提到,2021年被稱為NFT元年。短時間內(nèi),NFT它不再局限于加密世界的投機價值,它的潛力吸引了越來越多的國際品牌,比如耐克NFT專利允許用戶繁殖不同的鞋子來創(chuàng)造自己的定制運動鞋;其他,如美國國家籃球協(xié)會(NBA)、路易威登(LV)等國際知名品牌均在加速布局,可以說NFT為藝術(shù)收藏、音樂、游戲、體育、時尚等賦予新的價值加持。
NFT應(yīng)用場景豐富,初學(xué)者如何入門?本文將有助于小白的理解NFT合同合同。
NFT介紹合同標準
目前,NFT(Non-Fungible Tokens)最主流的合同有三種:ERC-721、ERC-1155和ERC-998。
在NFT大家嚴格遵守初期NFT定義規(guī)范,即ERC-721規(guī)范,早期非常熱門的加密貓系列是基于該規(guī)范開發(fā)的ERC-721 協(xié)議標準,每個基礎(chǔ)ERC-721創(chuàng)建的NFT都是獨一無二的,不可復(fù)制的。用戶可以在智能合同中編寫代碼來創(chuàng)建自己的代碼NFT,該段代碼遵循基本的通用模板格式,可以通過該代碼添加NFT所有者名稱、元數(shù)據(jù)或安全文件鏈接等細節(jié)。
ERC-721雖然規(guī)范可以很好的描述NFT,但是有一些缺點。例如,假設(shè)我想一次鑄造30個NFT,然后我需要發(fā)起30次鑄造NFT交易、效率和用戶體驗都不友好ERC-1155多個概念可以提出包裝NFT封裝成一個Collection,允許開發(fā)者在智能合同中實現(xiàn)無限數(shù)量FT和NFT。由于包裝的特點,相當于ERC-1155協(xié)議標準集成了ERC-20和ERC-721能力具有效率高、靈活性強的優(yōu)點。目前,它為許多游戲提供了動力。例如,游戲開發(fā)者可以在合同中定義各種項目(角色、武器、盔甲、藥水和超能力)。
隨著NFT概念進一步火熱,組合NFT概念被提出。例如,頭像可以由眼睛、嘴和鼻子組成,每個元素都是一個元素NFT或者FT,這些元素共同構(gòu)成了一個獨特的元素NFT但是整個頭像NFT在過去的傳統(tǒng)合同中,沒有所謂的層次關(guān)系,即鼻子部分不知道自己屬于哪一個NFT,或者化身部分不知道自己是由什么NFT或者FT組成。為此,,,ERC-998便應(yīng)運而生,也就是可組合Composable NFTs,縮寫為CNFT,即一個ERC-998可包含多個ERC-721和ERC-20轉(zhuǎn)移形式通證CNFT即是轉(zhuǎn)移CNFT整個層次結(jié)構(gòu)與所屬關(guān)系。
為了幫助您快速理解和介紹,下面將首先進行分析ERC-721和ERC-1155合同設(shè)計理念,然后詳細介紹如何編寫ERC 721合約。
NFT合同設(shè)計理念
ERC-721
ERC-721作為最為基礎(chǔ)的NFT合同有以下接口:
function balanceOf(address owner) -> uint256 balance /// @notice Find the owner of an NFT /// @dev NFTs assigned to zero address are considered invalid,and queries /// about them do throw. /// @param _tokenId The identifier for an NFT /// @return The address of the owner of the NFTfunction ownerOf(uint256 tokenId) -> address owner/// @notice Transfers the ownership of an NFT from one address to another address /// @dev Throws unless `msg.sender` is the current owner,an authorized /// operator,or the approved address for this NFT. Throws if `_from` is /// not the current owner. Throws if `_to` is the zero address. Throws if /// `_tokenId` is not a valid NFT. When transfer is complete,this function /// checks if `_to` is a smart contract (code size > 0). If so,it calls /// `onERC721Received` on `_to` and throws if the return value is not /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer /// @param data Additional data with no specified format,sent in call to `_to`function safeTransferFrom(address from,address to,uint256 tokenId)/// @notice Transfers the ownership of an NFT from one address to another address /// @dev This works identically to the other function with an extra data parameter, /// except this function just sets data to "". /// @param _from The current owner of the NFT /// @param _to The new owner /// @param _tokenId The NFT to transfer function transferFrom(address from,address to,uint256 tokenId) /// @notice Change or reaffirm the approved address for an NFT /// @dev The zero address indicates there is no approved address. /// Throws unless `msg.sender` is the current NFT owner,or an authorized /// operator of the current owner. /// @param _approved The new approved NFT controller /// @param _tokenId The NFT to approvefunction approve(address to,uint256 tokenId)/// @notice Enable or disable approval for a third party ("operator") to manage /// all of `msg.sender`'s assets /// @dev Emits the ApprovalForAll event. The contract MUST allow /// multiple operators per owner. /// @param _operator Address to add to the set of authorized operators /// @param _approved True if the operator is approved,false to revoke approvalfunction getApproved(uint256 tokenId) -> address operator /// @notice Query if an address is an authorized operator for another address /// @param _owner The address that owns the NFTs /// @param _operator The address that acts on behalf of the owner /// @return True if `_operator` is an approved operator for `_owner`,false otherwisefunction setApprovalForAll(address operator,bool _approved)/// @notice Get the approved address for a single NFT /// @dev Throws if `_tokenId` is not a valid NFT. /// @param _tokenId The NFT to find the approved address for /// @return The approved address for this NFT,or the zero address if there is nonefunction isApprovedForAll(address owner,address operator) -> bool
每個函數(shù)的含義見上述注釋。接下來,我們將簡要分析底層存儲邏輯:
mapping (uint256 => address) internal idToOwnermapping (uint256 => address) internal idToApprovalmapping (address => uint256) private ownerToNFTokenCountmapping (address => mapping (address => bool)) internal ownerToOperators
上述的4個mapping保持整個合同的存儲結(jié)構(gòu):
idToOwner維護誰有通證,映射關(guān)系就是通證ID到所有者地址;idToApproval誰被授權(quán)操作通證,映射關(guān)系就是通證ID到授權(quán)操作地址;ownerToNFTokenCount維護地址所有權(quán)nft總量,映射關(guān)系是用戶地址到代表總量的整數(shù);ownerToOperators維護一個地址是否授權(quán)另一個地址;一個主要的modifier是canOperate:
// 檢查是否有操作nft的權(quán)限modifier canOperate( uint256 _tokenId ) { / 找到對應(yīng)token所有者 address tokenOwner = idToOwner[_tokenId]; require( / 操作人員是所有者或授權(quán) tokenOwner == msg.sender | ownerToOperators[tokenOwner][msg.sender], / 否則返回錯誤 NOT_OWNER_OR_OPERATOR _; }
同時,ERC-721還支持可選實現(xiàn)項,metadata extension,主要用于返回NFT描述信息。
ERC-1155
ERC-1155與上述描述相同,因為實現(xiàn)了包裝功能ERC-1155支持大部分函數(shù)batch操作。相比之下ERC-721,ERC-1155有很好的效率提升。
ERC-1155接口如下:
function balanceOf(address account,uint256 id) → uint256function balanceOfBatch(address[] accounts,uint256[] ids) → uint256[]function setApprovalForAll(address operator,bool approved)function isApprovedForAll(address account,address operator) -> boolfunction safeTransferFrom(address from,address to,uint256 id,uint256 amount,bytes data)function safeBatchTransferFrom(address from,address to,uint256[] ids,uint256[] amounts,bytes data)
具體界面也比較清晰,這里就不贅述了。存儲結(jié)構(gòu),ERC-1155有以下兩個基本結(jié)構(gòu):
mapping (uint256 => mapping(address => uint256)) internal balances;mapping (address => mapping(address => bool)) internal operatorApproval;
balances維護一個賬戶所擁有的NFT總量,基本映射邏輯是id=>(owner=>balances)operatorApproval維護一個賬戶是否已被另一個賬戶授權(quán),主要邏輯相同;同樣的,ERC-1155有可選的擴展合同ERC1155Metadata_URI,主要是返回通證uri json。
ERC-721合約編寫
因為目前社區(qū)已經(jīng)開源了很多ERC-721標準模板可供參考,大部分都是寫的NFT合同可以借鑒一般模板。如果標準模板不能滿足所有需求,可以在外部建立自己的合同(內(nèi)部實現(xiàn)相應(yīng)的業(yè)務(wù)邏輯),繼承標準合同。
以下示例將符合開源標準ERC-721以合同為基本模板,在趣鏈中展示BaaS平臺內(nèi)的Web IDE進一步開發(fā)合同。
1)進入Web IDE:如下圖,在nf-token-mock合約中定義了mint NFT進入合同并執(zhí)行編譯操作的方法。
2)具體結(jié)果如下:
3)Web IDE與以太坊在線不同不同于以太坊在線IDE編輯器如Remix,趣鏈BaaS的Web IDE無需用戶使用,直接提供模擬部署和執(zhí)行環(huán)境Metamask測試網(wǎng)賬戶相當于省用戶Metamask導(dǎo)入測試網(wǎng)賬戶并持有測試通證的步驟,無需每次調(diào)用簽名授權(quán),可提高調(diào)試效率。
因此,我們可以選擇下圖NFTTokenMock模擬部署合同,包裝在合同中NFT mint等等,我們先做mint之后,可以進一步實施balanceof(查詢余額),Approve(授權(quán))等操作。
4)mint(鑄造):方向0xd69e9413029e7Fc483eFB5cB1aBCE4Ec44437F2C鑄造地址通證ID為166的NFT
5)balanceof(查詢余額):查詢0xd69e9413029e7Fc483eFB5cB1aBCE4Ec44437F2C有幾個地址NFT
類似地,您可以參考合同設(shè)計中提到的不同接口信息,調(diào)用函數(shù)執(zhí)行Approve(授權(quán))等操作。
6)合同安全檢測:如前所述,上述合同是基于社區(qū)開源的合同文件,安全性尚不清楚。因此,我們可以使用有趣的鏈Web IDE合同安全檢測工具,如靜態(tài)分析和形式驗證,有助于最大限度地規(guī)避合同潛在漏洞造成的風(fēng)險。
7)合同功能的個性化改進:本合同包裝了許多函數(shù)方法,但開發(fā)人員也可以根據(jù)需要編寫更多的功能,也可以在模擬執(zhí)行中使用Debug幫助調(diào)試操作。
8)合同編譯文件集成到SDK: 做完以上所有調(diào)試并編譯完成后,可將最終的合同編譯文件集成到趣鏈BaaS提供的SDK由此可以通過SDK進行NFT合約的部署、調(diào)用等管理操作。
9)SDK集成到區(qū)塊鏈應(yīng)用程序:最后,開發(fā)人員還需要打開業(yè)務(wù)系統(tǒng)與鏈上智能合約的交互,只需要對應(yīng)SDK集成到自己的區(qū)塊鏈應(yīng)用程序中。
【備注】
步驟8中介紹的是通過SDK對于初學(xué)者來說,部署合同仍然有一定的學(xué)習(xí)門檻。圖趣鏈如下BaaS合同實例的一鍵可視化部署功能。部署完成后,可直接通過趣鏈BaaS智能合約可視化調(diào)用平臺。
部署界面
合同實例管理界面
合同實例調(diào)用界面
總結(jié)
NFT經(jīng)過十年的發(fā)展,出現(xiàn)了ERC-721,ERC-1155和ERC-998作為典型的主流合同,技術(shù)結(jié)構(gòu)日益完善。初學(xué)者除了咨詢一些開源項目外,還了解一般情況NFT除了合同文件,還可以使用有趣的鏈條Web IDE智能合約研發(fā)設(shè)施等便捷,可充分賦予智能合約研發(fā)、部署、調(diào)用、升級等全生命周期管理流程,加快相關(guān)區(qū)塊鏈應(yīng)用的實施。