跳到主要内容

PRC-721

PRC-721合约#

PRC-721是在PlatON网络上发行非同质化代币(non-fungible token, NFT)一套标准接口,与ERC-721完全兼容。

协议标准#

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

PRC-721 & PRC-165接口#

interface PRC721 /* is PRC165 */ {    //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 PRC165 {        function supportsInterface(bytes4 interfaceID) external view returns (bool);    }
    //optional    interface PRC721TokenReceiver {        function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);    }    //metadata extension接口对于PRC-721智能合约来说是可选的,用户可以查询智能合约的名称以及NFT代表的资产的详细信息。    interface PRC721Metadata {        function name() external view returns (string _name);        function symbol() external view returns (string _symbol);        function tokenURI(uint256 _tokenId) external view returns (string);    }    //enumeration extension对于PRC-721智能合约是可选的,允许用户的智能合约发布其NFT的完整列表并使其可见。    interface PRC721Enumerable {        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

    合约如果需要接受安全转账,必须实现PRC721TokenReceiver接口。

  • name

    合约的名字。

  • symbol

    合约的缩写代号。

  • tokenURI

    给token一个统一资源标识符(URI),指向符合 "PRC721 元数据 JSON Schema" 的 JSON 文件,铸造代币时需要给每个代币指定唯一的URI。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),在链上记录此信息日志。

示例#

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

查看#

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

合约发行#

请参考Solidity合约入门手册

调用方法#

以python为例:

安装python依赖#

使用下列命令,安装PlatON 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', 210425, 'lat'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"  },]prc721 = w3.eth.contract(address='contract address', abi=abi)# 查看合约所有的function 和 eventprint([func for func in prc721.functions])print([event for event in prc721.events])

查询合约信息#

以balanceOf、ownerOf示例,其他查询方法与此类似:

# 统计用户持有的NFT数量prc721.functions.balanceOf('your address').call()# 查询NFT的持有者prc721.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 = prc721.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 = prc721.events.Transfer().processReceipt(receipt)

上传Metadata到IPFS网络#

Metadata 是NFT代币的元数据,以便更丰富地显示NFT资产的详细信息, 存储在链下,一般发行一枚NFT代币会指定一个URI路径,指向这枚代币的Metadata数据。

1.安装IPFS#

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

2.添加文件到ipfs#

准备一张图片,将图片命名为PlatON.jpeg,上传到ipfs节点上

$ ipfs add  PlatON.jpegadded QmZtmD2qt6fJot32nabSP3CUjicnypEBz7bHVDhPQt9aAy PlatON.jpeg//QmZtmD2qt6fJot32nabSP3CUjicnypEBz7bHVDhPQt9aAy 是文件的唯一ID,添加到ipfs后生成

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

有了上面的图片链接,就可以用它来构造用于NFT的metadata了。

首先按照PRC-721文档中metadata的说明创建一个json文件,命名为PlatON.json:

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

上传到ipfs节点上:

$ ipfs add  PlatON.jsonadded QmQXqTVCb1w7CmdsYxHWR1T1qyaCHHgWwiPmoZDcQL39Px PlatON.json

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

以上,上传完毕。