跳到主要内容

ARC721

ARC-721#

ARC-721是 Alaya 网络上发行非同质化代币(NFT)的一套标准接口,与 ERC-721完全兼容。

协议标准#

每个符合 ARC-721 标准的智能合约都必须实现 ARC721 与 ARC165 接口,并可以根据业务需要实现其他扩展接口。

ARC-721 & ARC-165接口#

interface ARC721 /* is ARC165 */ {    //events    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
    //required    function balanceOf(address _owner) external view returns (uint256);    function ownerOf(uint256 _tokenId) external view returns (address);    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;    function approve(address _approved, uint256 _tokenId) external payable;    function setApprovalForAll(address _operator, bool _approved) external;    function getApproved(uint256 _tokenId) external view returns (address);    function isApprovedForAll(address _owner, address _operator) external view returns (bool);    interface ARC165 {        function supportsInterface(bytes4 interfaceID) external view returns (bool);    }
    //optional    interface ARC721TokenReceiver {        function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);    }    //metadata extension接口对于ARC-721智能合约来说是可选的,用户可以查询智能合约的名称以及NFT代表的资产的详细信息。    interface ARC721Metadata {        function name() external view returns (string _name);        function symbol() external view returns (string _symbol);        function tokenURI(uint256 _tokenId) external view returns (string);    }    //enumeration extension对于ARC-721智能合约是可选的,允许用户的智能合约发布其NFT的完整列表并使其可见    interface ARC721Enumerable {        function totalSupply() external view returns (uint256);        function tokenByIndex(uint256 _index) external view returns (uint256);        function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);    }}

必须实现的接口#

  • balanceOf:统计用户持有的NFT数量
  • ownerOf:查询NFT的持有者
  • safeTransferFrom:将NFT的所有权从一个地址转移到另一个地址
  • transferFrom:转移NFT所有权,调用者负责确认接收者是否有能力接收NFT,否则可能永久丢失
  • approve:授权第三方操作某个NFT资产
  • setApprovalForAll:启用或禁用第三方管理交易发起者的所有资产
  • getApproved:获取单个NFT的被授权给哪个地址管理
  • isApprovedForAll:查询一个地址是否被另一个地址授权管理token
  • supportsInterface:查询合约是否实现了某个接口

可选实现的接口#

  • onERC721Received:合约如果需要接受安全转账,必须实现ARC721TokenReceiver接口
  • name:合约的名字
  • symbol:合约的缩写代号
  • tokenURI:给token一个统一资源标识符(URI),指向符合 "ARC721 元数据 JSON Schema" 的 JSON 文件,JSON 结构如下:
    {  "title": "Asset Metadata",  "type": "object",  "properties": {      "name": {          "type": "string",          "description": "指示NFT代表什么"      },      "description": {          "type": "string",          "description": "描述NFT 代表的资产"      },      "image": {          "type": "string",          "description": "指向NFT表示资产的资源的URI(MIME 类型为 image/*) , 可以考虑宽度在320到1080像素之间,宽高比在1.91:1到4:5之间的图像      }  }}
  • totalSupply:查询此合约拥有的token的总数
  • tokenByIndex:根据索引查询token
  • tokenOfOwnerByIndex:根据索引来查找用户的token

事件#

  • Transfer:任何NFT的所有权更改时(不管哪种方式),就会触发此事件,在链上记录所有权更改信息日志
  • Approval:当更改或确认NFT的授权地址时触发,在链上记录此信息日志
  • ApprovalForAll:所有者启用或禁用操作员时触发(操作员可管理所有者所持有的NFTs),在链上记录此信息日志

示例#

ARC-721标准与ERC-721完全兼容,示例可参考这里

查看#

可以通过Alaya浏览器查看,也可以通过ATON查看ARC-721合约交易。

合约发行#

请参考Solidity合约入门手册

调用方法#

以python为例:

安装python依赖#

使用下列命令,安装 python library,建议 Python 版本在3.7.5+:

pip install client-sdk-python

在安装过程中,部分依赖包会需要 c++14 编译,请在看到相关提示后,下载cppbuildtools,使用默认值安装后,重新执行 pip 安装命令即可。

实例化合约#

以下为python代码示例:

from client_sdk_python import Web3, HTTPProvider
rpc, chain_id, hrp = 'http://127.0.0.1:6789', 201030, 'ATP'w3 = Web3(HTTPProvider(rpc), chain_id=chain_id, hrp_type=hrp)abi = [  {    "inputs": [      {"internalType": "string", "name": "_name", "type": "string"},      {"internalType": "string", "name": "_symbol", "type": "string"}    ],    "stateMutability": "nonpayable",    "type": "constructor"  },  {    "inputs": [      {"internalType": "address", "name": "_to", "type": "address"},      {"internalType": "uint256", "name": "_tokenId", "type": "uint256"},      {"internalType": "string", "name": "_uri", "type": "string"}    ],    "name": "mint",    "outputs": [],    "stateMutability": "nonpayable",    "type": "function"  },  {    "inputs": [{"internalType": "address", "name": "_owner", "type": "address"}],    "name": "balanceOf",    "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],    "stateMutability": "view",    "type": "function"  },  {    "inputs": [{"internalType": "uint256", "name": "_tokenId", "type": "uint256"}],    "name": "ownerOf",    "outputs": [{"internalType": "address", "name": "_owner", "type": "address"}],    "stateMutability": "view",    "type": "function"  },  {    "inputs": [      {"internalType": "address", "name": "_from", "type": "address"},      {"internalType": "address", "name": "_to", "type": "address"},      {"internalType": "uint256", "name": "_tokenId", "type": "uint256"}    ],    "name": "safeTransferFrom",    "outputs": [],    "stateMutability": "nonpayable",    "type": "function"  },  {    "anonymous": false,    "inputs": [      {"indexed": true, "internalType": "address", "name": "_from", "type": "address"},      {"indexed": true, "internalType": "address", "name": "_to", "type": "address"},      {"indexed": true, "internalType": "uint256", "name": "_tokenId", "type": "uint256"}    ],    "name": "Transfer",    "type": "event"  },]arc721 = w3.eth.contract(address='contract address', abi=abi)# 查看合约所有的function 和 eventprint([func for func in arc721.functions])print([event for event in arc721.events])

查询合约信息#

balanceOfownerOf 示例,其他查询方法与此类似:

# 统计用户持有的NFT数量arc721.functions.balanceOf('your address').call()# 查询NFT的持有者arc721.functions.ownerOf('your token id').call()

发送合约交易#

safeTransferFrom示例,其他交易方法与此类似:

# 将NFT的所有权从一个地址转移到另一个地址tx = {    'chainId': w3.chain_id,    'nonce': w3.eth.getTransactionCount('your address'),    'gas': 4012388,    'value': 0,    'gasPrice': 1000000000,}txn = arc721.functions.safeTransferFrom(_from='your address', _to='to address', _tokenId='your token id').buildTransaction(tx)signed_txn = w3.eth.account.signTransaction(txn, private_key='your private key')tx_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()receipt = w3.eth.waitForTransactionReceipt(tx_hash)

获取合约事件#

safeTransferFrom交易事件示例,其他事件获取方法与此类似:

events = arc721.events.Transfer().processReceipt(receipt)

上传Metadata到IPFS网络#

Metadata是NFT代币的详细信息, 存储在链下,一般发行一枚 NFT 代币会指定一个 URI 路径,指向这枚代币的 Metadata 数据。

1. 安装IPFS#

参照IPFS安装说明进行安装与启动。

2. 添加文件到ipfs#

准备一张图片,将图片命名为 Alaya.jpeg,通过以下命令上传到 ipfs 节点上

$ ipfs add Alaya.jpeg

输出以下文件ID:

added QmZtmD2qt6fJot32nabSP3CUjicnypEBz7bHVDhPQt9aAy Alaya.jpeg

3. 验证文件是否上传成功#

在浏览器里面打开图片的链接,能看到图片说明图片能成功被下载: https://ipfs.io/ipfs/QmZtmD2qt6fJot32nabSP3CUjicnypEBz7bHVDhPQt9aAy?filename=Alaya.jpeg

4. 构造NFT的 Metadata#

首先按照ARC721 元数据 JSON Schema 创建一个json文件,命名为Alaya.json,示例如下:

{    "name":"Alaya.jpg",    "author":"Alaya",    "description":"use for arc721",    "image":"https://ipfs.io/ipfs/QmZtmD2qt6fJot32nabSP3CUjicnypEBz7bHVDhPQt9aAy?filename=Alaya.jpeg"}

将 Alaya.json 上传到 IPFS 节点上:

$ ipfs add Alaya.json

输出以下文件ID:

added QmQXqTVCb1w7CmdsYxHWR1T1qyaCHHgWwiPmoZDcQL39Px Alaya.json

在浏览器里面打开metadata文件的uri: https://ipfs.io/ipfs/QmQXqTVCb1w7CmdsYxHWR1T1qyaCHHgWwiPmoZDcQL39Px?filename=Alaya.json

以上,上传完毕。