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 对观点赞同或支持。如需转载,请注明文章来源。