interfaceIERC6147{/// Logged when the guard of an NFT is changed or expires is changed
/// @notice Emitted when the `guard` is changed or the `expires` is changed
/// The zero address for `newGuard` indicates that there currently is no guard address
eventUpdateGuardLog(uint256indexedtokenId,addressindexednewGuard,addressoldGuard,uint64expires);/// @notice Owner, authorised operators and approved address of the NFT can set guard and expires of the NFT and
/// valid guard can modifiy guard and expires of the NFT
/// If the NFT has a valid guard role, the owner, authorised operators and approved address of the NFT
/// cannot modify guard and expires
/// @dev The `newGuard` can not be zero address
/// The `expires` need to be valid
/// 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
/// @param expires UNIX timestamp, the guard could manage the token before expires
functionchangeGuard(uint256tokenId,addressnewGuard,uint64expires)external;/// @notice Remove the guard and expires of the NFT
/// Only guard can remove its own guard role and expires
/// @dev The guard address is set to 0 address
/// The expires is set to 0
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to remove the guard and expires for
functionremoveGuard(uint256tokenId)external;/// @notice Transfer the NFT and remove its guard and expires
/// @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
functiontransferAndRemove(addressfrom,addressto,uint256tokenId)external;/// @notice Get the guard address and expires of the NFT
/// @dev The zero address indicates that there is no guard
/// @param tokenId The NFT to get the guard address and expires for
/// @return The guard address and expires for the NFT
functionguardInfo(uint256tokenId)externalviewreturns(address,uint64);}
changeGuard(uint256 tokenId, address newGuard, uint64 expires) 函数可以实现为 public 或 external。
removeGuard(uint256 tokenId) 函数可以实现为 public 或 external。
transferAndRemove(address from,address to,uint256 tokenId) 函数可以实现为 public 或 external。
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.8;import"@openzeppelin/contracts/token/ERC721/ERC721.sol";import"./IERC6147.sol";abstractcontractERC6147isERC721,IERC6147{/// @dev A structure representing a token of guard address and expires
/// @param guard address of guard role
/// @param expirs UNIX timestamp, the guard could manage the token before expires
structGuardInfo{addressguard;uint64expires;}mapping(uint256=>GuardInfo)internal_guardInfo;/// @notice Owner, authorised operators and approved address of the NFT can set guard and expires of the NFT and
/// valid guard can modifiy guard and expires of the NFT
/// If the NFT has a valid guard role, the owner, authorised operators and approved address of the NFT
/// cannot modify guard and expires
/// @dev The `newGuard` can not be zero address
/// The `expires` need to be valid
/// 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
/// @param expires UNIX timestamp, the guard could manage the token before expires
functionchangeGuard(uint256tokenId,addressnewGuard,uint64expires)publicvirtual{require(expires>block.timestamp,"ERC6147: invalid expires");_updateGuard(tokenId,newGuard,expires,false);}/// @notice Remove the guard and expires of the NFT
/// Only guard can remove its own guard role and expires
/// @dev The guard address is set to 0 address
/// The expires is set to 0
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to remove the guard and expires for
functionremoveGuard(uint256tokenId)publicvirtual{_updateGuard(tokenId,address(0),0,true);}/// @notice Transfer the NFT and remove its guard and expires
/// @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
functiontransferAndRemove(addressfrom,addressto,uint256tokenId)publicvirtual{safeTransferFrom(from,to,tokenId);removeGuard(tokenId);}/// @notice Get the guard address and expires of the NFT
/// @dev The zero address indicates that there is no guard
/// @param tokenId The NFT to get the guard address and expires for
/// @return The guard address and expires for the NFT
functionguardInfo(uint256tokenId)publicviewvirtualreturns(address,uint64){if(_guardInfo[tokenId].expires>=block.timestamp){return(_guardInfo[tokenId].guard,_guardInfo[tokenId].expires);}else{return(address(0),0);}}/// @notice Update the guard of the NFT
/// @dev Delete function: set guard to 0 address and set expires to 0;
/// and update function: set guard to new address and set expires
/// Throws if `tokenId` is not valid NFT
/// @param tokenId The NFT to update the guard address for
/// @param newGuard The newGuard address
/// @param expires UNIX timestamp, the guard could manage the token before expires
/// @param allowNull Allow 0 address
function_updateGuard(uint256tokenId,addressnewGuard,uint64expires,boolallowNull)internal{(addressguard,)=guardInfo(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)){_guardInfo[tokenId]=GuardInfo(newGuard,expires);emitUpdateGuardLog(tokenId,newGuard,guard,expires);}}/// @notice Check the guard address
/// @dev The zero address indicates there is no guard
/// @param tokenId The NFT to check the guard address for
/// @return The guard address
function_checkGuard(uint256tokenId)internalviewreturns(address){(addressguard,)=guardInfo(tokenId);addresssender=_msgSender();if(guard!=address(0)){require(guard==sender,"ERC6147: sender is not guard of the token");returnguard;}else{returnaddress(0);}}/// @dev Before transferring the NFT, need to check the gurard address
functiontransferFrom(addressfrom,addressto,uint256tokenId)publicvirtualoverride{addressguard;addressnew_from=from;if(from!=address(0)){guard=_checkGuard(tokenId);new_from=ownerOf(tokenId);}if(guard==address(0)){require(_isApprovedOrOwner(_msgSender(),tokenId),"ERC721: transfer caller is not owner nor approved");}_transfer(new_from,to,tokenId);}/// @dev Before safe transferring the NFT, need to check the gurard address
functionsafeTransferFrom(addressfrom,addressto,uint256tokenId,bytesmemory_data)publicvirtualoverride{addressguard;addressnew_from=from;if(from!=address(0)){guard=_checkGuard(tokenId);new_from=ownerOf(tokenId);}if(guard==address(0)){require(_isApprovedOrOwner(_msgSender(),tokenId),"ERC721: transfer caller is not owner nor approved");}_safeTransfer(from,to,tokenId,_data);}/// @dev When burning, delete `token_guard_map[tokenId]`
/// 销毁时,删除`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(uint256tokenId)internalvirtualoverride{(addressguard,)=guardInfo(tokenId);super._burn(tokenId);delete_guardInfo[tokenId];emitUpdateGuardLog(tokenId,address(0),guard,0);}/// @dev See {IERC165-supportsInterface}.
functionsupportsInterface(bytes4interfaceId)publicviewvirtualoverridereturns(bool){returninterfaceId==type(IERC6147).interfaceId||super.supportsInterface(interfaceId);}}