-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9eaa0e5
commit c210efd
Showing
11 changed files
with
529 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
# ERC Standards | ||
|
||
!!! Info | ||
参考: | ||
|
||
- [openzeppelin 的 ERC 实现]() | ||
- [xg 有关 ERC 的笔记](https://note.tonycrane.cc/ctf/blockchain/eth/erc/) | ||
|
||
## ERC-20 | ||
|
||
## ERC-721 | ||
|
||
### Events | ||
|
||
- `event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);` | ||
- Emitted when `tokenId` token is transferred from `from` to `to`. | ||
- `event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);` | ||
- Emitted when `owner` enables `approved` to manage the `tokenId` token. | ||
- `event ApprovalForAll(address indexed owner, address indexed operator, bool approved);` | ||
- Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. | ||
|
||
### Approve | ||
|
||
- `function approve(address to, uint256 tokenId) public virtual;` | ||
- 作用:授权 `tokenId` 给 `to`。 | ||
- 细节:见 `_approve`,只有在需要发出事件(`emitEvent` 为真)或者授权者不为 0 时才读取所有者,这时候会检查代币拥有者是否是授权者、`auth` 是否被代币的拥有者授权,这两个条件均不满足就会抛出异常。我们经常使用的 `approve` 函数就默认了 `emitEvent` 为真,这时候会仔细检查授权情况。在最后将 `_tokenApprovals[tokenId]` 设置为 `to`。 | ||
- 实现: | ||
```solidity | ||
function approve(address to, uint256 tokenId) public virtual { | ||
_approve(to, tokenId, _msgSender()); | ||
} | ||
function _approve(address to, uint256 tokenId, address auth) internal { | ||
_approve(to, tokenId, auth, true); | ||
} | ||
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual { | ||
// Avoid reading the owner unless necessary | ||
if (emitEvent || auth != address(0)) { | ||
address owner = _requireOwned(tokenId); | ||
// We do not use _isAuthorized because single-token approvals should not be able to call approve | ||
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) { | ||
revert ERC721InvalidApprover(auth); | ||
} | ||
if (emitEvent) { | ||
emit Approval(owner, to, tokenId); | ||
} | ||
} | ||
_tokenApprovals[tokenId] = to; | ||
} | ||
``` | ||
- `function setApprovalForAll(address operator, bool approved) public virtual;` | ||
- 实现: | ||
```solidity | ||
function setApprovalForAll(address operator, bool approved) public virtual { | ||
_setApprovalForAll(_msgSender(), operator, approved); | ||
} | ||
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { | ||
if (operator == address(0)) { | ||
revert ERC721InvalidOperator(operator); | ||
} | ||
_operatorApprovals[owner][operator] = approved; | ||
emit ApprovalForAll(owner, operator, approved); | ||
} | ||
``` | ||
|
||
### Inquire | ||
|
||
- `function balanceOf(address owner) public view virtual returns (uint256);` | ||
|
||
- `function ownerOf(uint256 tokenId) public view virtual returns (address);` | ||
|
||
- `function getApproved(uint256 tokenId) public view virtual returns (address);` | ||
- 作用:查询 tokenId 的授权者,也就是返回 `_tokenApprovals[tokenId]`,同时要求该代币存在(拥有者不为 0)。 | ||
- 实现: | ||
```solidity | ||
function getApproved(uint256 tokenId) public view virtual returns (address) { | ||
_requireOwned(tokenId); | ||
return _getApproved(tokenId); | ||
} | ||
function _getApproved(uint256 tokenId) internal view virtual returns (address) { | ||
return _tokenApprovals[tokenId]; | ||
} | ||
``` | ||
- `function isApprovedForAll(address owner, address operator) public view virtual returns (bool);` | ||
- 实现: | ||
```solidity | ||
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { | ||
return _operatorApprovals[owner][operator]; | ||
} | ||
``` | ||
|
||
|
||
### Check | ||
|
||
- `function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool);` | ||
- 作用:检查 `spender` 是否有权限操作 `tokenId`,但是不抛出异常。 | ||
- 细节:要求 `spender` 不为 0,同时需要下面三个要求满足其一: | ||
- `owner` 与 `spender` 相同,拥有者自然有权操纵代币; | ||
- `owner` 授权给 `spender`,全局授权,`spender` 可以操纵 `owner` 的所有代币; | ||
- `tokenId` 的授权者是 `spender`。 | ||
- 实现: | ||
```solidity | ||
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) { | ||
return | ||
spender != address(0) && | ||
(owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender); | ||
} | ||
``` | ||
- `function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual;` | ||
- 作用:检查 `spender` 是否有权限操作 `tokenId`,如果没有就抛出异常。 | ||
- 细节:首先调用 `_isAuthorized` 检查权限,如果没有权限就抛出异常,异常分为两种情况: | ||
- `owner` 为 0,代币不存在,抛出 `ERC721NonexistentToken` 异常; | ||
- `owner` 为 0,代币存在,但是 `spender` 没有权限, 抛出 `ERC721InsufficientApproval` 异常。 | ||
- 实现: | ||
```solidity | ||
function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual { | ||
if (!_isAuthorized(owner, spender, tokenId)) { | ||
if (owner == address(0)) { | ||
revert ERC721NonexistentToken(tokenId); | ||
} else { | ||
revert ERC721InsufficientApproval(spender, tokenId); | ||
} | ||
} | ||
} | ||
``` | ||
- `function _update(address to, uint256 tokenId, address auth) internal virtual returns (address);` | ||
- 实现: | ||
```solidity | ||
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) { | ||
address from = _ownerOf(tokenId); | ||
// Perform (optional) operator check | ||
if (auth != address(0)) { | ||
_checkAuthorized(from, auth, tokenId); | ||
} | ||
// Execute the update | ||
if (from != address(0)) { | ||
// Clear approval. No need to re-authorize or emit the Approval event | ||
_approve(address(0), tokenId, address(0), false); | ||
unchecked { | ||
_balances[from] -= 1; | ||
} | ||
} | ||
if (to != address(0)) { | ||
unchecked { | ||
_balances[to] += 1; | ||
} | ||
} | ||
_owners[tokenId] = to; | ||
emit Transfer(from, to, tokenId); | ||
return from; | ||
} | ||
``` | ||
- `function _requireOwned(uint256 tokenId) internal view virtual returns (address);` | ||
- 作用:检查 tokenId 是否存在,如果不存在(也就是拥有者为地址 0)就抛出异常。 | ||
- 实现: | ||
```solidity | ||
function _requireOwned(uint256 tokenId) internal view returns (address) { | ||
address owner = _ownerOf(tokenId); | ||
if (owner == address(0)) { | ||
revert ERC721NonexistentToken(tokenId); | ||
} | ||
return owner; | ||
} | ||
``` | ||
|
||
### Transfer | ||
|
||
- `function transferFrom(address from, address to, uint256 tokenId) public virtual;` | ||
- 实现: | ||
```solidity | ||
function transferFrom(address from, address to, uint256 tokenId) public virtual { | ||
if (to == address(0)) { | ||
revert ERC721InvalidReceiver(address(0)); | ||
} | ||
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists | ||
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. | ||
address previousOwner = _update(to, tokenId, _msgSender()); | ||
if (previousOwner != from) { | ||
revert ERC721IncorrectOwner(from, tokenId, previousOwner); | ||
} | ||
} | ||
``` | ||
- `function safeTransferFrom(address from, address to, uint256 tokenId) public;` | ||
- 实现: | ||
```solidity | ||
function safeTransferFrom(address from, address to, uint256 tokenId) public { | ||
safeTransferFrom(from, to, tokenId, ""); | ||
} | ||
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual { | ||
transferFrom(from, to, tokenId); | ||
_checkOnERC721Received(from, to, tokenId, data); | ||
} | ||
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private { | ||
if (to.code.length > 0) { | ||
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { | ||
if (retval != IERC721Receiver.onERC721Received.selector) { | ||
revert ERC721InvalidReceiver(to); | ||
} | ||
} catch (bytes memory reason) { | ||
if (reason.length == 0) { | ||
revert ERC721InvalidReceiver(to); | ||
} else { | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
revert(add(32, reason), mload(reason)) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` |
Empty file.
Oops, something went wrong.