我们在《区块链杀手级应用落地想象(上)》中提到,2021年被称为NFT元年。短时间内,NFT它不再局限于加密世界的投机价值,它的潜力吸引了越来越多的国际品牌,比如耐克NFT专利允许用户繁殖不同的鞋子来创造自己的定制运动鞋;其他,如美国国家篮球协会(NBA)、路易威登(LV)等国际知名品牌均在加速布局,可以说NFT为艺术收藏、音乐、游戏、体育、时尚等赋予新的价值加持。
NFT应用场景丰富,初学者如何入门?本文将有助于小白的理解NFT合同合同。
NFT介绍合同标准
目前,NFT(Non-Fungible Tokens)最主流的合同有三种:ERC-721、ERC-1155和ERC-998。
在NFT大家严格遵守初期NFT定义规范,即ERC-721规范,早期非常热门的加密猫系列是基于该规范开发的ERC-721 协议标准,每个基础ERC-721创建的NFT都是独一无二的,不可复制的。用户可以在智能合同中编写代码来创建自己的代码NFT,该段代码遵循基本的通用模板格式,可以通过该代码添加NFT所有者名称、元数据或安全文件链接等细节。
ERC-721虽然规范可以很好的描述NFT,但是有一些缺点。例如,假设我想一次铸造30个NFT,然后我需要发起30次铸造NFT交易、效率和用户体验都不友好ERC-1155多个概念可以提出包装NFT封装成一个Collection,允许开发者在智能合同中实现无限数量FT和NFT。由于包装的特点,相当于ERC-1155协议标准集成了ERC-20和ERC-721能力具有效率高、灵活性强的优点。目前,它为许多游戏提供了动力。例如,游戏开发者可以在合同中定义各种项目(角色、武器、盔甲、药水和超能力)。
随着NFT概念进一步火热,组合NFT概念被提出。例如,头像可以由眼睛、嘴和鼻子组成,每个元素都是一个元素NFT或者FT,这些元素共同构成了一个独特的元素NFT但是整个头像NFT在过去的传统合同中,没有所谓的层次关系,即鼻子部分不知道自己属于哪一个NFT,或者化身部分不知道自己是由什么NFT或者FT组成。为此,,,ERC-998便应运而生,也就是可组合Composable NFTs,缩写为CNFT,即一个ERC-998可包含多个ERC-721和ERC-20转移形式通证CNFT即是转移CNFT整个层次结构与所属关系。
为了帮助您快速理解和介绍,下面将首先进行分析ERC-721和ERC-1155合同设计理念,然后详细介绍如何编写ERC 721合约。
NFT合同设计理念
ERC-721
ERC-721作为最为基础的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
每个函数的含义见上述注释。接下来,我们将简要分析底层存储逻辑:
mapping (uint256 => address) internal idToOwnermapping (uint256 => address) internal idToApprovalmapping (address => uint256) private ownerToNFTokenCountmapping (address => mapping (address => bool)) internal ownerToOperators
上述的4个mapping保持整个合同的存储结构:
idToOwner维护谁有通证,映射关系就是通证ID到所有者地址;idToApproval谁被授权操作通证,映射关系就是通证ID到授权操作地址;ownerToNFTokenCount维护地址所有权nft总量,映射关系是用户地址到代表总量的整数;ownerToOperators维护一个地址是否授权另一个地址;一个主要的modifier是canOperate:
// 检查是否有操作nft的权限modifier canOperate( uint256 _tokenId ) { / 找到对应token所有者 address tokenOwner = idToOwner[_tokenId]; require( / 操作人员是所有者或授权 tokenOwner == msg.sender | ownerToOperators[tokenOwner][msg.sender], / 否则返回错误 NOT_OWNER_OR_OPERATOR _; }
同时,ERC-721还支持可选实现项,metadata extension,主要用于返回NFT描述信息。
ERC-1155
ERC-1155与上述描述相同,因为实现了包装功能ERC-1155支持大部分函数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)
具体界面也比较清晰,这里就不赘述了。存储结构,ERC-1155有以下两个基本结构:
mapping (uint256 => mapping(address => uint256)) internal balances;mapping (address => mapping(address => bool)) internal operatorApproval;
balances维护一个账户所拥有的NFT总量,基本映射逻辑是id=>(owner=>balances)operatorApproval维护一个账户是否已被另一个账户授权,主要逻辑相同;同样的,ERC-1155有可选的扩展合同ERC1155Metadata_URI,主要是返回通证uri json。
ERC-721合约编写
因为目前社区已经开源了很多ERC-721标准模板可供参考,大部分都是写的NFT合同可以借鉴一般模板。如果标准模板不能满足所有需求,可以在外部建立自己的合同(内部实现相应的业务逻辑),继承标准合同。
以下示例将符合开源标准ERC-721以合同为基本模板,在趣链中展示BaaS平台内的Web IDE进一步开发合同。
1)进入Web IDE:如下图,在nf-token-mock合约中定义了mint NFT进入合同并执行编译操作的方法。
2)具体结果如下:
3)Web IDE与以太坊在线不同不同于以太坊在线IDE编辑器如Remix,趣链BaaS的Web IDE无需用户使用,直接提供模拟部署和执行环境Metamask测试网账户相当于省用户Metamask导入测试网账户并持有测试通证的步骤,无需每次调用签名授权,可提高调试效率。
因此,我们可以选择下图NFTTokenMock模拟部署合同,包装在合同中NFT mint等等,我们先做mint之后,可以进一步实施balanceof(查询余额),Approve(授权)等操作。
4)mint(铸造):方向0xd69e9413029e7Fc483eFB5cB1aBCE4Ec44437F2C铸造地址通证ID为166的NFT
5)balanceof(查询余额):查询0xd69e9413029e7Fc483eFB5cB1aBCE4Ec44437F2C有几个地址NFT
类似地,您可以参考合同设计中提到的不同接口信息,调用函数执行Approve(授权)等操作。
6)合同安全检测:如前所述,上述合同是基于社区开源的合同文件,安全性尚不清楚。因此,我们可以使用有趣的链Web IDE合同安全检测工具,如静态分析和形式验证,有助于最大限度地规避合同潜在漏洞造成的风险。
7)合同功能的个性化改进:本合同包装了许多函数方法,但开发人员也可以根据需要编写更多的功能,也可以在模拟执行中使用Debug帮助调试操作。
8)合同编译文件集成到SDK: 做完以上所有调试并编译完成后,可将最终的合同编译文件集成到趣链BaaS提供的SDK由此可以通过SDK进行NFT合约的部署、调用等管理操作。
9)SDK集成到区块链应用程序:最后,开发人员还需要打开业务系统与链上智能合约的交互,只需要对应SDK集成到自己的区块链应用程序中。
【备注】
步骤8中介绍的是通过SDK对于初学者来说,部署合同仍然有一定的学习门槛。图趣链如下BaaS合同实例的一键可视化部署功能。部署完成后,可直接通过趣链BaaS智能合约可视化调用平台。
部署界面
合同实例管理界面
合同实例调用界面
总结
NFT经过十年的发展,出现了ERC-721,ERC-1155和ERC-998作为典型的主流合同,技术结构日益完善。初学者除了咨询一些开源项目外,还了解一般情况NFT除了合同文件,还可以使用有趣的链条Web IDE智能合约研发设施等便捷,可充分赋予智能合约研发、部署、调用、升级等全生命周期管理流程,加快相关区块链应用的实施。