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
以上,上传完毕。