Skip to content

Commit

Permalink
Update EIP-6147: Move to Review (ethereum#6452)
Browse files Browse the repository at this point in the history
  • Loading branch information
5660-eth authored and iseriohn committed Feb 16, 2023
1 parent 53733d5 commit cb7b596
Showing 1 changed file with 56 additions and 59 deletions.
115 changes: 56 additions & 59 deletions EIPS/eip-6147.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Guard of NFT/SBT, an Extension of EIP-721
description: A new management role of NFT/SBT is defined, which realizes the separation of transfer right and holding right of NFT/SBT.
author: 5660-eth (@5660-eth), Wizard Wang
discussions-to: https://ethereum-magicians.org/t/guard-of-nft-sbt-an-extension-of-eip-721/12052
status: Draft
status: Review
type: Standards Track
category: ERC
created: 2022-12-07
Expand Down Expand Up @@ -49,25 +49,23 @@ When a token has a guard, if the token burns, the guard MUST be deleted.
If issuing or minting SBTs, the guard MAY be uniformly set to the designated address to facilitate management.

### Contract Interface


```solidity
interface IERC6147 {
/// Logged when the guard of an NFT is changed
/// @notice Emitted when the `guard` is changed
/// The zero address for guard indicates that there is no guard address
event UpdateGuardLog(uint256 indexed tokenId,address indexed newGuard,address oldGuard);
event UpdateGuardLog(uint256 indexed tokenId, address indexed newGuard, address oldGuard);
/// @notice Owner, authorised operators and approved address of the NFT can set guard of the NFT and guard can modifiy guard of the NFT
/// @notice Owner, authorised operators and approved address of the NFT can set guard of the NFT and guard can modifiy guard of the NFT
/// If the NFT has a guard role, the owner, authorised operators and approved address of the NFT cannot modify guard
/// @dev The newGuard can not be zero address
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to get the guard address for
/// @param newGuard The new guard address of the NFT
function changeGuard(uint256 tokenId, address newGuard) external;
/// @notice Remove the guard of the NFT
/// Only guard can remove its own guard role
/// @dev The guard address is set to 0 address
Expand All @@ -81,7 +79,7 @@ If issuing or minting SBTs, the guard MAY be uniformly set to the designated add
/// @param from The address of the previous owner of the NFT
/// @param to The address of NFT recipient
/// @param tokenId The NFT to get transferred for
function transferAndRemove(address from,address to,uint256 tokenId) external;
function transferAndRemove(address from, address to, uint256 tokenId) external;
/// @notice Get the guard address of the NFT
/// @dev The zero address indicates that there is no guard
Expand All @@ -90,7 +88,6 @@ If issuing or minting SBTs, the guard MAY be uniformly set to the designated add
/// @return The guard address for the NFT
function guardOf(uint256 tokenId) external view returns (address);
}
```

The `changeGuard(uint256 tokenId, address newGuard)` function MAY be implemented as `public` or `external`.
Expand Down Expand Up @@ -135,8 +132,7 @@ The alternative names are `guardian` and `guard`, both of which basically match

This standard can be fully EIP-721 compatible by adding an extension function set.

If the NFT issued based on the above standard does not set a `guard` , then it is no different from the current NFT issued based on the EIP-721 standard.

If an NFT issued based on the above standard does not set a `guard` , then it is no different in the existing functions from the current NFT issued based on the EIP-721 standard.

## Reference Implementation

Expand All @@ -151,54 +147,33 @@ abstract contract ERC6147 is ERC721, IERC6147 {
mapping(uint256 => address) internal token_guard_map;
/// @notice Update the guard of the NFT
/// @dev Delete function: set guard to 0 address,update function: set guard to new address
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to update the guard address for
/// @param newGuard The newGuard address
/// @param allowNull Allow 0 address
function updateGuard(uint256 tokenId,address newGuard,bool allowNull) internal {
address guard = guardOf(tokenId);
if (!allowNull) {
require(newGuard != address(0), "ERC6147: new guard can not be null");
}
if (guard != address(0)) {
require(guard == _msgSender(), "ERC6147: only guard can change it self");
} else {
require(_isApprovedOrOwner(_msgSender(), tokenId),""ERC6147: caller is not owner nor approved");
}
if (guard != address(0) || newGuard != address(0)) {
token_guard_map[tokenId] = newGuard;
emit UpdateGuardLog(tokenId, newGuard, guard);
}
}
/// @notice Owner sets guard or guard modifies guard
/// @notice Owner, authorised operators and approved address of the NFT can set guard of the NFT and guard can modifiy guard of the NFT
/// If the NFT has a guard role, the owner, authorised operators and approved address of the NFT cannot modify guard
/// @dev The newGuard can not be zero address
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to get the guard address for
/// @param newGuard The new guard address of the NFT
function changeGuard(uint256 tokenId, address newGuard) public virtual{
updateGuard(tokenId, newGuard, false);
_updateGuard(tokenId, newGuard, false);
}
/// @notice Remove the guard of the NFT
/// Only guard can remove its own guard role
/// @dev The guard address is set to 0 address
/// Only guard can remove its own guard role
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to remove the guard address for
function removeGuard(uint256 tokenId) public virtual {
updateGuard(tokenId, address(0), true);
_updateGuard(tokenId, address(0), true);
}
/// @notice Transfer the NFT and remove its guard role
/// Throws if `tokenId` is not valid NFT
/// @param from The address of the previous owner of the NFT
/// @param to The address of NFT recipient
/// @param tokenId The NFT to get transferred for
function transferAndRemove(address from,address to,uint256 tokenId) public virtual {
transferFrom(from,to,tokenId);
/// @dev The NFT is transferred to `to` and the guard address is set to 0 address
/// Throws if `tokenId` is not valid NFT
/// @param from The address of the previous owner of the NFT
/// @param to The address of NFT recipient
/// @param tokenId The NFT to get transferred for
function transferAndRemove(address from, address to, uint256 tokenId) public virtual {
safeTransferFrom(from, to, tokenId);
removeGuard(tokenId);
}
Expand All @@ -210,37 +185,52 @@ abstract contract ERC6147 is ERC721, IERC6147 {
function guardOf(uint256 tokenId) public view virtual returns (address) {
return token_guard_map[tokenId];
}
/// @notice Update the guard of the NFT
/// @dev Delete function: set guard to 0 address; and update function: set guard to new address
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to update the guard address for
/// @param newGuard The newGuard address
/// @param allowNull Allow 0 address
function _updateGuard(uint256 tokenId, address newGuard, bool allowNull) internal {
address guard = guardOf(tokenId);
if (!allowNull) {
require(newGuard != address(0), "ERC6147: new guard can not be null");
}
if (guard != address(0)) {
require(guard == _msgSender(), "ERC6147: only guard can change it self");
} else {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC6147: caller is not owner nor approved");
}
if (guard != address(0) || newGuard != address(0)) {
token_guard_map[tokenId] = newGuard;
emit UpdateGuardLog(tokenId, newGuard, guard);
}
}
/// @notice Check the guard address
/// @dev The zero address indicates there is no guard
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to check the guard address for
/// @return The guard address
function checkGuard(uint256 tokenId) internal view returns (address) {
function _checkGuard(uint256 tokenId) internal view returns (address) {
address guard = guardOf(tokenId);
address sender = _msgSender();
if (guard != address(0)) {
require(guard == sender, "sender is not guard of token");
require(guard == sender, "ERC6147: sender is not guard of the token");
return guard;
}else{
return address(0);
}
}
///@dev When burning, delete `token_guard_map[tokenId]`
function _burn(uint256 tokenId) internal virtual override {
address guard=guardOf(tokenId);
super._burn(tokenId);
delete token_guard_map[tokenId];
emit UpdateGuardLog(tokenId, address(0), guard);
}
/// @dev Before transferring the NFT, need to check the gurard address
function transferFrom(address from,address to,uint256 tokenId) public virtual override {
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
address guard;
address new_from = from;
if (from != address(0)) {
guard = checkGuard(tokenId);
guard = _checkGuard(tokenId);
new_from = ownerOf(tokenId);
}
if (guard == address(0)) {
Expand All @@ -253,11 +243,11 @@ abstract contract ERC6147 is ERC721, IERC6147 {
}
/// @dev Before safe transferring the NFT, need to check the gurard address
function safeTransferFrom(address from,address to,uint256 tokenId,bytes memory _data) public virtual override {
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
address guard;
address new_from = from;
if (from != address(0)) {
guard = checkGuard(tokenId);
guard = _checkGuard(tokenId);
new_from = ownerOf(tokenId);
}
if (guard == address(0)) {
Expand All @@ -269,20 +259,27 @@ abstract contract ERC6147 is ERC721, IERC6147 {
_safeTransfer(from, to, tokenId, _data);
}
/// @dev When burning, delete `token_guard_map[tokenId]`
/// This is an internal function that does not check if the sender is authorized to operate on the token.
function _burn(uint256 tokenId) internal virtual override {
address guard=guardOf(tokenId);
super._burn(tokenId);
delete token_guard_map[tokenId];
emit UpdateGuardLog(tokenId, address(0), guard);
}
/// @dev See {IERC165-supportsInterface}.
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC6147).interfaceId || super.supportsInterface(interfaceId);
}
}
```


## Security Considerations

When an NFT has a `guard`, even if an address is authorized as an operator through `approve` or `setApprovalForAll`, the operator still has no right to transfer the NFT.

For NFT trading platforms that trade through `setApprovalForAll` + holder's signature, when an NFT has a `guard`, it cannot be traded. It is recommended to prevent such pending orders by checking the interface beforehand.
When an NFT has a `guard`, the `owner` cannot sell the NFT. Some trading platforms list NFTs through `setApprovalForAll` and owners' signature. It is recommended to prevent listing these NFTs by checking `guardOf`.

## Copyright

Expand Down

0 comments on commit cb7b596

Please sign in to comment.