主页 > imtoken有usdt钱包地址吗 > 以太坊代币系列的非同质化代币ERC721
以太坊代币系列的非同质化代币ERC721
0x00写在前面
2013年7月,一个名为MasterCoin的项目通过数字货币论坛Bitcointalk筹集了5000多枚比特币,从而开启了区块链行业新一轮的众筹。 后来跃升至数字货币市值榜单第二位的以太坊网络,在主网上线后推出了发行代币的功能。 而这一特性更是将区块链行业的募资推向了一个高峰。
为了更快地发行代币,以太坊社区制定了代币标准规范ERC20,直接将代币发行时间缩短到10分钟以内。
最近火热的猫狗游戏,本质上就是基于以太坊网络的代币。 每只猫或狗都是以太坊上的一个代币,每个代币都是独一无二的,无法交换。 改变。 而这种代币极受社区看好,所以社区制定了非同质化代币的标准。 这个标准就是我们今天说的ERC721。
与ERC20相比,ERC721具有更大的想象空间。 由于其独特的属性,每一个代币都是独一无二的,不仅是虚拟资产,现实生活中的汽车、房子也是如此。 当资产可以被证明时,我们就可以实现资产的上链。
0x01 ERC721标准制定动机
为了让任何人都可以在以太坊上发行ERC721等非同质代币,并让钱包/拍卖应用/交易所等应用能够快速方便地接入此类非同质代币以太坊链可以转erc20吗,以太坊社区制定了ERC721代币标准。
ERC721代币称为非同质代币,应该是不可替代的代币,简称NFT。
0x02 ERC721标准规范
在ERC721规范中,为了让代币非同质化,每一个NFT都有一个唯一的ID,ID在智能合约中是不可变的,这个NFT将能够标记现实中的一种资产,包括房子,汽车,收藏品, ETC。
在ERC20代币中,每个代币的转账都是一样的,并且可以拆分代币,最多支持18位小数,而在ERC721中,每个代币不能拆分,一个代币就是一个资产,所有的转账和兑换都是交易所一个标记,因为当映射到现实时,我们的房子和汽车不能分成很多份。
我们在转ERC20代币的时候,只需要标注我们转的数量,不需要具体说明我转的是我的100个代币中的哪一个,因为它们是同质代币,转哪一个的价值是一样的的。 在ERC721中,我们每次转账token时,都需要指定转账token的ID,因为每个token代表着不同的数字资产,或者现实中映射的某种实物资产。
每个实现 ERC721 标准代币的合约都需要满足以下方法:
A.方法Method1,balanceOf()获取balance函数的原型
function balanceOf(address _owner) external view returns (uint256);
该方法获取_owner地址的余额,external修饰符表示该方法只能在合约外访问。
2. ownerOf() 获取token的owner函数原型
function ownerOf(uint256 _tokenId) external view returns (address);
该方法获取具有指定 _tokenId 的令牌的所有者,该令牌只能在合约之外访问。
因为ERC721代币有自己的唯一标识,我们可以通过唯一标识识别代币的拥有者,而ERC20做不到这一点,这也说明ERC721可以用来确权。
3. transferFrom()转账token函数原型
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
该方法将 _tokenId 令牌从 _from 地址传输到 _to 地址。
要以 ERC721 转账,您必须标记您要转账的代币的唯一 ID。
4. safeTransferFrom() 安全转账令牌函数原型
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
该方法将 _tokenId 令牌从 _from 地址传输到 _to 地址。
5. approve()授权token操作权函数原型
function approve(address _approved, uint256 _tokenId) external payable;
该方法授权批准_approved地址有权操作_tokenId token。
授权人必须是_tokenId token 的拥有者或者是被批准拥有token 操作权限的地址。
6. setApprovalForAll() 设置代币操作权函数原型
function setApprovalForAll(address _operator, bool _approved) external;
该方法授权并批准添加或删除_operator 成为所有代币的操作者。 合约必须支持代币存在多个运营商。
7、getApproved()获取审批人函数原型
function getApproved(uint256 _tokenId) external view returns (address);
获取 _tokenId 令牌的授权操作员的方法
8、isApprovedForAll()是否为授权函数原型
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
判断_operator是否被_owner授权为所有token的操作者的方法
B. EventEvent1、Transfer() 传输事件事件原型
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
事件 当令牌被转移时触发该事件。
2.Approval()授权事件事件原型
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
当使用单个令牌授权事件时,将触发此事件。
3、ApprovalForAll()授权所有token事件事件原型
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
调用事件 setApprovalForAll 方法时触发
0x03 ERC721标准接口
ERC721代币合约实现时,必须实现ERC721接口和ERC165接口。 以下是接口:
pragma solidity ^0.4.20;
interface ERC721 {
// 转移事件,记录转出,转入,以及被转移代币的ID
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
// 授权,记录授权者,被授权者,被授权代币ID
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
// 授权所有代币,记录授权者,被授权者
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
// 获取代币余额
// @params address _owner 代币所有者
// @return uint256 返回数量
function balanceOf(address _owner) external view returns (uint256);
// 获取nft代币所有者
// @params uint256 _tokenId 代币ID
// @return address 拥有者地址
function ownerOf(uint256 _tokenId) external view returns (address);
// 安全转移代币
// @params address _from 发送人地址
// @params address _to 收款人地址
// @params uint256 _tokenId 代币ID
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
// 转移代币
// @params address _from 发送人地址
// @params address _to 收款人地址
// @params uint256 _tokenId 代币ID
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
// 授权
// @params address _approved 被授权人
// @params uint256 _tokenId 代币ID
function approve(address _approved, uint256 _tokenId) external payable;
// 授权加入或者移除所有运营权
// @params address _approved 被授权人
// @params bool _approved 加入或删除
function setApprovalForAll(address _operator, bool _approved) external;
// 获取被授权人
// @params uint256 _tokenId 代币ID
// @return address 被授权地址
function getApproved(uint256 _tokenId) external view returns (address);
// 是否代币运营者
// @params address _owner 所有者
// @params address _operator 被授权者
// @return bool 是/否
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
interface ERC165 {
// 查看是否支持该接口
// @params bytes4 interfaceID 接口ID
// 计算方式如:bytes4(keccak256('supportsInterface(bytes4)'))
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
0x04 代码示例
以下为OpenZeppelin提供的代码示例,仅提供主要方法代码,完整版本代码可在附录中获取:
pragma solidity ^0.4.18;
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721BasicToken {
// 等于 `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba;
// 定义mapping 记录token的所有者
mapping (uint256 => address) internal tokenOwner;
// 记录指定token被授权人的地址
mapping (uint256 => address) internal tokenApprovals;
// 记录指定地址拥有多少个token
mapping (address => uint256) internal ownedTokensCount;
// 记录代币运营者
mapping (address => mapping (address => bool)) internal operatorApprovals;
// 修饰符,是否是 `_tokenId` 的所有者
modifier onlyOwnerOf(uint256 _tokenId) {
require(ownerOf(_tokenId) == msg.sender);
_;
}
// 修饰符 是否可以对该代币进行转账
modifier canTransfer(uint256 _tokenId) {
require(isApprovedOrOwner(msg.sender, _tokenId));
_;
}
// 获取代币余额
function balanceOf(address _owner) public view returns (uint256) {
require(_owner != address(0));
return ownedTokensCount[_owner];
}
// 代币持有人获取
function ownerOf(uint256 _tokenId) public view returns (address) {
address owner = tokenOwner[_tokenId];
require(owner != address(0));
return owner;
}
// 某个nft代币是否真实存在
function exists(uint256 _tokenId) public view returns (bool) {
address owner = tokenOwner[_tokenId];
return owner != address(0);
}
// 授权代币运营权
function approve(address _to, uint256 _tokenId) public {
address owner = ownerOf(_tokenId);
require(_to != owner);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
if (getApproved(_tokenId) != address(0) || _to != address(0)) {
tokenApprovals[_tokenId] = _to;
Approval(owner, _to, _tokenId);
}
}
// 获取运营权归属者
function getApproved(uint256 _tokenId) public view returns (address) {
return tokenApprovals[_tokenId];
}
// 从运营列表中移除或者添加进列表
function setApprovalForAll(address _to, bool _approved) public {
require(_to != msg.sender);
operatorApprovals[msg.sender][_to] = _approved;
ApprovalForAll(msg.sender, _to, _approved);
}
// 判断是否是运营者
function isApprovedForAll(address _owner, address _operator) public view returns (bool) {
return operatorApprovals[_owner][_operator];
}
// 代币转移
function transferFrom(address _from, address _to, uint256 _tokenId) public canTransfer(_tokenId) {
require(_from != address(0));
require(_to != address(0));
clearApproval(_from, _tokenId);
removeTokenFrom(_from, _tokenId);
addTokenTo(_to, _tokenId);
Transfer(_from, _to, _tokenId);
}
// 安全转移
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
)
public
canTransfer(_tokenId)
{
safeTransferFrom(_from, _to, _tokenId, "");
}
// 是否拥有代币的运营权
function isApprovedOrOwner(address _spender, uint256 _tokenId) internal view returns (bool) {
address owner = ownerOf(_tokenId);
return _spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender);
}
// 清除运营权
function clearApproval(address _owner, uint256 _tokenId) internal {
require(ownerOf(_tokenId) == _owner);
if (tokenApprovals[_tokenId] != address(0)) {
tokenApprovals[_tokenId] = address(0);
Approval(_owner, address(0), _tokenId);
}
}
// 添加一个代币
function addTokenTo(address _to, uint256 _tokenId) internal {
require(tokenOwner[_tokenId] == address(0));
tokenOwner[_tokenId] = _to;
ownedTokensCount[_to] = ownedTokensCount[_to].add(1);
}
// 移除一个代币
function removeTokenFrom(address _from, uint256 _tokenId) internal {
require(ownerOf(_tokenId) == _from);
ownedTokensCount[_from] = ownedTokensCount[_from].sub(1);
tokenOwner[_tokenId] = address(0);
}
// 是否是安全转移
// 能够接收ERC721代币的合约必须实现`onERC721Received`方法
// 通过判断是否存在该方法查看是否安全转移
function checkAndCallSafeTransfer(
address _from,
address _to,
uint256 _tokenId,
bytes _data
)
internal
returns (bool)
{
if (!_to.isContract()) {
return true;
}
bytes4 retval = ERC721Receiver(_to).onERC721Received(_from, _tokenId, _data);
return (retval == ERC721_RECEIVED);
}
}
0x05 附录 A. 修饰符
在Solidity编程中,有一个概念叫做modifier以太坊链可以转erc20吗,英文是modify。 当一个修饰符被添加到一个方法中时,意味着该方法必须满足修饰符的要求。 例如,external 关键字是要求此方法只能由外部契约访问。
下面列出了上面提到的一些修饰符:
external 只允许外部合约调用这个方法
payable 只有标有该关键字的方法才能接收转账操作
b. 引用
ERC721草案
OpenZeppelin 完整实现 ERC721
喜欢就别说话,扫一扫~