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
);
}

以下为 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
);
  
}
}

附录

A. 修饰符

在 Solidity编程中,有一个概念叫做修饰符,英文是 modify ,当为一个方法添加了某个修饰符时,也就意味着这个方法必须满足这个修饰符所要求的事情,比如 external 关键词是要求该方法仅允许外部合约访问。

下面列出一些上面提到的修饰符:

external 仅允许外部合约调用该方法

payable 仅标记了该关键词的方法能够接收转账操作

B. 引用

ERC721草案

https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md

OpenZeppelin对于ERC721完整实现

https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/

本文由 我爱PHP169 作者:admin 发表,其版权均为 我爱PHP169 所有,文章内容系作者个人观点,不代表 我爱PHP169 对观点赞同或支持。如需转载,请注明文章来源。

发表回复